From 92697139b01339b6c0767fa1305a4df9a7c1f37f Mon Sep 17 00:00:00 2001 From: Guanjun Date: Mon, 27 Nov 2023 16:31:27 +0800 Subject: lib/find_bit: Fix the code comments about find_next_bit_wrap The function find_next_bit_wrap only has one memory region to search on. Adjust the comments. Signed-off-by: Guanjun Signed-off-by: Yury Norov --- include/linux/find.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/find.h b/include/linux/find.h index 5e4f39ef2e72..af63ae5b9013 100644 --- a/include/linux/find.h +++ b/include/linux/find.h @@ -413,8 +413,8 @@ unsigned long find_next_and_bit_wrap(const unsigned long *addr1, } /** - * find_next_bit_wrap - find the next set bit in both memory regions - * @addr: The first address to base the search on + * find_next_bit_wrap - find the next set bit in a memory region + * @addr: The address to base the search on * @size: The bitmap size in bits * @offset: The bitnumber to start searching at * -- cgit v1.2.3 From 27c82f14e6d2bcb9f085bad37fe339227571de60 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Sat, 28 Oct 2023 12:05:29 -0700 Subject: lib/find: optimize find_*_bit_wrap When an offset is 0, there's no need to search a bitmap from the beginning after the 1st search failed, because each bit has already been tested. Signed-off-by: Yury Norov --- include/linux/find.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/find.h b/include/linux/find.h index af63ae5b9013..c69598e383c1 100644 --- a/include/linux/find.h +++ b/include/linux/find.h @@ -405,7 +405,7 @@ unsigned long find_next_and_bit_wrap(const unsigned long *addr1, { unsigned long bit = find_next_and_bit(addr1, addr2, size, offset); - if (bit < size) + if (bit < size || offset == 0) return bit; bit = find_first_and_bit(addr1, addr2, offset); @@ -427,7 +427,7 @@ unsigned long find_next_bit_wrap(const unsigned long *addr, { unsigned long bit = find_next_bit(addr, size, offset); - if (bit < size) + if (bit < size || offset == 0) return bit; bit = find_first_bit(addr, offset); -- cgit v1.2.3 From 71a5849aedaa9ea028fc51ee74576cad61954743 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 29 Sep 2023 21:11:57 +0000 Subject: mm: Change mmap_rnd_bits_max to __ro_after_init Allow mmap_rnd_bits_max to be updated on architectures that determine virtual address space size at runtime instead of relying on Kconfig options by changing it from const to __ro_after_init. Signed-off-by: Sami Tolvanen Reviewed-by: Kees Cook Reviewed-by: Palmer Dabbelt Acked-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230929211155.3910949-5-samitolvanen@google.com Signed-off-by: Palmer Dabbelt --- include/linux/mm.h | 2 +- mm/mmap.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mm.h b/include/linux/mm.h index f5a97dec5169..2488c0c5a288 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -86,7 +86,7 @@ extern int sysctl_legacy_va_layout; #ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS extern const int mmap_rnd_bits_min; -extern const int mmap_rnd_bits_max; +extern int mmap_rnd_bits_max __ro_after_init; extern int mmap_rnd_bits __read_mostly; #endif #ifdef CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS diff --git a/mm/mmap.c b/mm/mmap.c index b78e83d351d2..8f47011de22e 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -64,7 +64,7 @@ #ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS const int mmap_rnd_bits_min = CONFIG_ARCH_MMAP_RND_BITS_MIN; -const int mmap_rnd_bits_max = CONFIG_ARCH_MMAP_RND_BITS_MAX; +int mmap_rnd_bits_max __ro_after_init = CONFIG_ARCH_MMAP_RND_BITS_MAX; int mmap_rnd_bits __read_mostly = CONFIG_ARCH_MMAP_RND_BITS; #endif #ifdef CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS -- cgit v1.2.3 From cb4ede926134a65bc3bf90ed58dace8451d7e759 Mon Sep 17 00:00:00 2001 From: Xiao Wang Date: Sun, 12 Nov 2023 17:44:21 +0800 Subject: riscv: Avoid code duplication with generic bitops implementation There's code duplication between the fallback implementation for bitops __ffs/__fls/ffs/fls API and the generic C implementation in include/asm-generic/bitops/. To avoid this duplication, this patch renames the generic C implementation by adding a "generic_" prefix to them, then we can use these generic APIs as fallback. Suggested-by: Geert Uytterhoeven Signed-off-by: Xiao Wang Reviewed-by: Charlie Jenkins Link: https://lore.kernel.org/r/20231112094421.4014931-1-xiao.w.wang@intel.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/bitops.h | 138 +++++++------------------------------ include/asm-generic/bitops/__ffs.h | 8 ++- include/asm-generic/bitops/__fls.h | 8 ++- include/asm-generic/bitops/ffs.h | 8 ++- include/asm-generic/bitops/fls.h | 8 ++- 5 files changed, 48 insertions(+), 122 deletions(-) (limited to 'include') diff --git a/arch/riscv/include/asm/bitops.h b/arch/riscv/include/asm/bitops.h index 9ffc35537024..c4c2173dfe99 100644 --- a/arch/riscv/include/asm/bitops.h +++ b/arch/riscv/include/asm/bitops.h @@ -22,6 +22,16 @@ #include #else +#define __HAVE_ARCH___FFS +#define __HAVE_ARCH___FLS +#define __HAVE_ARCH_FFS +#define __HAVE_ARCH_FLS + +#include +#include +#include +#include + #include #include @@ -37,8 +47,6 @@ static __always_inline unsigned long variable__ffs(unsigned long word) { - int num; - asm_volatile_goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1) : : : : legacy); @@ -52,32 +60,7 @@ static __always_inline unsigned long variable__ffs(unsigned long word) return word; legacy: - num = 0; -#if BITS_PER_LONG == 64 - if ((word & 0xffffffff) == 0) { - num += 32; - word >>= 32; - } -#endif - if ((word & 0xffff) == 0) { - num += 16; - word >>= 16; - } - if ((word & 0xff) == 0) { - num += 8; - word >>= 8; - } - if ((word & 0xf) == 0) { - num += 4; - word >>= 4; - } - if ((word & 0x3) == 0) { - num += 2; - word >>= 2; - } - if ((word & 0x1) == 0) - num += 1; - return num; + return generic___ffs(word); } /** @@ -93,8 +76,6 @@ legacy: static __always_inline unsigned long variable__fls(unsigned long word) { - int num; - asm_volatile_goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1) : : : : legacy); @@ -108,32 +89,7 @@ static __always_inline unsigned long variable__fls(unsigned long word) return BITS_PER_LONG - 1 - word; legacy: - num = BITS_PER_LONG - 1; -#if BITS_PER_LONG == 64 - if (!(word & (~0ul << 32))) { - num -= 32; - word <<= 32; - } -#endif - if (!(word & (~0ul << (BITS_PER_LONG - 16)))) { - num -= 16; - word <<= 16; - } - if (!(word & (~0ul << (BITS_PER_LONG - 8)))) { - num -= 8; - word <<= 8; - } - if (!(word & (~0ul << (BITS_PER_LONG - 4)))) { - num -= 4; - word <<= 4; - } - if (!(word & (~0ul << (BITS_PER_LONG - 2)))) { - num -= 2; - word <<= 2; - } - if (!(word & (~0ul << (BITS_PER_LONG - 1)))) - num -= 1; - return num; + return generic___fls(word); } /** @@ -149,46 +105,23 @@ legacy: static __always_inline int variable_ffs(int x) { - int r; - - if (!x) - return 0; - asm_volatile_goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1) : : : : legacy); + if (!x) + return 0; + asm volatile (".option push\n" ".option arch,+zbb\n" CTZW "%0, %1\n" ".option pop\n" - : "=r" (r) : "r" (x) :); + : "=r" (x) : "r" (x) :); - return r + 1; + return x + 1; legacy: - r = 1; - if (!(x & 0xffff)) { - x >>= 16; - r += 16; - } - if (!(x & 0xff)) { - x >>= 8; - r += 8; - } - if (!(x & 0xf)) { - x >>= 4; - r += 4; - } - if (!(x & 3)) { - x >>= 2; - r += 2; - } - if (!(x & 1)) { - x >>= 1; - r += 1; - } - return r; + return generic_ffs(x); } /** @@ -204,46 +137,23 @@ legacy: static __always_inline int variable_fls(unsigned int x) { - int r; - - if (!x) - return 0; - asm_volatile_goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1) : : : : legacy); + if (!x) + return 0; + asm volatile (".option push\n" ".option arch,+zbb\n" CLZW "%0, %1\n" ".option pop\n" - : "=r" (r) : "r" (x) :); + : "=r" (x) : "r" (x) :); - return 32 - r; + return 32 - x; legacy: - r = 32; - if (!(x & 0xffff0000u)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xff000000u)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xf0000000u)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xc0000000u)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x80000000u)) { - x <<= 1; - r -= 1; - } - return r; + return generic_fls(x); } /** diff --git a/include/asm-generic/bitops/__ffs.h b/include/asm-generic/bitops/__ffs.h index 39e56e1c7203..446fea6dda78 100644 --- a/include/asm-generic/bitops/__ffs.h +++ b/include/asm-generic/bitops/__ffs.h @@ -5,12 +5,12 @@ #include /** - * __ffs - find first bit in word. + * generic___ffs - find first bit in word. * @word: The word to search * * Undefined if no bit exists, so code should check against 0 first. */ -static __always_inline unsigned long __ffs(unsigned long word) +static __always_inline unsigned long generic___ffs(unsigned long word) { int num = 0; @@ -41,4 +41,8 @@ static __always_inline unsigned long __ffs(unsigned long word) return num; } +#ifndef __HAVE_ARCH___FFS +#define __ffs(word) generic___ffs(word) +#endif + #endif /* _ASM_GENERIC_BITOPS___FFS_H_ */ diff --git a/include/asm-generic/bitops/__fls.h b/include/asm-generic/bitops/__fls.h index 03f721a8a2b1..54ccccf96e21 100644 --- a/include/asm-generic/bitops/__fls.h +++ b/include/asm-generic/bitops/__fls.h @@ -5,12 +5,12 @@ #include /** - * __fls - find last (most-significant) set bit in a long word + * generic___fls - find last (most-significant) set bit in a long word * @word: the word to search * * Undefined if no set bit exists, so code should check against 0 first. */ -static __always_inline unsigned long __fls(unsigned long word) +static __always_inline unsigned long generic___fls(unsigned long word) { int num = BITS_PER_LONG - 1; @@ -41,4 +41,8 @@ static __always_inline unsigned long __fls(unsigned long word) return num; } +#ifndef __HAVE_ARCH___FLS +#define __fls(word) generic___fls(word) +#endif + #endif /* _ASM_GENERIC_BITOPS___FLS_H_ */ diff --git a/include/asm-generic/bitops/ffs.h b/include/asm-generic/bitops/ffs.h index 323fd5d6ae26..4c43f242daeb 100644 --- a/include/asm-generic/bitops/ffs.h +++ b/include/asm-generic/bitops/ffs.h @@ -3,14 +3,14 @@ #define _ASM_GENERIC_BITOPS_FFS_H_ /** - * ffs - find first bit set + * generic_ffs - find first bit set * @x: the word to search * * This is defined the same way as * the libc and compiler builtin ffs routines, therefore * differs in spirit from ffz (man ffs). */ -static inline int ffs(int x) +static inline int generic_ffs(int x) { int r = 1; @@ -39,4 +39,8 @@ static inline int ffs(int x) return r; } +#ifndef __HAVE_ARCH_FFS +#define ffs(x) generic_ffs(x) +#endif + #endif /* _ASM_GENERIC_BITOPS_FFS_H_ */ diff --git a/include/asm-generic/bitops/fls.h b/include/asm-generic/bitops/fls.h index b168bb10e1be..26f3ce1dd6e4 100644 --- a/include/asm-generic/bitops/fls.h +++ b/include/asm-generic/bitops/fls.h @@ -3,14 +3,14 @@ #define _ASM_GENERIC_BITOPS_FLS_H_ /** - * fls - find last (most-significant) bit set + * generic_fls - find last (most-significant) bit set * @x: the word to search * * This is defined the same way as ffs. * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static __always_inline int fls(unsigned int x) +static __always_inline int generic_fls(unsigned int x) { int r = 32; @@ -39,4 +39,8 @@ static __always_inline int fls(unsigned int x) return r; } +#ifndef __HAVE_ARCH_FLS +#define fls(x) generic_fls(x) +#endif + #endif /* _ASM_GENERIC_BITOPS_FLS_H_ */ -- cgit v1.2.3 From b558469580f82b925d6651ac8d7a01070402dbf8 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 18 Jan 2024 19:36:33 +0100 Subject: USB: uapi: OTG 3.0 Add attribute bits defined in version 3.0 Signed-off-by: Oliver Neukum Link: https://lore.kernel.org/r/20240118183644.16125-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/usb/ch9.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 8a147abfc680..44d73ba8788d 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -763,6 +763,8 @@ struct usb_otg20_descriptor { #define USB_OTG_SRP (1 << 0) #define USB_OTG_HNP (1 << 1) /* swap host/device roles */ #define USB_OTG_ADP (1 << 2) /* support ADP */ +/* OTG 3.0 */ +#define USB_OTG_RSP (1 << 3) /* support RSP */ #define OTG_STS_SELECTOR 0xF000 /* OTG status selector */ /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From 4ca79255101b9edd6ac874d39361fd2b52927af0 Mon Sep 17 00:00:00 2001 From: Alexander Tsoy Date: Fri, 26 Jan 2024 00:16:35 +0300 Subject: usb: audio-v2: Correct comments for struct uac_clock_selector_descriptor This is likely a copy-paste error. Replace "Source" with "Selector" where appropriate. Fixes: 7e847894039d7 ("linux/usb/audio.h: split header") Signed-off-by: Alexander Tsoy Link: https://lore.kernel.org/r/20240125211635.30140-1-alexander@tsoy.me Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/audio-v2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index ca796dc1a984..6e5555610010 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -82,7 +82,7 @@ struct uac_clock_source_descriptor { #define UAC_CLOCK_SOURCE_TYPE_INT_PROG 0x3 #define UAC_CLOCK_SOURCE_SYNCED_TO_SOF (1 << 2) -/* 4.7.2.2 Clock Source Descriptor */ +/* 4.7.2.2 Clock Selector Descriptor */ struct uac_clock_selector_descriptor { __u8 bLength; @@ -91,7 +91,7 @@ struct uac_clock_selector_descriptor { __u8 bClockID; __u8 bNrInPins; __u8 baCSourceID[]; - /* bmControls and iClockSource omitted */ + /* bmControls and iClockSelector omitted */ } __attribute__((packed)); /* 4.7.2.3 Clock Multiplier Descriptor */ -- cgit v1.2.3 From 5de5f1e292e56fe4b8d28923d325f4c16f3766cf Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Wed, 13 Dec 2023 11:10:06 +0800 Subject: phy: core: add notify_connect and notify_disconnect callback In Realtek SoC, the parameter of usb phy is designed to be able to do dynamic tuning based in the port status. Therefore, add a notify callback of phy driver when usb connection/disconnection change. Signed-off-by: Stanley Chang Link: https://lore.kernel.org/r/20231213031203.4911-1-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/phy/phy-core.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/phy/phy.h | 21 +++++++++++++++++++++ 2 files changed, 68 insertions(+) (limited to 'include') diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index d9be6a4d5383..2e8b07eb637a 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -489,6 +489,53 @@ int phy_calibrate(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_calibrate); +/** + * phy_notify_connect() - phy connect notification + * @phy: the phy returned by phy_get() + * @port: the port index for connect + * + * If the phy needs to get connection status, the callback can be used. + * Returns: %0 if successful, a negative error code otherwise + */ +int phy_notify_connect(struct phy *phy, int port) +{ + int ret; + + if (!phy || !phy->ops->connect) + return 0; + + mutex_lock(&phy->mutex); + ret = phy->ops->connect(phy, port); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_notify_connect); + +/** + * phy_notify_disconnect() - phy disconnect notification + * @phy: the phy returned by phy_get() + * @port: the port index for disconnect + * + * If the phy needs to get connection status, the callback can be used. + * + * Returns: %0 if successful, a negative error code otherwise + */ +int phy_notify_disconnect(struct phy *phy, int port) +{ + int ret; + + if (!phy || !phy->ops->disconnect) + return 0; + + mutex_lock(&phy->mutex); + ret = phy->ops->disconnect(phy, port); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_notify_disconnect); + /** * phy_configure() - Changes the phy parameters * @phy: the phy returned by phy_get() diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index f6d607ef0e80..aa76609ba258 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -122,6 +122,11 @@ struct phy_ops { union phy_configure_opts *opts); int (*reset)(struct phy *phy); int (*calibrate)(struct phy *phy); + + /* notify phy connect status change */ + int (*connect)(struct phy *phy, int port); + int (*disconnect)(struct phy *phy, int port); + void (*release)(struct phy *phy); struct module *owner; }; @@ -243,6 +248,8 @@ static inline enum phy_mode phy_get_mode(struct phy *phy) } int phy_reset(struct phy *phy); int phy_calibrate(struct phy *phy); +int phy_notify_connect(struct phy *phy, int port); +int phy_notify_disconnect(struct phy *phy, int port); static inline int phy_get_bus_width(struct phy *phy) { return phy->attrs.bus_width; @@ -396,6 +403,20 @@ static inline int phy_calibrate(struct phy *phy) return -ENOSYS; } +static inline int phy_notify_connect(struct phy *phy, int index) +{ + if (!phy) + return 0; + return -ENOSYS; +} + +static inline int phy_notify_disconnect(struct phy *phy, int index) +{ + if (!phy) + return 0; + return -ENOSYS; +} + static inline int phy_configure(struct phy *phy, union phy_configure_opts *opts) { -- cgit v1.2.3 From 7494d4bc8e32a9480fd56b018db8e404b54b24e6 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Mon, 8 Jan 2024 19:16:14 +0000 Subject: usb: typec: altmodes: add typec_cable_ops to typec_altmode Add typec_cable_ops struct for enter, exit, and vdm. The struct is added to typec_altmode so port alt modes can have access to partner and cable specific callbacks, and alt mode drivers can specify operations over SOP' and SOP'' without modifying the existing API. typec_port_register_cable_ops is added as a new symbol for port drivers to use to register cable operations to their registered port alt modes. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240108191620.987785-15-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/bus.c | 102 ++++++++++++++++++++++++++++++++++++++ drivers/usb/typec/class.c | 19 +++++++ include/linux/usb/typec.h | 4 ++ include/linux/usb/typec_altmode.h | 20 ++++++++ 4 files changed, 145 insertions(+) (limited to 'include') diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index e95ec7e382bb..6ea103e1abae 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -244,6 +244,108 @@ typec_altmode_get_partner(struct typec_altmode *adev) } EXPORT_SYMBOL_GPL(typec_altmode_get_partner); +/* -------------------------------------------------------------------------- */ +/* API for cable alternate modes */ + +/** + * typec_cable_altmode_enter - Enter Mode + * @adev: The alternate mode + * @sop: Cable plug target for Enter Mode command + * @vdo: VDO for the Enter Mode command + * + * Alternate mode drivers use this function to enter mode on the cable plug. + * If the alternate mode does not require VDO, @vdo must be NULL. + */ +int typec_cable_altmode_enter(struct typec_altmode *adev, enum typec_plug_index sop, u32 *vdo) +{ + struct altmode *partner = to_altmode(adev)->partner; + struct typec_altmode *pdev; + + if (!adev || adev->active) + return 0; + + if (!partner) + return -ENODEV; + + pdev = &partner->adev; + + if (!pdev->active) + return -EPERM; + + if (!pdev->cable_ops || !pdev->cable_ops->enter) + return -EOPNOTSUPP; + + return pdev->cable_ops->enter(pdev, sop, vdo); +} +EXPORT_SYMBOL_GPL(typec_cable_altmode_enter); + +/** + * typec_cable_altmode_exit - Exit Mode + * @adev: The alternate mode + * @sop: Cable plug target for Exit Mode command + * + * The alternate mode drivers use this function to exit mode on the cable plug. + */ +int typec_cable_altmode_exit(struct typec_altmode *adev, enum typec_plug_index sop) +{ + struct altmode *partner = to_altmode(adev)->partner; + struct typec_altmode *pdev; + + if (!adev || !adev->active) + return 0; + + if (!partner) + return -ENODEV; + + pdev = &partner->adev; + + if (!pdev->cable_ops || !pdev->cable_ops->exit) + return -EOPNOTSUPP; + + return pdev->cable_ops->exit(pdev, sop); +} +EXPORT_SYMBOL_GPL(typec_cable_altmode_exit); + +/** + * typec_cable_altmode_vdm - Send Vendor Defined Messages (VDM) between the cable plug and port. + * @adev: Alternate mode handle + * @sop: Cable plug target for VDM + * @header: VDM Header + * @vdo: Array of Vendor Defined Data Objects + * @count: Number of Data Objects + * + * The alternate mode drivers use this function for SVID specific communication + * with the cable plugs. The port drivers use it to deliver the Structured VDMs + * received from the cable plugs to the alternate mode drivers. + */ +int typec_cable_altmode_vdm(struct typec_altmode *adev, enum typec_plug_index sop, + const u32 header, const u32 *vdo, int count) +{ + struct altmode *altmode; + struct typec_altmode *pdev; + + if (!adev) + return 0; + + altmode = to_altmode(adev); + + if (is_typec_plug(adev->dev.parent)) { + if (!altmode->partner) + return -ENODEV; + pdev = &altmode->partner->adev; + } else { + if (!altmode->plug[sop]) + return -ENODEV; + pdev = &altmode->plug[sop]->adev; + } + + if (!pdev->cable_ops || !pdev->cable_ops->vdm) + return -EOPNOTSUPP; + + return pdev->cable_ops->vdm(pdev, sop, header, vdo, count); +} +EXPORT_SYMBOL_GPL(typec_cable_altmode_vdm); + /* -------------------------------------------------------------------------- */ /* API for the alternate mode drivers */ diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 015aa9253353..8fc9795d6bd4 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -2280,6 +2280,25 @@ void typec_port_register_altmodes(struct typec_port *port, } EXPORT_SYMBOL_GPL(typec_port_register_altmodes); +/** + * typec_port_register_cable_ops - Register typec_cable_ops to port altmodes + * @altmodes: USB Type-C Port's altmode vector + * @max_altmodes: The maximum number of alt modes supported by the port + * @ops: Cable alternate mode vector + */ +void typec_port_register_cable_ops(struct typec_altmode **altmodes, int max_altmodes, + const struct typec_cable_ops *ops) +{ + int i; + + for (i = 0; i < max_altmodes; i++) { + if (!altmodes[i]) + return; + altmodes[i]->cable_ops = ops; + } +} +EXPORT_SYMBOL_GPL(typec_port_register_cable_ops); + /** * typec_register_port - Register a USB Type-C Port * @parent: Parent device diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index a05d6f6f2536..38f93d72fd1b 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -18,6 +18,7 @@ struct typec_cable; struct typec_plug; struct typec_port; struct typec_altmode_ops; +struct typec_cable_ops; struct fwnode_handle; struct device; @@ -157,6 +158,9 @@ void typec_port_register_altmodes(struct typec_port *port, const struct typec_altmode_ops *ops, void *drvdata, struct typec_altmode **altmodes, size_t n); +void typec_port_register_cable_ops(struct typec_altmode **altmodes, int max_altmodes, + const struct typec_cable_ops *ops); + void typec_unregister_altmode(struct typec_altmode *altmode); struct typec_port *typec_altmode2port(struct typec_altmode *alt); diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 28aeef8f9e7b..72ec8058543a 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -20,6 +20,7 @@ struct typec_altmode_ops; * @active: Tells has the mode been entered or not * @desc: Optional human readable description of the mode * @ops: Operations vector from the driver + * @cable_ops: Cable operations vector from the driver. */ struct typec_altmode { struct device dev; @@ -30,6 +31,7 @@ struct typec_altmode { char *desc; const struct typec_altmode_ops *ops; + const struct typec_cable_ops *cable_ops; }; #define to_typec_altmode(d) container_of(d, struct typec_altmode, dev) @@ -75,6 +77,24 @@ int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf, const struct typec_altmode * typec_altmode_get_partner(struct typec_altmode *altmode); +/** + * struct typec_cable_ops - Cable alternate mode operations vector + * @enter: Operations to be executed with Enter Mode Command + * @exit: Operations to be executed with Exit Mode Command + * @vdm: Callback for SVID specific commands + */ +struct typec_cable_ops { + int (*enter)(struct typec_altmode *altmode, enum typec_plug_index sop, u32 *vdo); + int (*exit)(struct typec_altmode *altmode, enum typec_plug_index sop); + int (*vdm)(struct typec_altmode *altmode, enum typec_plug_index sop, + const u32 hdr, const u32 *vdo, int cnt); +}; + +int typec_cable_altmode_enter(struct typec_altmode *altmode, enum typec_plug_index sop, u32 *vdo); +int typec_cable_altmode_exit(struct typec_altmode *altmode, enum typec_plug_index sop); +int typec_cable_altmode_vdm(struct typec_altmode *altmode, enum typec_plug_index sop, + const u32 header, const u32 *vdo, int count); + /* * These are the connector states (USB, Safe and Alt Mode) defined in USB Type-C * Specification. SVID specific connector states are expected to follow and -- cgit v1.2.3 From 231b7318413cef0f8e5c2ca8db1a95b666c25d70 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Mon, 8 Jan 2024 19:16:15 +0000 Subject: usb: typec: altmodes: add svdm version info for typec cables Add typec_cable_set_svdm_version and typec_get_cable_svdm version symbols. Cables can operate under a lower PD revision than the port partner, and the max SVDM version is tied to the PD revision. So, typec_cable maintains its own svdm_version. Add typec_altmode_get_cable_svdm_version to return the cable's negotiated svdm_version for altmode drivers to use. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240108191620.987785-16-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 40 +++++++++++++++++++++++++++++++++++++++ drivers/usb/typec/class.h | 1 + include/linux/usb/typec.h | 3 +++ include/linux/usb/typec_altmode.h | 10 ++++++++++ 4 files changed, 54 insertions(+) (limited to 'include') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 8fc9795d6bd4..caea2b829980 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -2131,6 +2131,46 @@ int typec_get_negotiated_svdm_version(struct typec_port *port) } EXPORT_SYMBOL_GPL(typec_get_negotiated_svdm_version); +/** + * typec_get_cable_svdm_version - Get cable negotiated SVDM Version + * @port: USB Type-C Port. + * + * Get the negotiated SVDM Version for the cable. The Version is set to the port + * default value based on the PD Revision during cable registration, and updated + * after a successful Discover Identity if the negotiated value is less than the + * default. + * + * Returns usb_pd_svdm_ver if the cable has been registered otherwise -ENODEV. + */ +int typec_get_cable_svdm_version(struct typec_port *port) +{ + enum usb_pd_svdm_ver svdm_version; + struct device *cable_dev; + + cable_dev = device_find_child(&port->dev, NULL, cable_match); + if (!cable_dev) + return -ENODEV; + + svdm_version = to_typec_cable(cable_dev)->svdm_version; + put_device(cable_dev); + + return svdm_version; +} +EXPORT_SYMBOL_GPL(typec_get_cable_svdm_version); + +/** + * typec_cable_set_svdm_version - Set negotiated Structured VDM (SVDM) Version + * @cable: USB Type-C Active Cable that supports SVDM + * @svdm_version: Negotiated SVDM Version + * + * This routine is used to save the negotiated SVDM Version. + */ +void typec_cable_set_svdm_version(struct typec_cable *cable, enum usb_pd_svdm_ver svdm_version) +{ + cable->svdm_version = svdm_version; +} +EXPORT_SYMBOL_GPL(typec_cable_set_svdm_version); + /** * typec_get_drvdata - Return private driver data pointer * @port: USB Type-C port diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index c36761ba3f59..759b98355eeb 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -23,6 +23,7 @@ struct typec_cable { struct usb_pd_identity *identity; unsigned int active:1; u16 pd_revision; /* 0300H = "3.0" */ + enum usb_pd_svdm_ver svdm_version; }; struct typec_partner { diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 38f93d72fd1b..b35b427561ab 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -337,6 +337,9 @@ void typec_partner_set_svdm_version(struct typec_partner *partner, enum usb_pd_svdm_ver svdm_version); int typec_get_negotiated_svdm_version(struct typec_port *port); +int typec_get_cable_svdm_version(struct typec_port *port); +void typec_cable_set_svdm_version(struct typec_cable *cable, enum usb_pd_svdm_ver svdm_version); + struct usb_power_delivery *typec_partner_usb_power_delivery_register(struct typec_partner *partner, struct usb_power_delivery_desc *desc); diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 72ec8058543a..b3c0866ea70f 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -95,6 +95,16 @@ int typec_cable_altmode_exit(struct typec_altmode *altmode, enum typec_plug_inde int typec_cable_altmode_vdm(struct typec_altmode *altmode, enum typec_plug_index sop, const u32 header, const u32 *vdo, int count); +/** + * typec_altmode_get_cable_svdm_version - Get negotiated SVDM version for cable plug + * @altmode: Handle to the alternate mode + */ +static inline int +typec_altmode_get_cable_svdm_version(struct typec_altmode *altmode) +{ + return typec_get_cable_svdm_version(typec_altmode2port(altmode)); +} + /* * These are the connector states (USB, Safe and Alt Mode) defined in USB Type-C * Specification. SVID specific connector states are expected to follow and -- cgit v1.2.3 From 59cd27a0cab1ceddcc4251309fd3643921ed9ab9 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Mon, 8 Jan 2024 19:16:16 +0000 Subject: usb: typec: tcpci: add cable_comm_capable attribute Add cable_comm_capable to tcpci_data for tcpci drivers to indicate that the port tcpc is capable of communicating to cables over SOP. A corresponding tcpci callback tcpci_cable_comm_capable returns this value. The tcpm will primarily use this in later patches to determine if the port can transmit and receive SOP' messages. Maxim based tcpci drivers are capable of SOP' communication, so the cable_comm_capable flag is set to true. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240108191620.987785-17-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci.c | 8 ++++++++ drivers/usb/typec/tcpm/tcpci_maxim_core.c | 1 + include/linux/usb/tcpci.h | 3 +++ include/linux/usb/tcpm.h | 4 ++++ 4 files changed, 16 insertions(+) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 0ee3e6e29bb1..1ededbcecc09 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -584,6 +584,13 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type return 0; } +static bool tcpci_cable_comm_capable(struct tcpc_dev *tcpc) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + + return tcpci->data->cable_comm_capable; +} + static int tcpci_init(struct tcpc_dev *tcpc) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); @@ -793,6 +800,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) tcpci->tcpc.enable_frs = tcpci_enable_frs; tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus; tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable; + tcpci->tcpc.cable_comm_capable = tcpci_cable_comm_capable; if (tcpci->data->check_contaminant) tcpci->tcpc.check_contaminant = tcpci_check_contaminant; diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index 7fb966fd639b..7b2d4e6e52a2 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -478,6 +478,7 @@ static int max_tcpci_probe(struct i2c_client *client) chip->data.vbus_vsafe0v = true; chip->data.set_partner_usb_comm_capable = max_tcpci_set_partner_usb_comm_capable; chip->data.check_contaminant = max_tcpci_check_contaminant; + chip->data.cable_comm_capable = true; max_tcpci_init_regs(chip); chip->tcpci = tcpci_register_port(chip->dev, &chip->data); diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 467e8045e9f8..1d0b849defd0 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -198,12 +198,15 @@ struct tcpci; * Chip level drivers are expected to check for contaminant and call * tcpm_clean_port when the port is clean to put the port back into * toggling state. + * @cable_comm_capable + * optional; Set when TCPC can communicate with cable plugs over SOP' */ struct tcpci_data { struct regmap *regmap; unsigned char TX_BUF_BYTE_x_hidden:1; unsigned char auto_discharge_disconnect:1; unsigned char vbus_vsafe0v:1; + unsigned char cable_comm_capable:1; int (*init)(struct tcpci *tcpci, struct tcpci_data *data); int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data, diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 65fac5e1f317..430fa3ec69bb 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -119,6 +119,9 @@ enum tcpm_transmit_type { * at the end of the deboumce period or when the port is still * toggling. Chip level drivers are expected to check for contaminant * and call tcpm_clean_port when the port is clean. + * @cable_comm_capable + * Optional; Returns whether cable communication over SOP' is supported + * by the tcpc */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -154,6 +157,7 @@ struct tcpc_dev { bool (*is_vbus_vsafe0v)(struct tcpc_dev *dev); void (*set_partner_usb_comm_capable)(struct tcpc_dev *dev, bool enable); void (*check_contaminant)(struct tcpc_dev *dev); + bool (*cable_comm_capable)(struct tcpc_dev *dev); }; struct tcpm_port; -- cgit v1.2.3 From 3bbb9ba4f66006f27ad0d5ceaf2480117e16d489 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Mon, 8 Jan 2024 19:16:17 +0000 Subject: usb: typec: tcpci: add tcpm_transmit_type to tcpm_pd_receive tcpm_pd_receive adds the SOP type as a parameter, and passes it within the pd_rx_event struct for tcpm_pd_rx_handler to use. For now, the handler drops all SOP' messages. Maxim based tcpci drivers are capable of SOP' communication, so process_rx now takes the SOP type into account and passes the value to tcpm_pd_receive. tcpci_set_pd_rx now utilizes the cable_comm_capable flag to determine if TCPC_RX_DETECT_SOP1 should be added to the bitfield when enabling PD message reception. For all other consumers of tcpm_pd_receive, default the new field to TCPC_TX_SOP. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240108191620.987785-18-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302.c | 2 +- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c | 2 +- drivers/usb/typec/tcpm/tcpci.c | 7 +++++-- drivers/usb/typec/tcpm/tcpci_maxim_core.c | 20 +++++++++++++++++--- drivers/usb/typec/tcpm/tcpm.c | 10 +++++++++- drivers/usb/typec/tcpm/wcove.c | 2 +- include/linux/usb/tcpci.h | 1 + include/linux/usb/tcpm.h | 3 ++- 8 files changed, 37 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index bc21006e979c..ef18a448b740 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1467,7 +1467,7 @@ static int fusb302_pd_read_message(struct fusb302_chip *chip, if ((!len) && (pd_header_type_le(msg->header) == PD_CTRL_GOOD_CRC)) tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS); else - tcpm_pd_receive(chip->tcpm_port, msg); + tcpm_pd_receive(chip->tcpm_port, msg, TCPC_TX_SOP); return ret; } diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c index 52c81378e36e..a3154085ae32 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c @@ -299,7 +299,7 @@ done: if (!ret) { dev_vdbg(dev, "pd_receive: handing %d bytes to tcpm\n", size); - tcpm_pd_receive(pmic_typec_pdphy->tcpm_port, &msg); + tcpm_pd_receive(pmic_typec_pdphy->tcpm_port, &msg, TCPC_TX_SOP); } } diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 1ededbcecc09..8ea4ed159a13 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -445,8 +445,11 @@ static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable) unsigned int reg = 0; int ret; - if (enable) + if (enable) { reg = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET; + if (tcpci->data->cable_comm_capable) + reg |= TCPC_RX_DETECT_SOP1; + } ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg); if (ret < 0) return ret; @@ -719,7 +722,7 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) /* Read complete, clear RX status alert bit */ tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS); - tcpm_pd_receive(tcpci->port, &msg); + tcpm_pd_receive(tcpci->port, &msg, TCPC_TX_SOP); } if (tcpci->data->vbus_vsafe0v && (status & TCPC_ALERT_EXTENDED_STATUS)) { diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index 7b2d4e6e52a2..f9f838df43f7 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -128,6 +128,7 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) u8 count, frame_type, rx_buf[TCPC_RECEIVE_BUFFER_LEN]; int ret, payload_index; u8 *rx_buf_ptr; + enum tcpm_transmit_type rx_type; /* * READABLE_BYTE_COUNT: Indicates the number of bytes in the RX_BUF_BYTE_x registers @@ -143,10 +144,23 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) count = rx_buf[TCPC_RECEIVE_BUFFER_COUNT_OFFSET]; frame_type = rx_buf[TCPC_RECEIVE_BUFFER_FRAME_TYPE_OFFSET]; - if (count == 0 || frame_type != TCPC_RX_BUF_FRAME_TYPE_SOP) { + switch (frame_type) { + case TCPC_RX_BUF_FRAME_TYPE_SOP1: + rx_type = TCPC_TX_SOP_PRIME; + break; + case TCPC_RX_BUF_FRAME_TYPE_SOP: + rx_type = TCPC_TX_SOP; + break; + default: + rx_type = TCPC_TX_SOP; + break; + } + + if (count == 0 || (frame_type != TCPC_RX_BUF_FRAME_TYPE_SOP && + frame_type != TCPC_RX_BUF_FRAME_TYPE_SOP1)) { max_tcpci_write16(chip, TCPC_ALERT, TCPC_ALERT_RX_STATUS); dev_err(chip->dev, "%s\n", count == 0 ? "error: count is 0" : - "error frame_type is not SOP"); + "error frame_type is not SOP/SOP'"); return; } @@ -183,7 +197,7 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) if (ret < 0) return; - tcpm_pd_receive(chip->port, &msg); + tcpm_pd_receive(chip->port, &msg, rx_type); } static int max_tcpci_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata, bool source, bool sink) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 5945e3a2b0f7..ff0fcf560c88 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -518,6 +518,7 @@ struct pd_rx_event { struct kthread_work work; struct tcpm_port *port; struct pd_message msg; + enum tcpm_transmit_type rx_sop_type; }; static const char * const pd_rev[] = { @@ -2981,12 +2982,17 @@ static void tcpm_pd_rx_handler(struct kthread_work *work) const struct pd_message *msg = &event->msg; unsigned int cnt = pd_header_cnt_le(msg->header); struct tcpm_port *port = event->port; + enum tcpm_transmit_type rx_sop_type = event->rx_sop_type; mutex_lock(&port->lock); tcpm_log(port, "PD RX, header: %#x [%d]", le16_to_cpu(msg->header), port->attached); + /* Ignore SOP' for now */ + if (rx_sop_type == TCPC_TX_SOP_PRIME) + goto done; + if (port->attached) { enum pd_ctrl_msg_type type = pd_header_type_le(msg->header); unsigned int msgid = pd_header_msgid_le(msg->header); @@ -3028,7 +3034,8 @@ done: kfree(event); } -void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg) +void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg, + enum tcpm_transmit_type rx_sop_type) { struct pd_rx_event *event; @@ -3038,6 +3045,7 @@ void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg) kthread_init_work(&event->work, tcpm_pd_rx_handler); event->port = port; + event->rx_sop_type = rx_sop_type; memcpy(&event->msg, msg, sizeof(*msg)); kthread_queue_work(port->wq, &event->work); } diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 87d4abde0ea2..cf719307b3f6 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -535,7 +535,7 @@ static irqreturn_t wcove_typec_irq(int irq, void *data) goto err; } - tcpm_pd_receive(wcove->tcpm, &msg); + tcpm_pd_receive(wcove->tcpm, &msg, TCPC_TX_SOP); ret = regmap_read(wcove->regmap, USBC_RXSTATUS, &status); diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 1d0b849defd0..9ed6d62c9c5f 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -145,6 +145,7 @@ #define TCPC_RX_BYTE_CNT 0x30 #define TCPC_RX_BUF_FRAME_TYPE 0x31 #define TCPC_RX_BUF_FRAME_TYPE_SOP 0 +#define TCPC_RX_BUF_FRAME_TYPE_SOP1 1 #define TCPC_RX_HDR 0x32 #define TCPC_RX_DATA 0x34 /* through 0x4f */ diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 430fa3ec69bb..41d1ac9c8bbf 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -170,7 +170,8 @@ void tcpm_cc_change(struct tcpm_port *port); void tcpm_sink_frs(struct tcpm_port *port); void tcpm_sourcing_vbus(struct tcpm_port *port); void tcpm_pd_receive(struct tcpm_port *port, - const struct pd_message *msg); + const struct pd_message *msg, + enum tcpm_transmit_type rx_sop_type); void tcpm_pd_transmit_complete(struct tcpm_port *port, enum tcpm_transmit_status status); void tcpm_pd_hard_reset(struct tcpm_port *port); -- cgit v1.2.3 From 6bd181ba60e198fef6f750b543832f161fbd9f39 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Mon, 8 Jan 2024 19:16:19 +0000 Subject: usb: typec: tcpm: add control message support to sop' Add tx_sop_type to tcpm_pd_send_control and rx_sop_type to tcpm_pd_ctrl_request. TCPC_TX_SOP is added to all pd_send_control calls, but TCPC_TX_SOP_PRIME is added to pd_send_control for a SOFT_RESET message sent after a Vconn swap that makes the Port the Vconn source. Likewise, tcpm_pd_ctrl_request resets the proper protocol layer depending on rx_sop_type for SOFT_RESET. VCONN_SWAP_TURN_ON_VCONN now moves to a new state, VCONN_SWAP_SEND_SOFT_RESET. This state sends SOFT_RESET over SOP' before transitioning to the ready state if applicable. It transitions after PD_T_VCONN_STABLE, definied in pd.h as the time required for Vconn to be on before transmitting messages. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240108191620.987785-20-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 159 ++++++++++++++++++++++++++++++------------ include/linux/usb/pd.h | 1 + 2 files changed, 115 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index d2ca85c8fec6..a59927925714 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -108,6 +108,7 @@ S(VCONN_SWAP_WAIT_FOR_VCONN), \ S(VCONN_SWAP_TURN_ON_VCONN), \ S(VCONN_SWAP_TURN_OFF_VCONN), \ + S(VCONN_SWAP_SEND_SOFT_RESET), \ \ S(FR_SWAP_SEND), \ S(FR_SWAP_SEND_TIMEOUT), \ @@ -2395,7 +2396,8 @@ static inline enum tcpm_state ready_state(struct tcpm_port *port) } static int tcpm_pd_send_control(struct tcpm_port *port, - enum pd_ctrl_msg_type type); + enum pd_ctrl_msg_type type, + enum tcpm_transmit_type tx_sop_type); static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload, int cnt) @@ -2749,10 +2751,12 @@ static void tcpm_pps_complete(struct tcpm_port *port, int result) } static void tcpm_pd_ctrl_request(struct tcpm_port *port, - const struct pd_message *msg) + const struct pd_message *msg, + enum tcpm_transmit_type rx_sop_type) { enum pd_ctrl_msg_type type = pd_header_type_le(msg->header); enum tcpm_state next_state; + unsigned int rev = pd_header_rev_le(msg->header); /* * Stop VDM state machine if interrupted by other Messages while NOT_SUPP is allowed in @@ -2917,6 +2921,16 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, case SOFT_RESET_SEND: if (port->ams == SOFT_RESET_AMS) tcpm_ams_finish(port); + /* + * SOP' Soft Reset is done after Vconn Swap, + * which returns to ready state + */ + if (rx_sop_type == TCPC_TX_SOP_PRIME) { + if (rev < port->negotiated_rev_prime) + port->negotiated_rev_prime = rev; + tcpm_set_state(port, ready_state(port), 0); + break; + } if (port->pwr_role == TYPEC_SOURCE) { port->upcoming_state = SRC_SEND_CAPABILITIES; tcpm_ams_start(port, POWER_NEGOTIATION); @@ -3116,8 +3130,7 @@ static void tcpm_pd_rx_handler(struct kthread_work *work) if (msgid == port->rx_msgid_prime) goto done; port->rx_msgid_prime = msgid; - /* Ignore SOP' for now */ - goto done; + break; case TCPC_TX_SOP: default: if (msgid == port->rx_msgid && type != PD_CTRL_SOFT_RESET) @@ -3141,7 +3154,7 @@ static void tcpm_pd_rx_handler(struct kthread_work *work) else if (cnt) tcpm_pd_data_request(port, msg); else - tcpm_pd_ctrl_request(port, msg); + tcpm_pd_ctrl_request(port, msg, rx_sop_type); } } @@ -3168,17 +3181,40 @@ void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg, EXPORT_SYMBOL_GPL(tcpm_pd_receive); static int tcpm_pd_send_control(struct tcpm_port *port, - enum pd_ctrl_msg_type type) + enum pd_ctrl_msg_type type, + enum tcpm_transmit_type tx_sop_type) { struct pd_message msg; memset(&msg, 0, sizeof(msg)); - msg.header = PD_HEADER_LE(type, port->pwr_role, - port->data_role, - port->negotiated_rev, - port->message_id, 0); + switch (tx_sop_type) { + case TCPC_TX_SOP_PRIME: + msg.header = PD_HEADER_LE(type, + 0, /* Cable Plug Indicator for DFP/UFP */ + 0, /* Reserved */ + port->negotiated_rev, + port->message_id_prime, + 0); + break; + case TCPC_TX_SOP: + msg.header = PD_HEADER_LE(type, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + 0); + break; + default: + msg.header = PD_HEADER_LE(type, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + 0); + break; + } - return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); + return tcpm_pd_transmit(port, tx_sop_type, &msg); } /* @@ -3197,13 +3233,13 @@ static bool tcpm_send_queued_message(struct tcpm_port *port) switch (queued_message) { case PD_MSG_CTRL_WAIT: - tcpm_pd_send_control(port, PD_CTRL_WAIT); + tcpm_pd_send_control(port, PD_CTRL_WAIT, TCPC_TX_SOP); break; case PD_MSG_CTRL_REJECT: - tcpm_pd_send_control(port, PD_CTRL_REJECT); + tcpm_pd_send_control(port, PD_CTRL_REJECT, TCPC_TX_SOP); break; case PD_MSG_CTRL_NOT_SUPP: - tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP); + tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); break; case PD_MSG_DATA_SINK_CAP: ret = tcpm_pd_send_sink_caps(port); @@ -4218,7 +4254,7 @@ static void run_state_machine(struct tcpm_port *port) case SRC_NEGOTIATE_CAPABILITIES: ret = tcpm_pd_check_request(port); if (ret < 0) { - tcpm_pd_send_control(port, PD_CTRL_REJECT); + tcpm_pd_send_control(port, PD_CTRL_REJECT, TCPC_TX_SOP); if (!port->explicit_contract) { tcpm_set_state(port, SRC_WAIT_NEW_CAPABILITIES, 0); @@ -4226,7 +4262,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_READY, 0); } } else { - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); tcpm_set_partner_usb_comm_capable(port, !!(port->sink_request & RDO_USB_COMM)); tcpm_set_state(port, SRC_TRANSITION_SUPPLY, @@ -4235,7 +4271,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SRC_TRANSITION_SUPPLY: /* XXX: regulator_set_voltage(vbus, ...) */ - tcpm_pd_send_control(port, PD_CTRL_PS_RDY); + tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP); port->explicit_contract = true; typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_PD); port->pwr_opmode = TYPEC_PWR_MODE_PD; @@ -4720,7 +4756,7 @@ static void run_state_machine(struct tcpm_port *port) /* remove existing capabilities */ usb_power_delivery_unregister_capabilities(port->partner_source_caps); port->partner_source_caps = NULL; - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); tcpm_ams_finish(port); if (port->pwr_role == TYPEC_SOURCE) { port->upcoming_state = SRC_SEND_CAPABILITIES; @@ -4737,28 +4773,41 @@ static void run_state_machine(struct tcpm_port *port) tcpm_ams_start(port, SOFT_RESET_AMS); break; case SOFT_RESET_SEND: - port->message_id = 0; - port->rx_msgid = -1; - /* remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps = NULL; - if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET)) - tcpm_set_state_cond(port, hard_reset_state(port), 0); - else - tcpm_set_state_cond(port, hard_reset_state(port), - PD_T_SENDER_RESPONSE); + /* + * Power Delivery 3.0 Section 6.3.13 + * + * A Soft_Reset Message Shall be targeted at a specific entity + * depending on the type of SOP* packet used. + */ + if (port->tx_sop_type == TCPC_TX_SOP_PRIME) { + port->message_id_prime = 0; + port->rx_msgid_prime = -1; + tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET, TCPC_TX_SOP_PRIME); + tcpm_set_state_cond(port, ready_state(port), PD_T_SENDER_RESPONSE); + } else { + port->message_id = 0; + port->rx_msgid = -1; + /* remove existing capabilities */ + usb_power_delivery_unregister_capabilities(port->partner_source_caps); + port->partner_source_caps = NULL; + if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET, TCPC_TX_SOP)) + tcpm_set_state_cond(port, hard_reset_state(port), 0); + else + tcpm_set_state_cond(port, hard_reset_state(port), + PD_T_SENDER_RESPONSE); + } break; /* DR_Swap states */ case DR_SWAP_SEND: - tcpm_pd_send_control(port, PD_CTRL_DR_SWAP); + tcpm_pd_send_control(port, PD_CTRL_DR_SWAP, TCPC_TX_SOP); if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) port->send_discover = true; tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; case DR_SWAP_ACCEPT: - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) port->send_discover = true; tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); @@ -4782,7 +4831,7 @@ static void run_state_machine(struct tcpm_port *port) break; case FR_SWAP_SEND: - if (tcpm_pd_send_control(port, PD_CTRL_FR_SWAP)) { + if (tcpm_pd_send_control(port, PD_CTRL_FR_SWAP, TCPC_TX_SOP)) { tcpm_set_state(port, ERROR_RECOVERY, 0); break; } @@ -4802,7 +4851,7 @@ static void run_state_machine(struct tcpm_port *port) break; case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED: tcpm_set_pwr_role(port, TYPEC_SOURCE); - if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) { + if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP)) { tcpm_set_state(port, ERROR_RECOVERY, 0); break; } @@ -4812,11 +4861,11 @@ static void run_state_machine(struct tcpm_port *port) /* PR_Swap states */ case PR_SWAP_ACCEPT: - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); tcpm_set_state(port, PR_SWAP_START, 0); break; case PR_SWAP_SEND: - tcpm_pd_send_control(port, PD_CTRL_PR_SWAP); + tcpm_pd_send_control(port, PD_CTRL_PR_SWAP, TCPC_TX_SOP); tcpm_set_state_cond(port, PR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; @@ -4858,7 +4907,7 @@ static void run_state_machine(struct tcpm_port *port) * supply is turned off" */ tcpm_set_pwr_role(port, TYPEC_SINK); - if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) { + if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP)) { tcpm_set_state(port, ERROR_RECOVERY, 0); break; } @@ -4905,17 +4954,17 @@ static void run_state_machine(struct tcpm_port *port) * Source." */ tcpm_set_pwr_role(port, TYPEC_SOURCE); - tcpm_pd_send_control(port, PD_CTRL_PS_RDY); + tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP); tcpm_set_state(port, SRC_STARTUP, PD_T_SWAP_SRC_START); break; case VCONN_SWAP_ACCEPT: - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); tcpm_ams_finish(port); tcpm_set_state(port, VCONN_SWAP_START, 0); break; case VCONN_SWAP_SEND: - tcpm_pd_send_control(port, PD_CTRL_VCONN_SWAP); + tcpm_pd_send_control(port, PD_CTRL_VCONN_SWAP, TCPC_TX_SOP); tcpm_set_state(port, VCONN_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; @@ -4934,14 +4983,34 @@ static void run_state_machine(struct tcpm_port *port) PD_T_VCONN_SOURCE_ON); break; case VCONN_SWAP_TURN_ON_VCONN: - tcpm_set_vconn(port, true); - tcpm_pd_send_control(port, PD_CTRL_PS_RDY); - tcpm_set_state(port, ready_state(port), 0); + ret = tcpm_set_vconn(port, true); + tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP); + /* + * USB PD 3.0 Section 6.4.4.3.1 + * + * Note that a Cable Plug or VPD will not be ready for PD + * Communication until tVCONNStable after VCONN has been applied + */ + if (!ret) + tcpm_set_state(port, VCONN_SWAP_SEND_SOFT_RESET, + PD_T_VCONN_STABLE); + else + tcpm_set_state(port, ready_state(port), 0); break; case VCONN_SWAP_TURN_OFF_VCONN: tcpm_set_vconn(port, false); tcpm_set_state(port, ready_state(port), 0); break; + case VCONN_SWAP_SEND_SOFT_RESET: + tcpm_swap_complete(port, port->swap_status); + if (tcpm_can_communicate_sop_prime(port)) { + port->tx_sop_type = TCPC_TX_SOP_PRIME; + port->upcoming_state = SOFT_RESET_SEND; + tcpm_ams_start(port, SOFT_RESET_AMS); + } else { + tcpm_set_state(port, ready_state(port), 0); + } + break; case DR_SWAP_CANCEL: case PR_SWAP_CANCEL: @@ -4977,7 +5046,7 @@ static void run_state_machine(struct tcpm_port *port) } break; case GET_STATUS_SEND: - tcpm_pd_send_control(port, PD_CTRL_GET_STATUS); + tcpm_pd_send_control(port, PD_CTRL_GET_STATUS, TCPC_TX_SOP); tcpm_set_state(port, GET_STATUS_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; @@ -4985,7 +5054,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ready_state(port), 0); break; case GET_PPS_STATUS_SEND: - tcpm_pd_send_control(port, PD_CTRL_GET_PPS_STATUS); + tcpm_pd_send_control(port, PD_CTRL_GET_PPS_STATUS, TCPC_TX_SOP); tcpm_set_state(port, GET_PPS_STATUS_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; @@ -4993,7 +5062,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ready_state(port), 0); break; case GET_SINK_CAP: - tcpm_pd_send_control(port, PD_CTRL_GET_SINK_CAP); + tcpm_pd_send_control(port, PD_CTRL_GET_SINK_CAP, TCPC_TX_SOP); tcpm_set_state(port, GET_SINK_CAP_TIMEOUT, PD_T_SENDER_RESPONSE); break; case GET_SINK_CAP_TIMEOUT: @@ -5033,7 +5102,7 @@ static void run_state_machine(struct tcpm_port *port) /* Chunk state */ case CHUNK_NOT_SUPP: - tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP); + tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); tcpm_set_state(port, port->pwr_role == TYPEC_SOURCE ? SRC_READY : SNK_READY, 0); break; default: diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index eb626af0e4e7..d50098fb16b5 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -483,6 +483,7 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_T_BIST_CONT_MODE 50 /* 30 - 60 ms */ #define PD_T_SINK_TX 16 /* 16 - 20 ms */ #define PD_T_CHUNK_NOT_SUPP 42 /* 40 - 50 ms */ +#define PD_T_VCONN_STABLE 50 #define PD_T_DRP_TRY 100 /* 75 - 150 ms */ #define PD_T_DRP_TRYWAIT 600 /* 400 - 800 ms */ -- cgit v1.2.3 From 030509ac473da439e3d5438b1cd3c5b899844046 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Mon, 8 Jan 2024 19:16:20 +0000 Subject: usb: typec: tcpci: add attempt_vconn_swap_discovery callback Add attempt_vconn_swap_discovery callback to determine whether the TCPM should perform a Vconn swap following Discover Identity on SOP. The tcpci will return false unless chip level drivers implement the callback. Maxim based TCPCs will return true unless the last connection resulted in a Vconn Over Current Fault, which may be the result of the Vconn swap. In addition to the port resetting, the TCPCI will veto the next Vconn swap from occurring. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240108191620.987785-21-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci.c | 11 +++++++++++ drivers/usb/typec/tcpm/tcpci_maxim.h | 1 + drivers/usb/typec/tcpm/tcpci_maxim_core.c | 17 ++++++++++++++++- include/linux/usb/tcpci.h | 9 +++++++++ include/linux/usb/tcpm.h | 9 +++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 8ea4ed159a13..40c7b6224c74 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -594,6 +594,16 @@ static bool tcpci_cable_comm_capable(struct tcpc_dev *tcpc) return tcpci->data->cable_comm_capable; } +static bool tcpci_attempt_vconn_swap_discovery(struct tcpc_dev *tcpc) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + + if (tcpci->data->attempt_vconn_swap_discovery) + return tcpci->data->attempt_vconn_swap_discovery(tcpci, tcpci->data); + + return false; +} + static int tcpci_init(struct tcpc_dev *tcpc) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); @@ -804,6 +814,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus; tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable; tcpci->tcpc.cable_comm_capable = tcpci_cable_comm_capable; + tcpci->tcpc.attempt_vconn_swap_discovery = tcpci_attempt_vconn_swap_discovery; if (tcpci->data->check_contaminant) tcpci->tcpc.check_contaminant = tcpci_check_contaminant; diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h index 2c1c4d161b0d..78ff3b73ee7e 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.h +++ b/drivers/usb/typec/tcpm/tcpci_maxim.h @@ -62,6 +62,7 @@ struct max_tcpci_chip { struct i2c_client *client; struct tcpm_port *port; enum contamiant_state contaminant_state; + bool veto_vconn_swap; }; static inline int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val) diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index f9f838df43f7..eec3bcec119c 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -323,8 +323,10 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status) if (ret < 0) return ret; - if (reg_status & TCPC_FAULT_STATUS_VCONN_OC) + if (reg_status & TCPC_FAULT_STATUS_VCONN_OC) { + chip->veto_vconn_swap = true; tcpm_port_error_recovery(chip->port); + } } if (status & TCPC_ALERT_EXTND) { @@ -458,6 +460,18 @@ static void max_tcpci_check_contaminant(struct tcpci *tcpci, struct tcpci_data * tcpm_port_clean(chip->port); } +static bool max_tcpci_attempt_vconn_swap_discovery(struct tcpci *tcpci, struct tcpci_data *tdata) +{ + struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata); + + if (chip->veto_vconn_swap) { + chip->veto_vconn_swap = false; + return false; + } + + return true; +} + static int max_tcpci_probe(struct i2c_client *client) { int ret; @@ -493,6 +507,7 @@ static int max_tcpci_probe(struct i2c_client *client) chip->data.set_partner_usb_comm_capable = max_tcpci_set_partner_usb_comm_capable; chip->data.check_contaminant = max_tcpci_check_contaminant; chip->data.cable_comm_capable = true; + chip->data.attempt_vconn_swap_discovery = max_tcpci_attempt_vconn_swap_discovery; max_tcpci_init_regs(chip); chip->tcpci = tcpci_register_port(chip->dev, &chip->data); diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 9ed6d62c9c5f..47a86b8a4a50 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -201,6 +201,14 @@ struct tcpci; * toggling state. * @cable_comm_capable * optional; Set when TCPC can communicate with cable plugs over SOP' + * @attempt_vconn_swap_discovery: + * Optional; The callback is called by the TCPM when the result of + * a Discover Identity request indicates that the port partner is + * a receptacle capable of modal operation. Chip level TCPCI drivers + * can implement their own policy to determine if and when a Vconn + * swap following Discover Identity on SOP' occurs. + * Return true when the TCPM is allowed to request a Vconn swap + * after Discovery Identity on SOP. */ struct tcpci_data { struct regmap *regmap; @@ -219,6 +227,7 @@ struct tcpci_data { void (*set_partner_usb_comm_capable)(struct tcpci *tcpci, struct tcpci_data *data, bool capable); void (*check_contaminant)(struct tcpci *tcpci, struct tcpci_data *data); + bool (*attempt_vconn_swap_discovery)(struct tcpci *tcpci, struct tcpci_data *data); }; struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 41d1ac9c8bbf..6671427f7eeb 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -122,6 +122,14 @@ enum tcpm_transmit_type { * @cable_comm_capable * Optional; Returns whether cable communication over SOP' is supported * by the tcpc + * @attempt_vconn_swap_discovery: + * Optional; The callback is called by the TCPM when the result of + * a Discover Identity request indicates that the port partner is + * a receptacle capable of modal operation. Chip level TCPCI drivers + * can implement their own policy to determine if and when a Vconn + * swap following Discover Identity on SOP' occurs. + * Return true when the TCPM is allowed to request a Vconn swap + * after Discovery Identity on SOP. */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -158,6 +166,7 @@ struct tcpc_dev { void (*set_partner_usb_comm_capable)(struct tcpc_dev *dev, bool enable); void (*check_contaminant)(struct tcpc_dev *dev); bool (*cable_comm_capable)(struct tcpc_dev *dev); + bool (*attempt_vconn_swap_discovery)(struct tcpc_dev *dev); }; struct tcpm_port; -- cgit v1.2.3 From fb7ff25ae43332cb64c9e7bbbe36a6cc308d8de1 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Mon, 8 Jan 2024 19:16:21 +0000 Subject: usb: typec: tcpm: add discover identity support for SOP' Add data message handling and Discover Identity SVDM over SOP' This patch contains the following changes: 1. pd_vdo Add VDO indices for active and passive cables, documentation to reflect expected number of objects depending on PD Revision, and macro to indicate port parter is data host capable. 2. tcpm Add typec_cable and typec_plug to tcpm_port to maintain cable and plug information. tcpm_port also adds send_discover_prime to indicate that Discover Identity should be sent out of the ready state. tcpm_queue_vdm and tcpm_send_vdm now take the SOP* type when transmitting messages. tcpm_handle_vdm_request and tcpm_pd_svdm also use the SOP* type. tcpm_pd_svdm handles Discover Identity messages for SOP and SOP'. In the SOP case, the port uses tcpm_attempt_vconn_swap_discovery to determine if a Vconn swap is needed for cable communication. Otherwise, the port will send Discover Identity on SOP' if it can, or default to Discover SVIDs. svdm_consume_identity_sop_prime consumes the result of Discover Identity on SOP'. It fills out cable identity and description, and it registers the cable. The SOP' plug is registered as well. The VDM state machine is adjusted to construct messages based on the SOP* type. If a transmission error occurs after the max number of retries for Discover Identity over SOP', then the port will send Discover SVIDs over SOP. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240108191620.987785-22-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 388 ++++++++++++++++++++++++++++++++++++------ include/linux/usb/pd_vdo.h | 8 +- 2 files changed, 347 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index a59927925714..a870fbc6bc35 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -328,6 +328,12 @@ struct tcpm_port { struct typec_partner_desc partner_desc; struct typec_partner *partner; + struct usb_pd_identity cable_ident; + struct typec_cable_desc cable_desc; + struct typec_cable *cable; + struct typec_plug_desc plug_prime_desc; + struct typec_plug *plug_prime; + enum typec_cc_status cc_req; enum typec_cc_status src_rp; /* work only if pd_supported == false */ @@ -508,6 +514,12 @@ struct tcpm_port { bool potential_contaminant; /* SOP* Related Fields */ + /* + * Flag to determine if SOP' Discover Identity is available. The flag + * is set if Discover Identity on SOP' does not immediately follow + * Discover Identity on SOP. + */ + bool send_discover_prime; /* * tx_sop_type determines which SOP* a message is being sent on. * For messages that are queued and not sent immediately such as in @@ -1508,7 +1520,7 @@ static int tcpm_ams_start(struct tcpm_port *port, enum tcpm_ams ams) * VDM/VDO handling functions */ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, - const u32 *data, int cnt) + const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type) { u32 vdo_hdr = port->vdo_data[0]; @@ -1516,7 +1528,10 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, /* If is sending discover_identity, handle received message first */ if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) { - port->send_discover = true; + if (tx_sop_type == TCPC_TX_SOP_PRIME) + port->send_discover_prime = true; + else + port->send_discover = true; mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); } else { /* Make sure we are not still processing a previous VDM packet */ @@ -1531,6 +1546,8 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, port->vdm_state = VDM_STATE_READY; port->vdm_sm_running = true; + port->tx_sop_type = tx_sop_type; + mod_vdm_delayed_work(port, 0); } @@ -1538,7 +1555,7 @@ static void tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header, const u32 *data, int cnt) { mutex_lock(&port->lock); - tcpm_queue_vdm(port, header, data, cnt); + tcpm_queue_vdm(port, header, data, cnt, TCPC_TX_SOP); mutex_unlock(&port->lock); } @@ -1560,6 +1577,63 @@ static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt) PD_PRODUCT_PID(product), product & 0xffff); } +static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p, int cnt) +{ + u32 idh = p[VDO_INDEX_IDH]; + u32 product = p[VDO_INDEX_PRODUCT]; + int svdm_version; + + /* + * Attempt to consume identity only if cable currently is not set + */ + if (!IS_ERR_OR_NULL(port->cable)) + goto register_plug; + + /* Reset cable identity */ + memset(&port->cable_ident, 0, sizeof(port->cable_ident)); + + /* Fill out id header, cert, product, cable VDO 1 */ + port->cable_ident.id_header = idh; + port->cable_ident.cert_stat = p[VDO_INDEX_CSTAT]; + port->cable_ident.product = product; + port->cable_ident.vdo[0] = p[VDO_INDEX_CABLE_1]; + + /* Fill out cable desc, infer svdm_version from pd revision */ + port->cable_desc.type = (enum typec_plug_type) (VDO_TYPEC_CABLE_TYPE(p[VDO_INDEX_CABLE_1]) + + USB_PLUG_TYPE_A); + port->cable_desc.active = PD_IDH_PTYPE(idh) == IDH_PTYPE_ACABLE ? 1 : 0; + /* Log PD Revision and additional cable VDO from negotiated revision */ + switch (port->negotiated_rev_prime) { + case PD_REV30: + port->cable_desc.pd_revision = 0x0300; + if (port->cable_desc.active) + port->cable_ident.vdo[1] = p[VDO_INDEX_CABLE_2]; + break; + case PD_REV20: + port->cable_desc.pd_revision = 0x0200; + break; + default: + port->cable_desc.pd_revision = 0x0200; + break; + } + port->cable_desc.identity = &port->cable_ident; + /* Register Cable, set identity and svdm_version */ + port->cable = typec_register_cable(port->typec_port, &port->cable_desc); + if (IS_ERR_OR_NULL(port->cable)) + return; + typec_cable_set_identity(port->cable); + /* Get SVDM version */ + svdm_version = PD_VDO_SVDM_VER(p[VDO_INDEX_HDR]); + typec_cable_set_svdm_version(port->cable, svdm_version); + +register_plug: + if (IS_ERR_OR_NULL(port->plug_prime)) { + port->plug_prime_desc.index = TYPEC_PLUG_SOP_P; + port->plug_prime = typec_register_plug(port->cable, + &port->plug_prime_desc); + } +} + static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt) { struct pd_mode_data *pmdata = &port->mode_data; @@ -1654,6 +1728,7 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port) } #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header) +#define supports_host(port) PD_IDH_HOST_SUPP((port->partner_ident.id_header)) /* * Helper to determine whether the port is capable of SOP' communication at the @@ -1706,9 +1781,35 @@ static bool tcpm_can_communicate_sop_prime(struct tcpm_port *port) return false; } +static bool tcpm_attempt_vconn_swap_discovery(struct tcpm_port *port) +{ + if (!port->tcpc->attempt_vconn_swap_discovery) + return false; + + /* Port is already source, no need to perform swap */ + if (port->vconn_role == TYPEC_SOURCE) + return false; + + /* + * Partner needs to support Alternate Modes with modal support. If + * partner is also capable of being a USB Host, it could be a device + * that supports Alternate Modes as the DFP. + */ + if (!supports_modal(port) || supports_host(port)) + return false; + + if ((port->negotiated_rev == PD_REV20 && port->data_role == TYPEC_HOST) || + port->negotiated_rev == PD_REV30) + return port->tcpc->attempt_vconn_swap_discovery(port->tcpc); + + return false; +} + static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, const u32 *p, int cnt, u32 *response, - enum adev_actions *adev_action) + enum adev_actions *adev_action, + enum tcpm_transmit_type rx_sop_type, + enum tcpm_transmit_type *response_tx_sop_type) { struct typec_port *typec = port->typec_port; struct typec_altmode *pdev; @@ -1718,6 +1819,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, int cmd_type; int cmd; int i; + int ret; cmd_type = PD_VDO_CMDT(p[0]); cmd = PD_VDO_CMD(p[0]); @@ -1730,9 +1832,25 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, pdev = typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX, PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0])); - svdm_version = typec_get_negotiated_svdm_version(typec); - if (svdm_version < 0) - return 0; + switch (rx_sop_type) { + case TCPC_TX_SOP_PRIME: + if (!IS_ERR_OR_NULL(port->cable)) { + svdm_version = typec_get_cable_svdm_version(typec); + if (PD_VDO_SVDM_VER(p[0]) < svdm_version) + typec_cable_set_svdm_version(port->cable, svdm_version); + } + break; + case TCPC_TX_SOP: + svdm_version = typec_get_negotiated_svdm_version(typec); + if (svdm_version < 0) + return 0; + break; + default: + svdm_version = typec_get_negotiated_svdm_version(typec); + if (svdm_version < 0) + return 0; + break; + } switch (cmd_type) { case CMDT_INIT: @@ -1802,22 +1920,89 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, (VDO_SVDM_VERS(typec_get_negotiated_svdm_version(typec))); break; case CMDT_RSP_ACK: - /* silently drop message if we are not connected */ - if (IS_ERR_OR_NULL(port->partner)) + /* + * Silently drop message if we are not connected, but can process + * if SOP' Discover Identity prior to explicit contract. + */ + if (IS_ERR_OR_NULL(port->partner) && + !(rx_sop_type == TCPC_TX_SOP_PRIME && cmd == CMD_DISCOVER_IDENT)) break; tcpm_ams_finish(port); switch (cmd) { + /* + * SVDM Command Flow for SOP and SOP': + * SOP Discover Identity + * SOP' Discover Identity + * SOP Discover SVIDs + * Discover Modes + * + * Perform Discover SOP' if the port can communicate with cable + * plug. + */ case CMD_DISCOVER_IDENT: - if (PD_VDO_SVDM_VER(p[0]) < svdm_version) - typec_partner_set_svdm_version(port->partner, - PD_VDO_SVDM_VER(p[0])); - /* 6.4.4.3.1 */ - svdm_consume_identity(port, p, cnt); - response[0] = VDO(USB_SID_PD, 1, typec_get_negotiated_svdm_version(typec), - CMD_DISCOVER_SVID); - rlen = 1; + switch (rx_sop_type) { + case TCPC_TX_SOP: + if (PD_VDO_SVDM_VER(p[0]) < svdm_version) { + typec_partner_set_svdm_version(port->partner, + PD_VDO_SVDM_VER(p[0])); + /* If cable is discovered before partner, downgrade svdm */ + if (!IS_ERR_OR_NULL(port->cable) && + (typec_get_cable_svdm_version(port->typec_port) > + svdm_version)) + typec_cable_set_svdm_version(port->cable, + svdm_version); + } + /* 6.4.4.3.1 */ + svdm_consume_identity(port, p, cnt); + /* Attempt Vconn swap, delay SOP' discovery if necessary */ + if (tcpm_attempt_vconn_swap_discovery(port)) { + port->send_discover_prime = true; + port->upcoming_state = VCONN_SWAP_SEND; + ret = tcpm_ams_start(port, VCONN_SWAP); + if (!ret) + return 0; + port->upcoming_state = INVALID_STATE; + port->send_discover_prime = false; + } + + /* + * Attempt Discover Identity on SOP' if the + * cable was not discovered previously, and use + * the SVDM version of the partner to probe. + */ + if (IS_ERR_OR_NULL(port->cable) && + tcpm_can_communicate_sop_prime(port)) { + *response_tx_sop_type = TCPC_TX_SOP_PRIME; + port->send_discover_prime = true; + response[0] = VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_IDENT); + rlen = 1; + } else { + *response_tx_sop_type = TCPC_TX_SOP; + response[0] = VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_SVID); + rlen = 1; + } + break; + case TCPC_TX_SOP_PRIME: + /* + * svdm_consume_identity_sop_prime will determine + * the svdm_version for the cable moving forward. + */ + svdm_consume_identity_sop_prime(port, p, cnt); + *response_tx_sop_type = TCPC_TX_SOP; + response[0] = VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_SVID); + rlen = 1; + break; + default: + return 0; + } break; case CMD_DISCOVER_SVID: /* 6.4.4.3.2 */ @@ -1903,13 +2088,15 @@ static void tcpm_pd_handle_msg(struct tcpm_port *port, enum tcpm_ams ams); static void tcpm_handle_vdm_request(struct tcpm_port *port, - const __le32 *payload, int cnt) + const __le32 *payload, int cnt, + enum tcpm_transmit_type rx_sop_type) { enum adev_actions adev_action = ADEV_NONE; struct typec_altmode *adev; u32 p[PD_MAX_PAYLOAD]; u32 response[8] = { }; int i, rlen = 0; + enum tcpm_transmit_type response_tx_sop_type = TCPC_TX_SOP; for (i = 0; i < cnt; i++) p[i] = le32_to_cpu(payload[i]); @@ -1944,7 +2131,8 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, * - We will send NAK and the flag will be cleared in the state machine. */ port->vdm_sm_running = true; - rlen = tcpm_pd_svdm(port, adev, p, cnt, response, &adev_action); + rlen = tcpm_pd_svdm(port, adev, p, cnt, response, &adev_action, + rx_sop_type, &response_tx_sop_type); } else { if (port->negotiated_rev >= PD_REV30) tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); @@ -2012,19 +2200,38 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, mutex_lock(&port->lock); if (rlen > 0) - tcpm_queue_vdm(port, response[0], &response[1], rlen - 1); + tcpm_queue_vdm(port, response[0], &response[1], rlen - 1, response_tx_sop_type); else port->vdm_sm_running = false; } static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, - const u32 *data, int count) + const u32 *data, int count, enum tcpm_transmit_type tx_sop_type) { - int svdm_version = typec_get_negotiated_svdm_version(port->typec_port); + int svdm_version; u32 header; - if (svdm_version < 0) - return; + switch (tx_sop_type) { + case TCPC_TX_SOP_PRIME: + /* + * If the port partner is discovered, then the port partner's + * SVDM Version will be returned + */ + svdm_version = typec_get_cable_svdm_version(port->typec_port); + if (svdm_version < 0) + svdm_version = SVDM_VER_MAX; + break; + case TCPC_TX_SOP: + svdm_version = typec_get_negotiated_svdm_version(port->typec_port); + if (svdm_version < 0) + return; + break; + default: + svdm_version = typec_get_negotiated_svdm_version(port->typec_port); + if (svdm_version < 0) + return; + break; + } if (WARN_ON(count > VDO_MAX_SIZE - 1)) count = VDO_MAX_SIZE - 1; @@ -2033,7 +2240,7 @@ static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), svdm_version, cmd); - tcpm_queue_vdm(port, header, data, count); + tcpm_queue_vdm(port, header, data, count, tx_sop_type); } static unsigned int vdm_ready_timeout(u32 vdm_hdr) @@ -2067,6 +2274,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) struct pd_message msg; int i, res = 0; u32 vdo_hdr = port->vdo_data[0]; + u32 response[8] = { }; switch (port->vdm_state) { case VDM_STATE_READY: @@ -2091,7 +2299,17 @@ static void vdm_run_state_machine(struct tcpm_port *port) case CMD_DISCOVER_IDENT: res = tcpm_ams_start(port, DISCOVER_IDENTITY); if (res == 0) { - port->send_discover = false; + switch (port->tx_sop_type) { + case TCPC_TX_SOP_PRIME: + port->send_discover_prime = false; + break; + case TCPC_TX_SOP: + port->send_discover = false; + break; + default: + port->send_discover = false; + break; + } } else if (res == -EAGAIN) { port->vdo_data[0] = 0; mod_send_discover_delayed_work(port, @@ -2160,19 +2378,49 @@ static void vdm_run_state_machine(struct tcpm_port *port) tcpm_ams_finish(port); } else { tcpm_ams_finish(port); + if (port->tx_sop_type == TCPC_TX_SOP) + break; + /* Handle SOP' Transmission Errors */ + switch (PD_VDO_CMD(vdo_hdr)) { + /* + * If Discover Identity fails on SOP', then resume + * discovery process on SOP only. + */ + case CMD_DISCOVER_IDENT: + port->vdo_data[0] = 0; + response[0] = VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version( + port->typec_port), + CMD_DISCOVER_SVID); + tcpm_queue_vdm(port, response[0], &response[1], + 0, TCPC_TX_SOP); + break; + default: + break; + } } break; case VDM_STATE_SEND_MESSAGE: /* Prepare and send VDM */ memset(&msg, 0, sizeof(msg)); - msg.header = PD_HEADER_LE(PD_DATA_VENDOR_DEF, - port->pwr_role, - port->data_role, - port->negotiated_rev, - port->message_id, port->vdo_count); + if (port->tx_sop_type == TCPC_TX_SOP_PRIME) { + msg.header = PD_HEADER_LE(PD_DATA_VENDOR_DEF, + 0, /* Cable Plug Indicator for DFP/UFP */ + 0, /* Reserved */ + port->negotiated_rev_prime, + port->message_id_prime, + port->vdo_count); + } else { + msg.header = PD_HEADER_LE(PD_DATA_VENDOR_DEF, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + port->vdo_count); + } for (i = 0; i < port->vdo_count; i++) msg.payload[i] = cpu_to_le32(port->vdo_data[i]); - res = tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); + res = tcpm_pd_transmit(port, port->tx_sop_type, &msg); if (res < 0) { port->vdm_state = VDM_STATE_ERR_SEND; } else { @@ -2559,7 +2807,8 @@ static int tcpm_register_sink_caps(struct tcpm_port *port) } static void tcpm_pd_data_request(struct tcpm_port *port, - const struct pd_message *msg) + const struct pd_message *msg, + enum tcpm_transmit_type rx_sop_type) { enum pd_data_msg_type type = pd_header_type_le(msg->header); unsigned int cnt = pd_header_cnt_le(msg->header); @@ -2600,8 +2849,11 @@ static void tcpm_pd_data_request(struct tcpm_port *port, break; } - if (rev < PD_MAX_REV) + if (rev < PD_MAX_REV) { port->negotiated_rev = rev; + if (port->negotiated_rev_prime > port->negotiated_rev) + port->negotiated_rev_prime = port->negotiated_rev; + } if (port->pwr_role == TYPEC_SOURCE) { if (port->ams == GET_SOURCE_CAPABILITIES) @@ -2652,8 +2904,11 @@ static void tcpm_pd_data_request(struct tcpm_port *port, break; } - if (rev < PD_MAX_REV) + if (rev < PD_MAX_REV) { port->negotiated_rev = rev; + if (port->negotiated_rev_prime > port->negotiated_rev) + port->negotiated_rev_prime = port->negotiated_rev; + } if (port->pwr_role != TYPEC_SOURCE || cnt != 1) { tcpm_pd_handle_msg(port, @@ -2709,7 +2964,7 @@ static void tcpm_pd_data_request(struct tcpm_port *port, NONE_AMS); break; case PD_DATA_VENDOR_DEF: - tcpm_handle_vdm_request(port, msg->payload, cnt); + tcpm_handle_vdm_request(port, msg->payload, cnt, rx_sop_type); break; case PD_DATA_BIST: port->bist_request = le32_to_cpu(msg->payload[0]); @@ -3152,7 +3407,7 @@ static void tcpm_pd_rx_handler(struct kthread_work *work) if (le16_to_cpu(msg->header) & PD_HEADER_EXT_HDR) tcpm_pd_ext_msg_request(port, msg); else if (cnt) - tcpm_pd_data_request(port, msg); + tcpm_pd_data_request(port, msg, rx_sop_type); else tcpm_pd_ctrl_request(port, msg, rx_sop_type); } @@ -3809,6 +4064,7 @@ static int tcpm_src_attach(struct tcpm_port *port) port->attached = true; port->send_discover = true; + port->send_discover_prime = false; return 0; @@ -3825,6 +4081,15 @@ out_disable_mux: static void tcpm_typec_disconnect(struct tcpm_port *port) { + /* + * Unregister plug/cable outside of port->connected because cable can + * be discovered before SRC_READY/SNK_READY states where port->connected + * is set. + */ + typec_unregister_plug(port->plug_prime); + typec_unregister_cable(port->cable); + port->plug_prime = NULL; + port->cable = NULL; if (port->connected) { typec_partner_set_usb_power_delivery(port->partner, NULL); typec_unregister_partner(port->partner); @@ -3947,6 +4212,7 @@ static int tcpm_snk_attach(struct tcpm_port *port) port->attached = true; port->send_discover = true; + port->send_discover_prime = false; return 0; } @@ -4308,14 +4574,23 @@ static void run_state_machine(struct tcpm_port *port) * 6.4.4.3.1 Discover Identity * "The Discover Identity Command Shall only be sent to SOP when there is an * Explicit Contract." - * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using - * port->explicit_contract to decide whether to send the command. + * + * Discover Identity on SOP' should be discovered prior to the + * ready state, but if done after a Vconn Swap following Discover + * Identity on SOP then the discovery process can be run here + * as well. */ if (port->explicit_contract) { - tcpm_set_initial_svdm_version(port); + if (port->send_discover_prime) { + port->tx_sop_type = TCPC_TX_SOP_PRIME; + } else { + port->tx_sop_type = TCPC_TX_SOP; + tcpm_set_initial_svdm_version(port); + } mod_send_discover_delayed_work(port, 0); } else { port->send_discover = false; + port->send_discover_prime = false; } /* @@ -4607,14 +4882,23 @@ static void run_state_machine(struct tcpm_port *port) * 6.4.4.3.1 Discover Identity * "The Discover Identity Command Shall only be sent to SOP when there is an * Explicit Contract." - * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using - * port->explicit_contract. + * + * Discover Identity on SOP' should be discovered prior to the + * ready state, but if done after a Vconn Swap following Discover + * Identity on SOP then the discovery process can be run here + * as well. */ if (port->explicit_contract) { - tcpm_set_initial_svdm_version(port); + if (port->send_discover_prime) { + port->tx_sop_type = TCPC_TX_SOP_PRIME; + } else { + port->tx_sop_type = TCPC_TX_SOP; + tcpm_set_initial_svdm_version(port); + } mod_send_discover_delayed_work(port, 0); } else { port->send_discover = false; + port->send_discover_prime = false; } power_supply_changed(port->psy); @@ -4655,6 +4939,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_unregister_altmodes(port); port->nr_sink_caps = 0; port->send_discover = true; + port->send_discover_prime = false; if (port->pwr_role == TYPEC_SOURCE) tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF, PD_T_PS_HARD_RESET); @@ -4801,20 +5086,25 @@ static void run_state_machine(struct tcpm_port *port) /* DR_Swap states */ case DR_SWAP_SEND: tcpm_pd_send_control(port, PD_CTRL_DR_SWAP, TCPC_TX_SOP); - if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) + if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) { port->send_discover = true; + port->send_discover_prime = false; + } tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; case DR_SWAP_ACCEPT: tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); - if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) + if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) { port->send_discover = true; + port->send_discover_prime = false; + } tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); break; case DR_SWAP_SEND_TIMEOUT: tcpm_swap_complete(port, -ETIMEDOUT); port->send_discover = false; + port->send_discover_prime = false; tcpm_ams_finish(port); tcpm_set_state(port, ready_state(port), 0); break; @@ -5796,7 +6086,8 @@ static void tcpm_enable_frs_work(struct kthread_work *work) goto unlock; /* Send when the state machine is idle */ - if (port->state != SNK_READY || port->vdm_sm_running || port->send_discover) + if (port->state != SNK_READY || port->vdm_sm_running || port->send_discover || + port->send_discover_prime) goto resched; port->upcoming_state = GET_SINK_CAP; @@ -5819,11 +6110,12 @@ static void tcpm_send_discover_work(struct kthread_work *work) mutex_lock(&port->lock); /* No need to send DISCOVER_IDENTITY anymore */ - if (!port->send_discover) + if (!port->send_discover && !port->send_discover_prime) goto unlock; if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) { port->send_discover = false; + port->send_discover_prime = false; goto unlock; } @@ -5833,7 +6125,7 @@ static void tcpm_send_discover_work(struct kthread_work *work) goto unlock; } - tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); + tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0, port->tx_sop_type); unlock: mutex_unlock(&port->lock); diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h index 3a747938cdab..c09c5a12e273 100644 --- a/include/linux/usb/pd_vdo.h +++ b/include/linux/usb/pd_vdo.h @@ -86,12 +86,15 @@ * * Request is simply properly formatted SVDM header * - * Response is 4 data objects: + * Response is 4 data objects for Power Delivery 2.0 and Passive Cables for + * Power Delivery 3.0. Active Cables in Power Delivery 3.0 have 5 data objects. * [0] :: SVDM header * [1] :: Identitiy header * [2] :: Cert Stat VDO * [3] :: (Product | Cable) VDO + * [4] :: Cable VDO 1 * [4] :: AMA VDO + * [5] :: Cable VDO 2 * */ #define VDO_INDEX_HDR 0 @@ -100,6 +103,8 @@ #define VDO_INDEX_CABLE 3 #define VDO_INDEX_PRODUCT 3 #define VDO_INDEX_AMA 4 +#define VDO_INDEX_CABLE_1 4 +#define VDO_INDEX_CABLE_2 5 /* * SVDM Identity Header @@ -150,6 +155,7 @@ #define PD_IDH_MODAL_SUPP(vdo) ((vdo) & (1 << 26)) #define PD_IDH_DFP_PTYPE(vdo) (((vdo) >> 23) & 0x7) #define PD_IDH_CONN_TYPE(vdo) (((vdo) >> 21) & 0x3) +#define PD_IDH_HOST_SUPP(vdo) ((vdo) & (1 << 31)) /* * Cert Stat VDO -- cgit v1.2.3 From 174657478cd8425288aeabf93b964b9387e096fa Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:15 +0100 Subject: vgacon: inline vc_scrolldelta_helper() into vgacon_scrolldelta() Since commit 74d58cd48a8f ("USB: sisusbvga: remove console support"), vgacon_scrolldelta() is the only user of vc_scrolldelta_helper(). Inline the helper into vgacon_scrolldelta() and drop it. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-2-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 40 ---------------------------------------- drivers/video/console/vgacon.c | 36 ++++++++++++++++++++++++++++++++++-- include/linux/vt_kern.h | 3 --- 3 files changed, 34 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 156efda7c80d..3f3f7c216819 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4748,43 +4748,3 @@ void vcs_scr_updated(struct vc_data *vc) { notify_update(vc); } - -void vc_scrolldelta_helper(struct vc_data *c, int lines, - unsigned int rolled_over, void *base, unsigned int size) -{ - unsigned long ubase = (unsigned long)base; - ptrdiff_t scr_end = (void *)c->vc_scr_end - base; - ptrdiff_t vorigin = (void *)c->vc_visible_origin - base; - ptrdiff_t origin = (void *)c->vc_origin - base; - int margin = c->vc_size_row * 4; - int from, wrap, from_off, avail; - - /* Turn scrollback off */ - if (!lines) { - c->vc_visible_origin = c->vc_origin; - return; - } - - /* Do we have already enough to allow jumping from 0 to the end? */ - if (rolled_over > scr_end + margin) { - from = scr_end; - wrap = rolled_over + c->vc_size_row; - } else { - from = 0; - wrap = size; - } - - from_off = (vorigin - from + wrap) % wrap + lines * c->vc_size_row; - avail = (origin - from + wrap) % wrap; - - /* Only a little piece would be left? Show all incl. the piece! */ - if (avail < 2 * margin) - margin = 0; - if (from_off < margin) - from_off = 0; - if (from_off > avail - margin) - from_off = avail; - - c->vc_visible_origin = ubase + (from + from_off) % wrap; -} -EXPORT_SYMBOL_GPL(vc_scrolldelta_helper); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 8ef1579fa57f..9176fff9ce6e 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -138,8 +138,40 @@ static inline void vga_set_mem_top(struct vc_data *c) static void vgacon_scrolldelta(struct vc_data *c, int lines) { - vc_scrolldelta_helper(c, lines, vga_rolled_over, (void *)vga_vram_base, - vga_vram_size); + unsigned long scr_end = c->vc_scr_end - vga_vram_base; + unsigned long vorigin = c->vc_visible_origin - vga_vram_base; + unsigned long origin = c->vc_origin - vga_vram_base; + int margin = c->vc_size_row * 4; + int from, wrap, from_off, avail; + + /* Turn scrollback off */ + if (!lines) { + c->vc_visible_origin = c->vc_origin; + return; + } + + /* Do we have already enough to allow jumping from 0 to the end? */ + if (vga_rolled_over > scr_end + margin) { + from = scr_end; + wrap = vga_rolled_over + c->vc_size_row; + } else { + from = 0; + wrap = vga_vram_size; + } + + from_off = (vorigin - from + wrap) % wrap + lines * c->vc_size_row; + avail = (origin - from + wrap) % wrap; + + /* Only a little piece would be left? Show all incl. the piece! */ + if (avail < 2 * margin) + margin = 0; + if (from_off < margin) + from_off = 0; + if (from_off > avail - margin) + from_off = avail; + + c->vc_visible_origin = vga_vram_base + (from + from_off) % wrap; + vga_set_mem_top(c); } diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index c1f5aebef170..a789ea3ed2a0 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -168,7 +168,4 @@ void vt_set_led_state(unsigned int console, int leds); void vt_kbd_con_start(unsigned int console); void vt_kbd_con_stop(unsigned int console); -void vc_scrolldelta_helper(struct vc_data *c, int lines, - unsigned int rolled_over, void *_base, unsigned int size); - #endif /* _VT_KERN_H */ -- cgit v1.2.3 From a0b8a1681254346010edd2f94e799fb6b6568cf1 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:19 +0100 Subject: tty: vt: pass proper pointers from tioclinux() Pass proper types and proper pointers (the data with an offset) to the TIOCL_* handlers. So that they need not to cast or add anything to the passed pointer. This makes obvious what is passed/consumed. Signed-off-by: "Jiri Slaby (SUSE)" Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-6-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/selection.c | 8 ++++---- drivers/tty/vt/vt.c | 19 ++++++++++--------- include/linux/selection.h | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 8967c3a0d916..e172ede235a0 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -7,7 +7,7 @@ * 'int set_selection_kernel(struct tiocl_selection *, struct tty_struct *)' * 'void clear_selection(void)' * 'int paste_selection(struct tty_struct *)' - * 'int sel_loadlut(char __user *)' + * 'int sel_loadlut(u32 __user *)' * * Now that /dev/vcs exists, most of this can disappear again. */ @@ -111,15 +111,15 @@ static inline int inword(const u32 c) /** * sel_loadlut() - load the LUT table - * @p: user table + * @lut: user table * * Load the LUT table from user space. The caller must hold the console * lock. Make a temporary copy so a partial update doesn't make a mess. */ -int sel_loadlut(char __user *p) +int sel_loadlut(u32 __user *lut) { u32 tmplut[ARRAY_SIZE(inwordLut)]; - if (copy_from_user(tmplut, (u32 __user *)(p+4), sizeof(inwordLut))) + if (copy_from_user(tmplut, lut, sizeof(inwordLut))) return -EFAULT; memcpy(inwordLut, tmplut, sizeof(inwordLut)); return 0; diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index e131edea00da..079dbff562fd 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -145,7 +145,7 @@ static void gotoxy(struct vc_data *vc, int new_x, int new_y); static void save_cur(struct vc_data *vc); static void reset_terminal(struct vc_data *vc, int do_clear); static void con_flush_chars(struct tty_struct *tty); -static int set_vesa_blanking(char __user *p); +static int set_vesa_blanking(u8 __user *mode); static void set_cursor(struct vc_data *vc); static void hide_cursor(struct vc_data *vc); static void console_callback(struct work_struct *ignored); @@ -3134,6 +3134,8 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) { char type, data; char __user *p = (char __user *)arg; + void __user *param_aligned32 = (u32 __user *)arg + 1; + void __user *param = (void __user *)arg + 1; int lines; int ret; @@ -3147,8 +3149,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) case TIOCL_SETSEL: if (!capable(CAP_SYS_ADMIN)) return -EPERM; - return set_selection_user((struct tiocl_selection - __user *)(p+1), tty); + return set_selection_user(param, tty); case TIOCL_PASTESEL: if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -3162,7 +3163,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; console_lock(); - ret = sel_loadlut(p); + ret = sel_loadlut(param_aligned32); console_unlock(); break; case TIOCL_GETSHIFTSTATE: @@ -3181,7 +3182,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) return put_user(data, p); case TIOCL_SETVESABLANK: console_lock(); - ret = set_vesa_blanking(p); + ret = set_vesa_blanking(param); console_unlock(); break; case TIOCL_GETKMSGREDIRECT: @@ -3204,7 +3205,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) */ return fg_console; case TIOCL_SCROLLCONSOLE: - if (get_user(lines, (s32 __user *)(p+4))) + if (get_user(lines, (s32 __user *)param_aligned32)) return -EFAULT; /* @@ -4262,11 +4263,11 @@ postcore_initcall(vtconsole_class_init); * Screen blanking */ -static int set_vesa_blanking(char __user *p) +static int set_vesa_blanking(u8 __user *mode_user) { - unsigned int mode; + u8 mode; - if (get_user(mode, p + 1)) + if (get_user(mode, mode_user)) return -EFAULT; vesa_blank_mode = (mode < 4) ? mode : 0; diff --git a/include/linux/selection.h b/include/linux/selection.h index 170ef28ff26b..b7cd23e56a2b 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -20,7 +20,7 @@ extern int set_selection_user(const struct tiocl_selection __user *sel, extern int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty); extern int paste_selection(struct tty_struct *tty); -extern int sel_loadlut(char __user *p); +extern int sel_loadlut(u32 __user *lut); extern int mouse_reporting(void); extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry); -- cgit v1.2.3 From beccdcfa15666c442ce79a5f963fcb34ec28084e Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:21 +0100 Subject: tty: vt: pass vc_resize_user as a parameter It is pretty unfortunate to set vc_data::vc_resize_user in two callers of vc_do_resize(). vc_resize_user is immediately reset there (while remembering it). So instead of this back and forth, pass 'from_user' as a parameter. Notes on 'int user': * The name changes from 'user' to 'from_user' on some places to be consistent. * The type is bool now as 'int user' might evoke user's uid or whatever. Provided vc_resize() is called on many places and they need not to care about this parameter, its prototype is kept unchanged. Instead, it is now an inline calling a new __vc_resize() which implements the above. This patch makes the situation much more obvious. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: Daniel Vetter Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-8-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 28 +++++++++++++--------------- drivers/tty/vt/vt_ioctl.c | 6 ++---- drivers/video/console/vgacon.c | 4 ++-- drivers/video/fbdev/core/fbcon.c | 2 +- include/linux/console.h | 2 +- include/linux/console_struct.h | 1 - include/linux/vt_kern.h | 9 ++++++++- 7 files changed, 27 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 3a6f60ad2224..c87837306074 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1115,13 +1115,13 @@ err_free: } static inline int resize_screen(struct vc_data *vc, int width, int height, - int user) + bool from_user) { /* Resizes the resolution of the display adapater */ int err = 0; if (vc->vc_sw->con_resize) - err = vc->vc_sw->con_resize(vc, width, height, user); + err = vc->vc_sw->con_resize(vc, width, height, from_user); return err; } @@ -1132,6 +1132,7 @@ static inline int resize_screen(struct vc_data *vc, int width, int height, * @vc: virtual console private data * @cols: columns * @lines: lines + * @from_user: invoked by a user? * * Resize a virtual console, clipping according to the actual constraints. * If the caller passes a tty structure then update the termios winsize @@ -1142,21 +1143,17 @@ static inline int resize_screen(struct vc_data *vc, int width, int height, */ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, - unsigned int cols, unsigned int lines) + unsigned int cols, unsigned int lines, bool from_user) { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsigned long end; unsigned int old_rows, old_row_size, first_copied_row; unsigned int new_cols, new_rows, new_row_size, new_screen_size; - unsigned int user; unsigned short *oldscreen, *newscreen; u32 **new_uniscr = NULL; WARN_CONSOLE_UNLOCKED(); - user = vc->vc_resize_user; - vc->vc_resize_user = 0; - if (cols > VC_MAXCOL || lines > VC_MAXROW) return -EINVAL; @@ -1182,7 +1179,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, * to deal with possible errors from the code below, we call * the resize_screen here as well. */ - return resize_screen(vc, new_cols, new_rows, user); + return resize_screen(vc, new_cols, new_rows, from_user); } if (new_screen_size > KMALLOC_MAX_SIZE || !new_screen_size) @@ -1205,7 +1202,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, old_rows = vc->vc_rows; old_row_size = vc->vc_size_row; - err = resize_screen(vc, new_cols, new_rows, user); + err = resize_screen(vc, new_cols, new_rows, from_user); if (err) { kfree(newscreen); vc_uniscr_free(new_uniscr); @@ -1292,22 +1289,23 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, } /** - * vc_resize - resize a VT + * __vc_resize - resize a VT * @vc: virtual console * @cols: columns * @rows: rows + * @from_user: invoked by a user? * * Resize a virtual console as seen from the console end of things. We * use the common vc_do_resize methods to update the structures. The * caller must hold the console sem to protect console internals and * vc->port.tty */ - -int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) +int __vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows, + bool from_user) { - return vc_do_resize(vc->port.tty, vc, cols, rows); + return vc_do_resize(vc->port.tty, vc, cols, rows, from_user); } -EXPORT_SYMBOL(vc_resize); +EXPORT_SYMBOL(__vc_resize); /** * vt_resize - resize a VT @@ -1327,7 +1325,7 @@ static int vt_resize(struct tty_struct *tty, struct winsize *ws) int ret; console_lock(); - ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row); + ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row, false); console_unlock(); return ret; } diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 8c685b501404..4b91072f3a4e 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -714,8 +714,7 @@ static int vt_resizex(struct vc_data *vc, struct vt_consize __user *cs) vcp->vc_scan_lines = v.v_vlin; if (v.v_clin) vcp->vc_cell_height = v.v_clin; - vcp->vc_resize_user = 1; - ret = vc_resize(vcp, v.v_cols, v.v_rows); + ret = __vc_resize(vcp, v.v_cols, v.v_rows, true); if (ret) { vcp->vc_scan_lines = save_scan_lines; vcp->vc_cell_height = save_cell_height; @@ -923,9 +922,8 @@ int vt_ioctl(struct tty_struct *tty, vc = vc_cons[i].d; if (vc) { - vc->vc_resize_user = 1; /* FIXME: review v tty lock */ - vc_resize(vc_cons[i].d, cc, ll); + __vc_resize(vc_cons[i].d, cc, ll, true); } } console_unlock(); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 9176fff9ce6e..0c76e2817b49 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1081,12 +1081,12 @@ static int vgacon_font_get(struct vc_data *c, struct console_font *font, unsigne } static int vgacon_resize(struct vc_data *c, unsigned int width, - unsigned int height, unsigned int user) + unsigned int height, bool from_user) { if ((width << 1) * height > vga_vram_size) return -EINVAL; - if (user) { + if (from_user) { /* * Ho ho! Someone (svgatextmode, eh?) may have reprogrammed * the video mode! Set the new defaults then and go away. diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index a8c32cb4c878..dd2f4617485c 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1996,7 +1996,7 @@ static void updatescrollmode(struct fbcon_display *p, #define CALC_FONTSZ(h, p, c) ((h) * (p) * (c)) /* size = height * pitch * charcount */ static int fbcon_resize(struct vc_data *vc, unsigned int width, - unsigned int height, unsigned int user) + unsigned int height, bool from_user) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_ops *ops = info->fbcon_par; diff --git a/include/linux/console.h b/include/linux/console.h index 779d388af8a0..38b379d6c624 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -66,7 +66,7 @@ struct consw { int (*con_font_default)(struct vc_data *vc, struct console_font *font, char *name); int (*con_resize)(struct vc_data *vc, unsigned int width, - unsigned int height, unsigned int user); + unsigned int height, bool from_user); void (*con_set_palette)(struct vc_data *vc, const unsigned char *table); void (*con_scrolldelta)(struct vc_data *vc, int lines); diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index 539f1cd45309..20f564e98552 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -151,7 +151,6 @@ struct vc_data { DECLARE_BITMAP(vc_tab_stop, VC_TABSTOPS_COUNT); /* Tab stops. 256 columns. */ unsigned char vc_palette[16*3]; /* Colour palette for VGA+ */ unsigned short * vc_translate; - unsigned int vc_resize_user; /* resize request from user */ unsigned int vc_bell_pitch; /* Console bell pitch */ unsigned int vc_bell_duration; /* Console bell duration */ unsigned short vc_cur_blink_ms; /* Cursor blink duration */ diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index a789ea3ed2a0..d008c3d0a9bb 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -25,7 +25,8 @@ extern int fg_console, last_console, want_console; int vc_allocate(unsigned int console); int vc_cons_allocated(unsigned int console); -int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines); +int __vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines, + bool from_user); struct vc_data *vc_deallocate(unsigned int console); void reset_palette(struct vc_data *vc); void do_blank_screen(int entering_gfx); @@ -42,6 +43,12 @@ void redraw_screen(struct vc_data *vc, int is_switch); #define update_screen(x) redraw_screen(x, 0) #define switch_screen(x) redraw_screen(x, 1) +static inline int vc_resize(struct vc_data *vc, unsigned int cols, + unsigned int lines) +{ + return __vc_resize(vc, cols, lines, false); +} + struct tty_struct; int tioclinux(struct tty_struct *tty, unsigned long arg); -- cgit v1.2.3 From d4c0c481e49fdf483c43e13e4a419ea19c045023 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:22 +0100 Subject: tty: vt: make vc_is_sel()'s vc const It's only an aid to people reading the header and/or calling vc_is_sel(). vc is only tested there, so having it const makes sense. Signed-off-by: "Jiri Slaby (SUSE)" Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-9-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/selection.c | 2 +- include/linux/selection.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 91d789c025c6..34ee09f5a8f4 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -88,7 +88,7 @@ void clear_selection(void) } EXPORT_SYMBOL_GPL(clear_selection); -bool vc_is_sel(struct vc_data *vc) +bool vc_is_sel(const struct vc_data *vc) { return vc == vc_sel.cons; } diff --git a/include/linux/selection.h b/include/linux/selection.h index b7cd23e56a2b..533509f6ba4f 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -24,7 +24,7 @@ extern int sel_loadlut(u32 __user *lut); extern int mouse_reporting(void); extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry); -bool vc_is_sel(struct vc_data *vc); +bool vc_is_sel(const struct vc_data *vc); extern int console_blanked; -- cgit v1.2.3 From 649f6fbe6abe0c7749120067058709d41111f655 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:32 +0100 Subject: tty: vt: remove extern from functions in selection.h Remove unneeded 'extern' keyword from function prototypes in selection.h. This makes it more readable as no more wrapping is needed on many places. Signed-off-by: "Jiri Slaby (SUSE)" Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-19-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/selection.h | 52 ++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/selection.h b/include/linux/selection.h index 533509f6ba4f..bab7d30d3446 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -14,15 +14,14 @@ struct tty_struct; struct vc_data; -extern void clear_selection(void); -extern int set_selection_user(const struct tiocl_selection __user *sel, - struct tty_struct *tty); -extern int set_selection_kernel(struct tiocl_selection *v, - struct tty_struct *tty); -extern int paste_selection(struct tty_struct *tty); -extern int sel_loadlut(u32 __user *lut); -extern int mouse_reporting(void); -extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry); +void clear_selection(void); +int set_selection_user(const struct tiocl_selection __user *sel, + struct tty_struct *tty); +int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty); +int paste_selection(struct tty_struct *tty); +int sel_loadlut(u32 __user *lut); +int mouse_reporting(void); +void mouse_report(struct tty_struct *tty, int butt, int mrx, int mry); bool vc_is_sel(const struct vc_data *vc); @@ -33,24 +32,21 @@ extern unsigned char default_red[]; extern unsigned char default_grn[]; extern unsigned char default_blu[]; -extern unsigned short *screen_pos(const struct vc_data *vc, int w_offset, - bool viewed); -extern u16 screen_glyph(const struct vc_data *vc, int offset); -extern u32 screen_glyph_unicode(const struct vc_data *vc, int offset); -extern void complement_pos(struct vc_data *vc, int offset); -extern void invert_screen(struct vc_data *vc, int offset, int count, bool viewed); - -extern void getconsxy(const struct vc_data *vc, unsigned char xy[static 2]); -extern void putconsxy(struct vc_data *vc, unsigned char xy[static const 2]); - -extern u16 vcs_scr_readw(const struct vc_data *vc, const u16 *org); -extern void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org); -extern void vcs_scr_updated(struct vc_data *vc); - -extern int vc_uniscr_check(struct vc_data *vc); -extern void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, - bool viewed, - unsigned int row, unsigned int col, - unsigned int nr); +unsigned short *screen_pos(const struct vc_data *vc, int w_offset, bool viewed); +u16 screen_glyph(const struct vc_data *vc, int offset); +u32 screen_glyph_unicode(const struct vc_data *vc, int offset); +void complement_pos(struct vc_data *vc, int offset); +void invert_screen(struct vc_data *vc, int offset, int count, bool viewed); + +void getconsxy(const struct vc_data *vc, unsigned char xy[static 2]); +void putconsxy(struct vc_data *vc, unsigned char xy[static const 2]); + +u16 vcs_scr_readw(const struct vc_data *vc, const u16 *org); +void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org); +void vcs_scr_updated(struct vc_data *vc); + +int vc_uniscr_check(struct vc_data *vc); +void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, + unsigned int row, unsigned int col, unsigned int nr); #endif -- cgit v1.2.3 From 7995c30d8d771c8410d7f2ba5b9d42b69e0074c8 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:33 +0100 Subject: tty: vt: make consw::con_debug_*() return void The return value of con_debug_enter() and con_debug_leave() is ignored on many fronts. So just don't propagate errors (the current implementations return 0 anyway) and make the return type a void. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Daniel Vetter Cc: Helge Deller Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-20-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 21 ++++----------------- drivers/video/fbdev/core/fbcon.c | 6 ++---- include/linux/console.h | 18 ++++++------------ 3 files changed, 12 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 51a2787415db..bbfda0d33ca1 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4012,15 +4012,9 @@ EXPORT_SYMBOL(con_is_visible); * Called when the console is taken over by the kernel debugger, this * function needs to save the current console state, then put the console * into a state suitable for the kernel debugger. - * - * RETURNS: - * Zero on success, nonzero if a failure occurred when trying to prepare - * the console for the debugger. */ -int con_debug_enter(struct vc_data *vc) +void con_debug_enter(struct vc_data *vc) { - int ret = 0; - saved_fg_console = fg_console; saved_last_console = last_console; saved_want_console = want_console; @@ -4029,7 +4023,7 @@ int con_debug_enter(struct vc_data *vc) vc->vc_mode = KD_TEXT; console_blanked = 0; if (vc->vc_sw->con_debug_enter) - ret = vc->vc_sw->con_debug_enter(vc); + vc->vc_sw->con_debug_enter(vc); #ifdef CONFIG_KGDB_KDB /* Set the initial LINES variable if it is not already set */ if (vc->vc_rows < 999) { @@ -4059,7 +4053,6 @@ int con_debug_enter(struct vc_data *vc) } } #endif /* CONFIG_KGDB_KDB */ - return ret; } EXPORT_SYMBOL_GPL(con_debug_enter); @@ -4068,15 +4061,10 @@ EXPORT_SYMBOL_GPL(con_debug_enter); * * Restore the console state to what it was before the kernel debugger * was invoked. - * - * RETURNS: - * Zero on success, nonzero if a failure occurred when trying to restore - * the console. */ -int con_debug_leave(void) +void con_debug_leave(void) { struct vc_data *vc; - int ret = 0; fg_console = saved_fg_console; last_console = saved_last_console; @@ -4086,8 +4074,7 @@ int con_debug_leave(void) vc = vc_cons[fg_console].d; if (vc->vc_sw->con_debug_leave) - ret = vc->vc_sw->con_debug_leave(vc); - return ret; + vc->vc_sw->con_debug_leave(vc); } EXPORT_SYMBOL_GPL(con_debug_leave); diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index dd2f4617485c..d3fb98084eda 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2243,7 +2243,7 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) return 0; } -static int fbcon_debug_enter(struct vc_data *vc) +static void fbcon_debug_enter(struct vc_data *vc) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_ops *ops = info->fbcon_par; @@ -2253,10 +2253,9 @@ static int fbcon_debug_enter(struct vc_data *vc) if (info->fbops->fb_debug_enter) info->fbops->fb_debug_enter(info); fbcon_set_palette(vc, color_table); - return 0; } -static int fbcon_debug_leave(struct vc_data *vc) +static void fbcon_debug_leave(struct vc_data *vc) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_ops *ops = info->fbcon_par; @@ -2264,7 +2263,6 @@ static int fbcon_debug_leave(struct vc_data *vc) ops->graphics = ops->save_graphics; if (info->fbops->fb_debug_leave) info->fbops->fb_debug_leave(info); - return 0; } static int fbcon_get_font(struct vc_data *vc, struct console_font *font, unsigned int vpitch) diff --git a/include/linux/console.h b/include/linux/console.h index 38b379d6c624..93a1db5bf3b5 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -88,11 +88,11 @@ struct consw { * limited to, unblanking the console, loading an appropriate * palette, and allowing debugger generated output. */ - int (*con_debug_enter)(struct vc_data *vc); + void (*con_debug_enter)(struct vc_data *vc); /* * Restore the console to its pre-debug state as closely as possible. */ - int (*con_debug_leave)(struct vc_data *vc); + void (*con_debug_leave)(struct vc_data *vc); }; extern const struct consw *conswitchp; @@ -113,17 +113,11 @@ int do_unregister_con_driver(const struct consw *csw); int do_take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); #ifdef CONFIG_HW_CONSOLE -int con_debug_enter(struct vc_data *vc); -int con_debug_leave(void); +void con_debug_enter(struct vc_data *vc); +void con_debug_leave(void); #else -static inline int con_debug_enter(struct vc_data *vc) -{ - return 0; -} -static inline int con_debug_leave(void) -{ - return 0; -} +static inline void con_debug_enter(struct vc_data *vc) { } +static inline void con_debug_leave(void) { } #endif /* cursor */ -- cgit v1.2.3 From dae3e6b6180f1a2394b984c596d39ed2c57d25fe Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:34 +0100 Subject: tty: vt: make init parameter of consw::con_init() a bool The 'init' parameter of consw::con_init() is true for the first call of the hook on a particular console. So make the parameter a bool. And document the hook. Signed-off-by: "Jiri Slaby (SUSE)" Reviewed-by: Geert Uytterhoeven Cc: Helge Deller Cc: "James E.J. Bottomley" Cc: Daniel Vetter Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-parisc@vger.kernel.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-21-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 8 ++++---- drivers/video/console/dummycon.c | 2 +- drivers/video/console/mdacon.c | 2 +- drivers/video/console/newport_con.c | 2 +- drivers/video/console/sticon.c | 2 +- drivers/video/console/vgacon.c | 4 ++-- drivers/video/fbdev/core/fbcon.c | 2 +- include/linux/console.h | 4 +++- 8 files changed, 14 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index bbfda0d33ca1..fcb41c8724f3 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -999,7 +999,7 @@ int vc_cons_allocated(unsigned int i) return (i < MAX_NR_CONSOLES && vc_cons[i].d); } -static void visual_init(struct vc_data *vc, int num, int init) +static void visual_init(struct vc_data *vc, int num, bool init) { /* ++Geert: vc->vc_sw->con_init determines console size */ if (vc->vc_sw) @@ -1083,7 +1083,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ vc->port.ops = &vc_port_ops; INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); - visual_init(vc, currcons, 1); + visual_init(vc, currcons, true); if (!*vc->uni_pagedict_loc) con_set_default_unimap(vc); @@ -3513,7 +3513,7 @@ static int __init con_init(void) vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT); INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); tty_port_init(&vc->port); - visual_init(vc, currcons, 1); + visual_init(vc, currcons, true); /* Assuming vc->vc_{cols,rows,screenbuf_size} are sane here. */ vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT); vc_init(vc, currcons || !vc->vc_sw->con_save_screen); @@ -3682,7 +3682,7 @@ static int do_bind_con_driver(const struct consw *csw, int first, int last, old_was_color = vc->vc_can_do_color; vc->vc_sw->con_deinit(vc); vc->vc_origin = (unsigned long)vc->vc_screenbuf; - visual_init(vc, i, 0); + visual_init(vc, i, false); set_origin(vc); update_attr(vc); diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 14af5d9e13b0..f2cef9d9a4b5 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -98,7 +98,7 @@ static const char *dummycon_startup(void) return "dummy device"; } -static void dummycon_init(struct vc_data *vc, int init) +static void dummycon_init(struct vc_data *vc, bool init) { vc->vc_can_do_color = 1; if (init) { diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index ef29b321967f..c5b255c96879 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -352,7 +352,7 @@ static const char *mdacon_startup(void) return "MDA-2"; } -static void mdacon_init(struct vc_data *c, int init) +static void mdacon_init(struct vc_data *c, bool init) { c->vc_complement_mask = 0x0800; /* reverse video */ c->vc_display_fg = &mda_display_fg; diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index e8e4f82cd4a1..12c64ef47087 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -324,7 +324,7 @@ out_unmap: return NULL; } -static void newport_init(struct vc_data *vc, int init) +static void newport_init(struct vc_data *vc, bool init) { int cols, rows; diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 992a4fa431aa..0bfeabc3f7c7 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -273,7 +273,7 @@ static int sticon_font_set(struct vc_data *vc, struct console_font *font, return sticon_set_font(vc, font, vpitch); } -static void sticon_init(struct vc_data *c, int init) +static void sticon_init(struct vc_data *c, bool init) { struct sti_struct *sti = sticon_sti; int vc_cols, vc_rows; diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 0c76e2817b49..5d523753def8 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -367,7 +367,7 @@ static const char *vgacon_startup(void) return display_desc; } -static void vgacon_init(struct vc_data *c, int init) +static void vgacon_init(struct vc_data *c, bool init) { struct uni_pagedict *p; @@ -384,7 +384,7 @@ static void vgacon_init(struct vc_data *c, int init) c->vc_scan_lines = vga_scan_lines; c->vc_font.height = c->vc_cell_height = vga_video_font_height; - /* set dimensions manually if init != 0 since vc_resize() will fail */ + /* set dimensions manually if init is true since vc_resize() will fail */ if (init) { c->vc_cols = vga_video_num_columns; c->vc_rows = vga_video_num_lines; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index d3fb98084eda..939c5d893dfb 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -988,7 +988,7 @@ static const char *fbcon_startup(void) return display_desc; } -static void fbcon_init(struct vc_data *vc, int init) +static void fbcon_init(struct vc_data *vc, bool init) { struct fb_info *info; struct fbcon_ops *ops; diff --git a/include/linux/console.h b/include/linux/console.h index 93a1db5bf3b5..fc9450e0c78f 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -36,6 +36,8 @@ enum vc_intensity; /** * struct consw - callbacks for consoles * + * @con_init: initialize the console on @vc. @init is true for the very first + * call on this @vc. * @con_scroll: move lines from @top to @bottom in direction @dir by @lines. * Return true if no generic handling should be done. * Invoked by csi_M and printing to the console. @@ -46,7 +48,7 @@ enum vc_intensity; struct consw { struct module *owner; const char *(*con_startup)(void); - void (*con_init)(struct vc_data *vc, int init); + void (*con_init)(struct vc_data *vc, bool init); void (*con_deinit)(struct vc_data *vc); void (*con_clear)(struct vc_data *vc, int sy, int sx, int height, int width); -- cgit v1.2.3 From 559f01a0ee6d924c6fec3eaf6a5b078b15e71070 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:35 +0100 Subject: tty: vt: sanitize arguments of consw::con_clear() In consw::con_clear(): * Height is always 1, so drop it. * Offsets and width are always unsigned values, so re-type them as such. This needs a new __fbcon_clear() in the fbcon code to still handle height which might not be 1 when called internally. Note that tests for negative count/width are left in place -- they are taken care of in the next patches. And document the hook. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: "James E.J. Bottomley" Cc: Daniel Vetter Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-parisc@vger.kernel.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-22-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 2 +- drivers/video/console/dummycon.c | 4 ++-- drivers/video/console/mdacon.c | 15 +++++---------- drivers/video/console/newport_con.c | 6 +++--- drivers/video/console/sticon.c | 8 ++++---- drivers/video/console/vgacon.c | 4 ++-- drivers/video/fbdev/core/fbcon.c | 32 +++++++++++++++++++------------- include/linux/console.h | 5 +++-- 8 files changed, 39 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index fcb41c8724f3..b6f1449421bc 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1585,7 +1585,7 @@ static void csi_X(struct vc_data *vc) vc_uniscr_clear_line(vc, vc->state.x, count); scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count); if (con_should_update(vc)) - vc->vc_sw->con_clear(vc, vc->state.y, vc->state.x, 1, count); + vc->vc_sw->con_clear(vc, vc->state.y, vc->state.x, count); vc->vc_need_wrap = 0; } diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index f2cef9d9a4b5..0a69d5c216ee 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -109,8 +109,8 @@ static void dummycon_init(struct vc_data *vc, bool init) } static void dummycon_deinit(struct vc_data *vc) { } -static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height, - int width) { } +static void dummycon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, + unsigned int width) { } static void dummycon_cursor(struct vc_data *vc, int mode) { } static bool dummycon_scroll(struct vc_data *vc, unsigned int top, diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index c5b255c96879..1ddbb6cd5b0c 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -442,23 +442,18 @@ static void mdacon_putcs(struct vc_data *c, const unsigned short *s, } } -static void mdacon_clear(struct vc_data *c, int y, int x, - int height, int width) +static void mdacon_clear(struct vc_data *c, unsigned int y, unsigned int x, + unsigned int width) { u16 *dest = mda_addr(x, y); u16 eattr = mda_convert_attr(c->vc_video_erase_char); - if (width <= 0 || height <= 0) + if (width <= 0) return; - if (x==0 && width==mda_num_columns) { - scr_memsetw(dest, eattr, height*width*2); - } else { - for (; height > 0; height--, dest+=mda_num_columns) - scr_memsetw(dest, eattr, width*2); - } + scr_memsetw(dest, eattr, width * 2); } - + static int mdacon_switch(struct vc_data *c) { return 1; /* redrawing needed */ diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 12c64ef47087..55c6106b3507 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -346,12 +346,12 @@ static void newport_deinit(struct vc_data *c) } } -static void newport_clear(struct vc_data *vc, int sy, int sx, int height, - int width) +static void newport_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, + unsigned int width) { int xend = ((sx + width) << 3) - 1; int ystart = ((sy << 4) + topscan) & 0x3ff; - int yend = (((sy + height) << 4) + topscan - 1) & 0x3ff; + int yend = (((sy + 1) << 4) + topscan - 1) & 0x3ff; if (logo_active) return; diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 0bfeabc3f7c7..d99c2a659bfd 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -300,13 +300,13 @@ static void sticon_deinit(struct vc_data *c) sticon_set_def_font(i); } -static void sticon_clear(struct vc_data *conp, int sy, int sx, int height, - int width) +static void sticon_clear(struct vc_data *conp, unsigned int sy, unsigned int sx, + unsigned int width) { - if (!height || !width) + if (!width) return; - sti_clear(sticon_sti, sy, sx, height, width, + sti_clear(sticon_sti, sy, sx, 1, width, conp->vc_video_erase_char, font_data[conp->vc_num]); } diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 5d523753def8..85f29dec2c3d 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1191,8 +1191,8 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, * The console `switch' structure for the VGA based console */ -static void vgacon_clear(struct vc_data *vc, int sy, int sx, int height, - int width) { } +static void vgacon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, + unsigned int width) { } static void vgacon_putc(struct vc_data *vc, int c, int ypos, int xpos) { } static void vgacon_putcs(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos) { } diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 939c5d893dfb..8a31a36483ea 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1235,8 +1235,8 @@ finished: * restriction is simplicity & efficiency at the moment. */ -static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, - int width) +static void __fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, + unsigned int height, unsigned int width) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_ops *ops = info->fbcon_par; @@ -1273,6 +1273,12 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, ops->clear(vc, info, real_y(p, sy), sx, height, width); } +static void fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, + unsigned int width) +{ + __fbcon_clear(vc, sy, sx, 1, width); +} + static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos) { @@ -1760,7 +1766,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, case SCROLL_MOVE: fbcon_redraw_blit(vc, info, p, t, b - t - count, count); - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); + __fbcon_clear(vc, b - count, 0, count, vc->vc_cols); scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), @@ -1783,7 +1789,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, b - t - count, vc->vc_cols); else goto redraw_up; - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); + __fbcon_clear(vc, b - count, 0, count, vc->vc_cols); break; case SCROLL_PAN_REDRAW: @@ -1801,7 +1807,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, vc->vc_rows - b, b); } else fbcon_redraw_move(vc, p, t + count, b - t - count, t); - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); + __fbcon_clear(vc, b - count, 0, count, vc->vc_cols); break; case SCROLL_PAN_MOVE: @@ -1824,14 +1830,14 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, b - t - count, vc->vc_cols); else goto redraw_up; - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); + __fbcon_clear(vc, b - count, 0, count, vc->vc_cols); break; case SCROLL_REDRAW: redraw_up: fbcon_redraw(vc, t, b - t - count, count * vc->vc_cols); - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); + __fbcon_clear(vc, b - count, 0, count, vc->vc_cols); scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), @@ -1848,7 +1854,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, case SCROLL_MOVE: fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, -count); - fbcon_clear(vc, t, 0, count, vc->vc_cols); + __fbcon_clear(vc, t, 0, count, vc->vc_cols); scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), @@ -1871,7 +1877,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, b - t - count, vc->vc_cols); else goto redraw_down; - fbcon_clear(vc, t, 0, count, vc->vc_cols); + __fbcon_clear(vc, t, 0, count, vc->vc_cols); break; case SCROLL_PAN_MOVE: @@ -1893,7 +1899,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, b - t - count, vc->vc_cols); else goto redraw_down; - fbcon_clear(vc, t, 0, count, vc->vc_cols); + __fbcon_clear(vc, t, 0, count, vc->vc_cols); break; case SCROLL_PAN_REDRAW: @@ -1910,14 +1916,14 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, fbcon_redraw_move(vc, p, count, t, 0); } else fbcon_redraw_move(vc, p, t, b - t - count, t + count); - fbcon_clear(vc, t, 0, count, vc->vc_cols); + __fbcon_clear(vc, t, 0, count, vc->vc_cols); break; case SCROLL_REDRAW: redraw_down: fbcon_redraw(vc, b - 1, b - t - count, -count * vc->vc_cols); - fbcon_clear(vc, t, 0, count, vc->vc_cols); + __fbcon_clear(vc, t, 0, count, vc->vc_cols); scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), @@ -2196,7 +2202,7 @@ static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, oldc = vc->vc_video_erase_char; vc->vc_video_erase_char &= charmask; - fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols); + __fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols); vc->vc_video_erase_char = oldc; } } diff --git a/include/linux/console.h b/include/linux/console.h index fc9450e0c78f..8fd96a5fca5f 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -38,6 +38,7 @@ enum vc_intensity; * * @con_init: initialize the console on @vc. @init is true for the very first * call on this @vc. + * @con_clear: erase @count characters at [@x, @y] on @vc. @count >= 1. * @con_scroll: move lines from @top to @bottom in direction @dir by @lines. * Return true if no generic handling should be done. * Invoked by csi_M and printing to the console. @@ -50,8 +51,8 @@ struct consw { const char *(*con_startup)(void); void (*con_init)(struct vc_data *vc, bool init); void (*con_deinit)(struct vc_data *vc); - void (*con_clear)(struct vc_data *vc, int sy, int sx, int height, - int width); + void (*con_clear)(struct vc_data *vc, unsigned int y, + unsigned int x, unsigned int count); void (*con_putc)(struct vc_data *vc, int c, int ypos, int xpos); void (*con_putcs)(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos); -- cgit v1.2.3 From 338c28107b51083846afdc5fe8f7830cc8abd893 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:39 +0100 Subject: tty: vt: sanitize consw::con_putc() parameters Make parameters of consw::con_putc() saner: * x and y are unsigned now, as they cannot be negative, and * ca is made u16, as it is composed of two 8bit values (character and attribute). See the con_putcs() hook, u16/ushort is worked on there. And document the hook. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-26-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/dummycon.c | 6 ++++-- drivers/video/console/newport_con.c | 4 ++-- include/linux/console.h | 5 ++++- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 0a69d5c216ee..1874beed0325 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -50,7 +50,8 @@ void dummycon_unregister_output_notifier(struct notifier_block *nb) raw_notifier_chain_unregister(&dummycon_output_nh, nb); } -static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) +static void dummycon_putc(struct vc_data *vc, u16 c, unsigned int y, + unsigned int x) { WARN_CONSOLE_UNLOCKED(); @@ -84,7 +85,8 @@ static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch) return 1; } #else -static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { } +static void dummycon_putc(struct vc_data *vc, u16 c, unsigned int y, + unsigned int x) { } static void dummycon_putcs(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos) { } static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch) diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 55c6106b3507..9b5c0118873e 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -367,8 +367,8 @@ static void newport_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, } } -static void newport_putc(struct vc_data *vc, int charattr, int ypos, - int xpos) +static void newport_putc(struct vc_data *vc, u16 charattr, unsigned int ypos, + unsigned int xpos) { unsigned char *p; diff --git a/include/linux/console.h b/include/linux/console.h index 8fd96a5fca5f..92d57e5b3009 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -39,6 +39,8 @@ enum vc_intensity; * @con_init: initialize the console on @vc. @init is true for the very first * call on this @vc. * @con_clear: erase @count characters at [@x, @y] on @vc. @count >= 1. + * @con_putc: emit one character with attributes @ca to [@x, @y] on @vc. + * (optional -- @con_putcs would be called instead) * @con_scroll: move lines from @top to @bottom in direction @dir by @lines. * Return true if no generic handling should be done. * Invoked by csi_M and printing to the console. @@ -53,7 +55,8 @@ struct consw { void (*con_deinit)(struct vc_data *vc); void (*con_clear)(struct vc_data *vc, unsigned int y, unsigned int x, unsigned int count); - void (*con_putc)(struct vc_data *vc, int c, int ypos, int xpos); + void (*con_putc)(struct vc_data *vc, u16 ca, unsigned int y, + unsigned int x); void (*con_putcs)(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos); void (*con_cursor)(struct vc_data *vc, int mode); -- cgit v1.2.3 From bfd7de49d7444ce46a48e92ce7cb11266ce79905 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:40 +0100 Subject: tty: vt: sanitize consw::con_putcs() parameters Similar to con_putc() in the previous patch: * make the pointer to charattr a pointer to u16, and * make x, y, and count unsigned as they are strictly non-negative. And again, document that hook. Signed-off-by: "Jiri Slaby (SUSE)" Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-27-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/dummycon.c | 10 +++++----- drivers/video/console/mdacon.c | 4 ++-- drivers/video/console/newport_con.c | 9 +++++---- drivers/video/console/sticon.c | 4 ++-- drivers/video/console/vgacon.c | 4 ++-- drivers/video/fbdev/core/fbcon.c | 4 ++-- include/linux/console.h | 6 ++++-- 7 files changed, 22 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 1874beed0325..188d9f3e201c 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -59,10 +59,10 @@ static void dummycon_putc(struct vc_data *vc, u16 c, unsigned int y, raw_notifier_call_chain(&dummycon_output_nh, 0, NULL); } -static void dummycon_putcs(struct vc_data *vc, const unsigned short *s, - int count, int ypos, int xpos) +static void dummycon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, + unsigned int ypos, unsigned int xpos) { - int i; + unsigned int i; if (!dummycon_putc_called) { /* Ignore erases */ @@ -87,8 +87,8 @@ static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch) #else static void dummycon_putc(struct vc_data *vc, u16 c, unsigned int y, unsigned int x) { } -static void dummycon_putcs(struct vc_data *vc, const unsigned short *s, - int count, int ypos, int xpos) { } +static void dummycon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, + unsigned int ypos, unsigned int xpos) { } static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch) { return 0; diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index 01e779943c00..b8822b615b2f 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -427,8 +427,8 @@ static inline u16 *mda_addr(unsigned int x, unsigned int y) return mda_vram_base + y * mda_num_columns + x; } -static void mdacon_putcs(struct vc_data *c, const unsigned short *s, - int count, int y, int x) +static void mdacon_putcs(struct vc_data *c, const u16 *s, unsigned int count, + unsigned int y, unsigned int x) { u16 *dest = mda_addr(x, y); diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 9b5c0118873e..5e65ee0b7c07 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -396,12 +396,13 @@ static void newport_putc(struct vc_data *vc, u16 charattr, unsigned int ypos, RENDER(npregs, p); } -static void newport_putcs(struct vc_data *vc, const unsigned short *s, - int count, int ypos, int xpos) +static void newport_putcs(struct vc_data *vc, const u16 *s, + unsigned int count, unsigned int ypos, + unsigned int xpos) { - int i; - int charattr; unsigned char *p; + unsigned int i; + u16 charattr; charattr = (scr_readw(s) >> 8) & 0xff; diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 2f87b5909d0d..906da1fde7c8 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -71,8 +71,8 @@ static const char *sticon_startup(void) return "STI console"; } -static void sticon_putcs(struct vc_data *conp, const unsigned short *s, - int count, int ypos, int xpos) +static void sticon_putcs(struct vc_data *conp, const u16 *s, unsigned int count, + unsigned int ypos, unsigned int xpos) { if (vga_is_gfx || console_blanked) return; diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 4beab11f87eb..aa0589085847 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1193,8 +1193,8 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, static void vgacon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, unsigned int width) { } -static void vgacon_putcs(struct vc_data *vc, const unsigned short *s, - int count, int ypos, int xpos) { } +static void vgacon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, + unsigned int ypos, unsigned int xpos) { } const struct consw vga_con = { .owner = THIS_MODULE, diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 38de0f8723aa..7a7b2ac0d7a9 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1279,8 +1279,8 @@ static void fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, __fbcon_clear(vc, sy, sx, 1, width); } -static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, - int count, int ypos, int xpos) +static void fbcon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, + unsigned int ypos, unsigned int xpos) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_display *p = &fb_display[vc->vc_num]; diff --git a/include/linux/console.h b/include/linux/console.h index 92d57e5b3009..82d55764a66f 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -41,6 +41,7 @@ enum vc_intensity; * @con_clear: erase @count characters at [@x, @y] on @vc. @count >= 1. * @con_putc: emit one character with attributes @ca to [@x, @y] on @vc. * (optional -- @con_putcs would be called instead) + * @con_putcs: emit @count characters with attributes @s to [@x, @y] on @vc. * @con_scroll: move lines from @top to @bottom in direction @dir by @lines. * Return true if no generic handling should be done. * Invoked by csi_M and printing to the console. @@ -57,8 +58,9 @@ struct consw { unsigned int x, unsigned int count); void (*con_putc)(struct vc_data *vc, u16 ca, unsigned int y, unsigned int x); - void (*con_putcs)(struct vc_data *vc, const unsigned short *s, - int count, int ypos, int xpos); + void (*con_putcs)(struct vc_data *vc, const u16 *s, + unsigned int count, unsigned int ypos, + unsigned int xpos); void (*con_cursor)(struct vc_data *vc, int mode); bool (*con_scroll)(struct vc_data *vc, unsigned int top, unsigned int bottom, enum con_scroll dir, -- cgit v1.2.3 From a292e3fc94cb9795bbba4ddac075a9055cd58a5e Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:43 +0100 Subject: tty: vt: remove CM_* constants There is no difference between CM_MOVE and CM_DRAW. Either of them enables the cursor. CM_ERASE then disables cursor. So get rid of all of them and use simple "bool enable". Note that this propagates down to the fbcon code. And document the hook. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: "James E.J. Bottomley" Cc: Daniel Vetter Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-parisc@vger.kernel.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-30-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 4 ++-- drivers/video/console/dummycon.c | 2 +- drivers/video/console/mdacon.c | 4 ++-- drivers/video/console/newport_con.c | 4 ++-- drivers/video/console/sticon.c | 6 +++--- drivers/video/console/vgacon.c | 6 +++--- drivers/video/fbdev/core/bitblit.c | 4 ++-- drivers/video/fbdev/core/fbcon.c | 19 +++++++++---------- drivers/video/fbdev/core/fbcon.h | 4 ++-- drivers/video/fbdev/core/fbcon_ccw.c | 4 ++-- drivers/video/fbdev/core/fbcon_cw.c | 4 ++-- drivers/video/fbdev/core/fbcon_ud.c | 4 ++-- drivers/video/fbdev/core/tileblit.c | 4 ++-- include/linux/console.h | 8 ++------ 14 files changed, 36 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 6091ffcf93d8..e4edcaf9d0a3 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -860,7 +860,7 @@ static void hide_cursor(struct vc_data *vc) if (vc_is_sel(vc)) clear_selection(); - vc->vc_sw->con_cursor(vc, CM_ERASE); + vc->vc_sw->con_cursor(vc, false); hide_softcursor(vc); } @@ -873,7 +873,7 @@ static void set_cursor(struct vc_data *vc) clear_selection(); add_softcursor(vc); if (CUR_SIZE(vc->vc_cursor_type) != CUR_NONE) - vc->vc_sw->con_cursor(vc, CM_DRAW); + vc->vc_sw->con_cursor(vc, true); } else hide_cursor(vc); } diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 188d9f3e201c..1171e27edef7 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -113,7 +113,7 @@ static void dummycon_init(struct vc_data *vc, bool init) static void dummycon_deinit(struct vc_data *vc) { } static void dummycon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx, unsigned int width) { } -static void dummycon_cursor(struct vc_data *vc, int mode) { } +static void dummycon_cursor(struct vc_data *vc, bool enable) { } static bool dummycon_scroll(struct vc_data *vc, unsigned int top, unsigned int bottom, enum con_scroll dir, diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index b8822b615b2f..bc851a1d9f4d 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -470,9 +470,9 @@ static int mdacon_blank(struct vc_data *c, int blank, int mode_switch) } } -static void mdacon_cursor(struct vc_data *c, int mode) +static void mdacon_cursor(struct vc_data *c, bool enable) { - if (mode == CM_ERASE) { + if (!enable) { mda_set_cursor(mda_vram_len - 1); return; } diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index f852717b88f0..e35406dea7c7 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -438,14 +438,14 @@ static void newport_putcs(struct vc_data *vc, const u16 *s, } } -static void newport_cursor(struct vc_data *vc, int mode) +static void newport_cursor(struct vc_data *vc, bool enable) { unsigned short treg; int xcurs, ycurs; treg = newport_vc2_get(npregs, VC2_IREG_CONTROL); - if (mode == CM_ERASE) { + if (!enable) { newport_vc2_set(npregs, VC2_IREG_CONTROL, (treg & ~(VC2_CTRL_ECDISP))); return; diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 42480874db00..786e1b3a98ea 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -86,7 +86,7 @@ static void sticon_putcs(struct vc_data *conp, const u16 *s, unsigned int count, } } -static void sticon_cursor(struct vc_data *conp, int mode) +static void sticon_cursor(struct vc_data *conp, bool enable) { unsigned short car1; @@ -95,7 +95,7 @@ static void sticon_cursor(struct vc_data *conp, int mode) return; car1 = conp->vc_screenbuf[conp->state.x + conp->state.y * conp->vc_cols]; - if (mode == CM_ERASE) { + if (!enable) { sti_putc(sticon_sti, car1, conp->state.y, conp->state.x, font_data[conp->vc_num]); return; @@ -121,7 +121,7 @@ static bool sticon_scroll(struct vc_data *conp, unsigned int t, if (vga_is_gfx) return false; - sticon_cursor(conp, CM_ERASE); + sticon_cursor(conp, false); switch (dir) { case SM_UP: diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 82d01a9ccd6d..d93eb15da435 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -503,7 +503,7 @@ static void vgacon_set_cursor_size(int from, int to) raw_spin_unlock_irqrestore(&vga_lock, flags); } -static void vgacon_cursor(struct vc_data *c, int mode) +static void vgacon_cursor(struct vc_data *c, bool enable) { unsigned int c_height; @@ -516,7 +516,7 @@ static void vgacon_cursor(struct vc_data *c, int mode) write_vga(14, (c->vc_pos - vga_vram_base) / 2); - if (mode == CM_ERASE) { + if (!enable) { if (vga_video_type >= VIDEO_TYPE_VGAC) vgacon_set_cursor_size(31, 30); else @@ -1030,7 +1030,7 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight) /* void size to cause regs to be rewritten */ cursor_size_lastfrom = 0; cursor_size_lastto = 0; - c->vc_sw->con_cursor(c, CM_DRAW); + c->vc_sw->con_cursor(c, true); } c->vc_font.height = c->vc_cell_height = fontheight; vc_resize(c, 0, rows); /* Adjust console size */ diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c index daff152f4c22..3ff1b2a8659e 100644 --- a/drivers/video/fbdev/core/bitblit.c +++ b/drivers/video/fbdev/core/bitblit.c @@ -233,7 +233,7 @@ static void bit_clear_margins(struct vc_data *vc, struct fb_info *info, } } -static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, +static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable, int fg, int bg) { struct fb_cursor cursor; @@ -348,7 +348,7 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, mask[i++] = msk; } - ops->cursor_state.enable = (mode != CM_ERASE) && !use_sw; + ops->cursor_state.enable = enable && !use_sw; cursor.image.data = src; cursor.image.fg_color = ops->cursor_state.image.fg_color; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 7a7b2ac0d7a9..c1765a6ef490 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -351,7 +351,7 @@ static void fb_flashcursor(struct work_struct *work) struct fb_info *info; struct vc_data *vc = NULL; int c; - int mode; + bool enable; int ret; /* FIXME: we should sort out the unbind locking instead */ @@ -375,9 +375,8 @@ static void fb_flashcursor(struct work_struct *work) } c = scr_readw((u16 *) vc->vc_pos); - mode = (!ops->cursor_flash || ops->cursor_state.enable) ? - CM_ERASE : CM_DRAW; - ops->cursor(vc, info, mode, get_color(vc, info, c, 1), + enable = ops->cursor_flash && !ops->cursor_state.enable; + ops->cursor(vc, info, enable, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); console_unlock(); @@ -1301,7 +1300,7 @@ static void fbcon_clear_margins(struct vc_data *vc, int bottom_only) ops->clear_margins(vc, info, margin_color, bottom_only); } -static void fbcon_cursor(struct vc_data *vc, int mode) +static void fbcon_cursor(struct vc_data *vc, bool enable) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_ops *ops = info->fbcon_par; @@ -1317,12 +1316,12 @@ static void fbcon_cursor(struct vc_data *vc, int mode) else fbcon_add_cursor_work(info); - ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; + ops->cursor_flash = enable; if (!ops->cursor) return; - ops->cursor(vc, info, mode, get_color(vc, info, c, 1), + ops->cursor(vc, info, enable, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); } @@ -1742,7 +1741,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, if (fbcon_is_inactive(vc, info)) return true; - fbcon_cursor(vc, CM_ERASE); + fbcon_cursor(vc, false); /* * ++Geert: Only use ywrap/ypan if the console is in text mode @@ -2221,7 +2220,7 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) if (!fbcon_is_inactive(vc, info)) { if (ops->blank_state != blank) { ops->blank_state = blank; - fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); + fbcon_cursor(vc, !blank); ops->cursor_flash = (!blank); if (fb_blank(info, blank)) @@ -2649,7 +2648,7 @@ void fbcon_suspended(struct fb_info *info) vc = vc_cons[ops->currcon].d; /* Clear cursor, restore saved data */ - fbcon_cursor(vc, CM_ERASE); + fbcon_cursor(vc, false); } void fbcon_resumed(struct fb_info *info) diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h index 0eaf54a21151..df70ea5ec5b3 100644 --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -61,8 +61,8 @@ struct fbcon_ops { int fg, int bg); void (*clear_margins)(struct vc_data *vc, struct fb_info *info, int color, int bottom_only); - void (*cursor)(struct vc_data *vc, struct fb_info *info, int mode, - int fg, int bg); + void (*cursor)(struct vc_data *vc, struct fb_info *info, + bool enable, int fg, int bg); int (*update_start)(struct fb_info *info); int (*rotate_font)(struct fb_info *info, struct vc_data *vc); struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */ diff --git a/drivers/video/fbdev/core/fbcon_ccw.c b/drivers/video/fbdev/core/fbcon_ccw.c index 889423d580bc..f9b794ff7d39 100644 --- a/drivers/video/fbdev/core/fbcon_ccw.c +++ b/drivers/video/fbdev/core/fbcon_ccw.c @@ -218,7 +218,7 @@ static void ccw_clear_margins(struct vc_data *vc, struct fb_info *info, } } -static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode, +static void ccw_cursor(struct vc_data *vc, struct fb_info *info, bool enable, int fg, int bg) { struct fb_cursor cursor; @@ -349,7 +349,7 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode, kfree(tmp); } - ops->cursor_state.enable = (mode != CM_ERASE) && !use_sw; + ops->cursor_state.enable = enable && !use_sw; cursor.image.data = src; cursor.image.fg_color = ops->cursor_state.image.fg_color; diff --git a/drivers/video/fbdev/core/fbcon_cw.c b/drivers/video/fbdev/core/fbcon_cw.c index a306ca5802e8..903f6fc174e1 100644 --- a/drivers/video/fbdev/core/fbcon_cw.c +++ b/drivers/video/fbdev/core/fbcon_cw.c @@ -201,7 +201,7 @@ static void cw_clear_margins(struct vc_data *vc, struct fb_info *info, } } -static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, +static void cw_cursor(struct vc_data *vc, struct fb_info *info, bool enable, int fg, int bg) { struct fb_cursor cursor; @@ -332,7 +332,7 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, kfree(tmp); } - ops->cursor_state.enable = (mode != CM_ERASE) && !use_sw; + ops->cursor_state.enable = enable && !use_sw; cursor.image.data = src; cursor.image.fg_color = ops->cursor_state.image.fg_color; diff --git a/drivers/video/fbdev/core/fbcon_ud.c b/drivers/video/fbdev/core/fbcon_ud.c index f6fc458b46c7..594331936fd3 100644 --- a/drivers/video/fbdev/core/fbcon_ud.c +++ b/drivers/video/fbdev/core/fbcon_ud.c @@ -248,7 +248,7 @@ static void ud_clear_margins(struct vc_data *vc, struct fb_info *info, } } -static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode, +static void ud_cursor(struct vc_data *vc, struct fb_info *info, bool enable, int fg, int bg) { struct fb_cursor cursor; @@ -372,7 +372,7 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode, mask[i++] = ~msk; } - ops->cursor_state.enable = (mode != CM_ERASE) && !use_sw; + ops->cursor_state.enable = enable && !use_sw; cursor.image.data = src; cursor.image.fg_color = ops->cursor_state.image.fg_color; diff --git a/drivers/video/fbdev/core/tileblit.c b/drivers/video/fbdev/core/tileblit.c index 2768eff247ba..eff7ec4da167 100644 --- a/drivers/video/fbdev/core/tileblit.c +++ b/drivers/video/fbdev/core/tileblit.c @@ -79,7 +79,7 @@ static void tile_clear_margins(struct vc_data *vc, struct fb_info *info, return; } -static void tile_cursor(struct vc_data *vc, struct fb_info *info, int mode, +static void tile_cursor(struct vc_data *vc, struct fb_info *info, bool enable, int fg, int bg) { struct fb_tilecursor cursor; @@ -87,7 +87,7 @@ static void tile_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.sx = vc->state.x; cursor.sy = vc->state.y; - cursor.mode = (mode == CM_ERASE || use_sw) ? 0 : 1; + cursor.mode = enable && !use_sw; cursor.fg = fg; cursor.bg = bg; diff --git a/include/linux/console.h b/include/linux/console.h index 82d55764a66f..a6a46b5efd66 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -42,6 +42,7 @@ enum vc_intensity; * @con_putc: emit one character with attributes @ca to [@x, @y] on @vc. * (optional -- @con_putcs would be called instead) * @con_putcs: emit @count characters with attributes @s to [@x, @y] on @vc. + * @con_cursor: enable/disable cursor depending on @enable * @con_scroll: move lines from @top to @bottom in direction @dir by @lines. * Return true if no generic handling should be done. * Invoked by csi_M and printing to the console. @@ -61,7 +62,7 @@ struct consw { void (*con_putcs)(struct vc_data *vc, const u16 *s, unsigned int count, unsigned int ypos, unsigned int xpos); - void (*con_cursor)(struct vc_data *vc, int mode); + void (*con_cursor)(struct vc_data *vc, bool enable); bool (*con_scroll)(struct vc_data *vc, unsigned int top, unsigned int bottom, enum con_scroll dir, unsigned int lines); @@ -128,11 +129,6 @@ static inline void con_debug_enter(struct vc_data *vc) { } static inline void con_debug_leave(void) { } #endif -/* cursor */ -#define CM_DRAW (1) -#define CM_ERASE (2) -#define CM_MOVE (3) - /* * The interface for a console, or any other device that wants to capture * console messages (printer driver?) -- cgit v1.2.3 From 8d5cc8eed738e3202379722295c626cba0849785 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:44 +0100 Subject: tty: vt: make consw::con_switch() return a bool The non-zero (true) return value from consw::con_switch() means a redraw is needed. So make this return type a bool explicitly instead of int. The latter might imply that -Eerrors are expected. They are not. And document the hook. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: "James E.J. Bottomley" Cc: Daniel Vetter Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-parisc@vger.kernel.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-31-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 2 +- drivers/video/console/dummycon.c | 4 ++-- drivers/video/console/mdacon.c | 4 ++-- drivers/video/console/newport_con.c | 4 ++-- drivers/video/console/sticon.c | 4 ++-- drivers/video/console/vgacon.c | 4 ++-- drivers/video/fbdev/core/fbcon.c | 6 +++--- include/linux/console.h | 4 +++- 8 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index e4edcaf9d0a3..fd868046f586 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -970,7 +970,7 @@ void redraw_screen(struct vc_data *vc, int is_switch) } if (redraw) { - int update; + bool update; int old_was_color = vc->vc_can_do_color; set_origin(vc); diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 1171e27edef7..c8d5aa0e3ed0 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -122,9 +122,9 @@ static bool dummycon_scroll(struct vc_data *vc, unsigned int top, return false; } -static int dummycon_switch(struct vc_data *vc) +static bool dummycon_switch(struct vc_data *vc) { - return 0; + return false; } /* diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index bc851a1d9f4d..4485ef923bb3 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -446,9 +446,9 @@ static void mdacon_clear(struct vc_data *c, unsigned int y, unsigned int x, scr_memsetw(dest, eattr, width * 2); } -static int mdacon_switch(struct vc_data *c) +static bool mdacon_switch(struct vc_data *c) { - return 1; /* redrawing needed */ + return true; /* redrawing needed */ } static int mdacon_blank(struct vc_data *c, int blank, int mode_switch) diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index e35406dea7c7..039d1c9937d2 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -459,7 +459,7 @@ static void newport_cursor(struct vc_data *vc, bool enable) newport_vc2_set(npregs, VC2_IREG_CURSY, ycurs); } -static int newport_switch(struct vc_data *vc) +static bool newport_switch(struct vc_data *vc) { static int logo_drawn = 0; @@ -473,7 +473,7 @@ static int newport_switch(struct vc_data *vc) } } - return 1; + return true; } static int newport_blank(struct vc_data *c, int blank, int mode_switch) diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 786e1b3a98ea..f3bb48a0e980 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -293,9 +293,9 @@ static void sticon_clear(struct vc_data *conp, unsigned int sy, unsigned int sx, conp->vc_video_erase_char, font_data[conp->vc_num]); } -static int sticon_switch(struct vc_data *conp) +static bool sticon_switch(struct vc_data *conp) { - return 1; /* needs refreshing */ + return true; /* needs refreshing */ } static int sticon_blank(struct vc_data *c, int blank, int mode_switch) diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index d93eb15da435..f89eb53c0b79 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -614,7 +614,7 @@ static void vgacon_doresize(struct vc_data *c, raw_spin_unlock_irqrestore(&vga_lock, flags); } -static int vgacon_switch(struct vc_data *c) +static bool vgacon_switch(struct vc_data *c) { int x = c->vc_cols * VGA_FONTWIDTH; int y = c->vc_rows * c->vc_cell_height; @@ -643,7 +643,7 @@ static int vgacon_switch(struct vc_data *c) vgacon_doresize(c, c->vc_cols, c->vc_rows); } - return 0; /* Redrawing not needed */ + return false; /* Redrawing not needed */ } static void vga_set_palette(struct vc_data *vc, const unsigned char *table) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index c1765a6ef490..d5d924225209 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2056,7 +2056,7 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, return 0; } -static int fbcon_switch(struct vc_data *vc) +static bool fbcon_switch(struct vc_data *vc) { struct fb_info *info, *old_info = NULL; struct fbcon_ops *ops; @@ -2178,9 +2178,9 @@ static int fbcon_switch(struct vc_data *vc) vc->vc_origin + vc->vc_size_row * vc->vc_top, vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); - return 0; + return false; } - return 1; + return true; } static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, diff --git a/include/linux/console.h b/include/linux/console.h index a6a46b5efd66..f7c6b5fc3a36 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -46,6 +46,8 @@ enum vc_intensity; * @con_scroll: move lines from @top to @bottom in direction @dir by @lines. * Return true if no generic handling should be done. * Invoked by csi_M and printing to the console. + * @con_switch: notifier about the console switch; it is supposed to return + * true if a redraw is needed. * @con_set_palette: sets the palette of the console to @table (optional) * @con_scrolldelta: the contents of the console should be scrolled by @lines. * Invoked by user. (optional) @@ -66,7 +68,7 @@ struct consw { bool (*con_scroll)(struct vc_data *vc, unsigned int top, unsigned int bottom, enum con_scroll dir, unsigned int lines); - int (*con_switch)(struct vc_data *vc); + bool (*con_switch)(struct vc_data *vc); int (*con_blank)(struct vc_data *vc, int blank, int mode_switch); int (*con_font_set)(struct vc_data *vc, struct console_font *font, unsigned int vpitch, unsigned int flags); -- cgit v1.2.3 From ace4ebf9b70a7daea12102c09ba5ef6bb73223aa Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:46 +0100 Subject: tty: vt: define a common enum for VESA blanking constants There are currently two places with VESA blanking constants definitions: fb.h and console.h. Extract/unify the two to a separate header (vesa.h). Given the fb's is in an uapi header, create the common header in uapi too. Note that instead of macros, an enum (vesa_blank_mode) is created. But the macros are kept too (they now expand to the enum constants), just in case someone in userspace performs some #ifdeffery. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: linux-kernel@vger.kernel.org Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: Thomas Zimmermann Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-33-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/console.h | 7 +------ include/uapi/linux/fb.h | 8 +------- include/uapi/linux/vesa.h | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 include/uapi/linux/vesa.h (limited to 'include') diff --git a/include/linux/console.h b/include/linux/console.h index f7c6b5fc3a36..860f82756c9c 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -18,6 +18,7 @@ #include #include #include +#include struct vc_data; struct console_font_op; @@ -520,12 +521,6 @@ void vcs_remove_sysfs(int index); */ extern atomic_t ignore_console_lock_warning; -/* VESA Blanking Levels */ -#define VESA_NO_BLANKING 0 -#define VESA_VSYNC_SUSPEND 1 -#define VESA_HSYNC_SUSPEND 2 -#define VESA_POWERDOWN 3 - extern void console_init(void); /* For deferred console takeover */ diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h index 3a49913d006c..cde8f173f566 100644 --- a/include/uapi/linux/fb.h +++ b/include/uapi/linux/fb.h @@ -4,6 +4,7 @@ #include #include +#include /* Definitions of frame buffers */ @@ -293,13 +294,6 @@ struct fb_con2fbmap { __u32 framebuffer; }; -/* VESA Blanking Levels */ -#define VESA_NO_BLANKING 0 -#define VESA_VSYNC_SUSPEND 1 -#define VESA_HSYNC_SUSPEND 2 -#define VESA_POWERDOWN 3 - - enum { /* screen: unblanked, hsync: on, vsync: on */ FB_BLANK_UNBLANK = VESA_NO_BLANKING, diff --git a/include/uapi/linux/vesa.h b/include/uapi/linux/vesa.h new file mode 100644 index 000000000000..81947f5088cd --- /dev/null +++ b/include/uapi/linux/vesa.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_VESA_H +#define _UAPI_LINUX_VESA_H + +/* VESA Blanking Levels */ +enum vesa_blank_mode { + VESA_NO_BLANKING = 0, +#define VESA_NO_BLANKING VESA_NO_BLANKING + VESA_VSYNC_SUSPEND = 1, +#define VESA_VSYNC_SUSPEND VESA_VSYNC_SUSPEND + VESA_HSYNC_SUSPEND = 2, +#define VESA_HSYNC_SUSPEND VESA_HSYNC_SUSPEND + VESA_POWERDOWN = VESA_VSYNC_SUSPEND | VESA_HSYNC_SUSPEND, +#define VESA_POWERDOWN VESA_POWERDOWN + VESA_BLANK_MAX = VESA_POWERDOWN, +}; + +#endif -- cgit v1.2.3 From 0a58d83dfb14ac30126c37b18d4578e5b261459d Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:48 +0100 Subject: tty: vt: use enum constants for VESA blanking modes Use the new enum for VESA constants. This improves type checking in consw::con_blank(). Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: "James E.J. Bottomley" Cc: Daniel Vetter Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-parisc@vger.kernel.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-35-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 4 ++-- drivers/video/console/dummycon.c | 6 ++++-- drivers/video/console/mdacon.c | 3 ++- drivers/video/console/newport_con.c | 3 ++- drivers/video/console/sticon.c | 3 ++- drivers/video/console/vgacon.c | 7 ++++--- drivers/video/fbdev/core/fbcon.c | 3 ++- include/linux/console.h | 3 ++- 8 files changed, 20 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 0d5d7b5074a4..de9148094c2d 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -175,7 +175,7 @@ int do_poke_blanked_console; int console_blanked; EXPORT_SYMBOL(console_blanked); -static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ +static enum vesa_blank_mode vesa_blank_mode; static int vesa_off_interval; static int blankinterval; core_param(consoleblank, blankinterval, int, 0444); @@ -4320,7 +4320,7 @@ static int set_vesa_blanking(u8 __user *mode_user) return -EFAULT; console_lock(); - vesa_blank_mode = (mode < 4) ? mode : VESA_NO_BLANKING; + vesa_blank_mode = (mode <= VESA_BLANK_MAX) ? mode : VESA_NO_BLANKING; console_unlock(); return 0; diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index c8d5aa0e3ed0..d86c1d798690 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -79,7 +79,8 @@ static void dummycon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, raw_notifier_call_chain(&dummycon_output_nh, 0, NULL); } -static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch) +static int dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank, + int mode_switch) { /* Redraw, so that we get putc(s) for output done while blanked */ return 1; @@ -89,7 +90,8 @@ static void dummycon_putc(struct vc_data *vc, u16 c, unsigned int y, unsigned int x) { } static void dummycon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, unsigned int ypos, unsigned int xpos) { } -static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch) +static int dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank, + int mode_switch) { return 0; } diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index 4485ef923bb3..63e3ce678aab 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -451,7 +451,8 @@ static bool mdacon_switch(struct vc_data *c) return true; /* redrawing needed */ } -static int mdacon_blank(struct vc_data *c, int blank, int mode_switch) +static int mdacon_blank(struct vc_data *c, enum vesa_blank_mode blank, + int mode_switch) { if (mda_type == TYPE_MDA) { if (blank) diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index ad3a09142770..38437a53b7f1 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -476,7 +476,8 @@ static bool newport_switch(struct vc_data *vc) return true; } -static int newport_blank(struct vc_data *c, int blank, int mode_switch) +static int newport_blank(struct vc_data *c, enum vesa_blank_mode blank, + int mode_switch) { unsigned short treg; diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 817b89c45e81..e9d5d1f92883 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -298,7 +298,8 @@ static bool sticon_switch(struct vc_data *conp) return true; /* needs refreshing */ } -static int sticon_blank(struct vc_data *c, int blank, int mode_switch) +static int sticon_blank(struct vc_data *c, enum vesa_blank_mode blank, + int mode_switch) { if (blank == VESA_NO_BLANKING) { if (mode_switch) diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 02eccd9b3542..84f3682704c7 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -81,7 +81,7 @@ static unsigned int vga_video_num_lines; /* Number of text lines */ static bool vga_can_do_color; /* Do we support colors? */ static unsigned int vga_default_font_height __read_mostly; /* Height of default screen font */ static unsigned char vga_video_type __read_mostly; /* Card type */ -static int vga_vesa_blanked; +static enum vesa_blank_mode vga_vesa_blanked; static bool vga_palette_blanked; static bool vga_is_gfx; static bool vga_512_chars; @@ -683,7 +683,7 @@ static struct { unsigned char ClockingMode; /* Seq-Controller:01h */ } vga_state; -static void vga_vesa_blank(struct vgastate *state, int mode) +static void vga_vesa_blank(struct vgastate *state, enum vesa_blank_mode mode) { /* save original values of VGA controller registers */ if (!vga_vesa_blanked) { @@ -797,7 +797,8 @@ static void vga_pal_blank(struct vgastate *state) } } -static int vgacon_blank(struct vc_data *c, int blank, int mode_switch) +static int vgacon_blank(struct vc_data *c, enum vesa_blank_mode blank, + int mode_switch) { switch (blank) { case VESA_NO_BLANKING: /* Unblank */ diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index d5d924225209..69be5f2106bc 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2198,7 +2198,8 @@ static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, } } -static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) +static int fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank, + int mode_switch) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_ops *ops = info->fbcon_par; diff --git a/include/linux/console.h b/include/linux/console.h index 860f82756c9c..69040d7c8f97 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -70,7 +70,8 @@ struct consw { unsigned int bottom, enum con_scroll dir, unsigned int lines); bool (*con_switch)(struct vc_data *vc); - int (*con_blank)(struct vc_data *vc, int blank, int mode_switch); + int (*con_blank)(struct vc_data *vc, enum vesa_blank_mode blank, + int mode_switch); int (*con_font_set)(struct vc_data *vc, struct console_font *font, unsigned int vpitch, unsigned int flags); int (*con_font_get)(struct vc_data *vc, struct console_font *font, -- cgit v1.2.3 From 77e110936a42b212c0fb576356ed274eb1d90c54 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:49 +0100 Subject: tty: vt: make types around consw::con_blank() bool Both the mode_switch parameter and the return value (a redraw needed) are true/false. So switch them to bool, so that users won't return -Eerrors or anything else. And document the hook. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: "James E.J. Bottomley" Cc: Daniel Vetter Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-parisc@vger.kernel.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-36-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/dummycon.c | 12 ++++++------ drivers/video/console/mdacon.c | 8 ++++---- drivers/video/console/newport_con.c | 7 ++++--- drivers/video/console/sticon.c | 9 +++++---- drivers/video/console/vgacon.c | 4 ++-- drivers/video/fbdev/core/fbcon.c | 6 +++--- include/linux/console.h | 7 +++++-- 7 files changed, 29 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index d86c1d798690..139049368fdc 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -79,21 +79,21 @@ static void dummycon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, raw_notifier_call_chain(&dummycon_output_nh, 0, NULL); } -static int dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank, - int mode_switch) +static bool dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank, + bool mode_switch) { /* Redraw, so that we get putc(s) for output done while blanked */ - return 1; + return true; } #else static void dummycon_putc(struct vc_data *vc, u16 c, unsigned int y, unsigned int x) { } static void dummycon_putcs(struct vc_data *vc, const u16 *s, unsigned int count, unsigned int ypos, unsigned int xpos) { } -static int dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank, - int mode_switch) +static bool dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank, + bool mode_switch) { - return 0; + return false; } #endif diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index 63e3ce678aab..c0e1f4554a44 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -451,8 +451,8 @@ static bool mdacon_switch(struct vc_data *c) return true; /* redrawing needed */ } -static int mdacon_blank(struct vc_data *c, enum vesa_blank_mode blank, - int mode_switch) +static bool mdacon_blank(struct vc_data *c, enum vesa_blank_mode blank, + bool mode_switch) { if (mda_type == TYPE_MDA) { if (blank) @@ -460,14 +460,14 @@ static int mdacon_blank(struct vc_data *c, enum vesa_blank_mode blank, mda_convert_attr(c->vc_video_erase_char), c->vc_screenbuf_size); /* Tell console.c that it has to restore the screen itself */ - return 1; + return true; } else { if (blank) outb_p(0x00, mda_mode_port); /* disable video */ else outb_p(MDA_MODE_VIDEO_EN | MDA_MODE_BLINK_EN, mda_mode_port); - return 0; + return false; } } diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 38437a53b7f1..dbb31bf87bf1 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -476,8 +476,8 @@ static bool newport_switch(struct vc_data *vc) return true; } -static int newport_blank(struct vc_data *c, enum vesa_blank_mode blank, - int mode_switch) +static bool newport_blank(struct vc_data *c, enum vesa_blank_mode blank, + bool mode_switch) { unsigned short treg; @@ -492,7 +492,8 @@ static int newport_blank(struct vc_data *c, enum vesa_blank_mode blank, newport_vc2_set(npregs, VC2_IREG_CONTROL, (treg & ~(VC2_CTRL_EDISP))); } - return 1; + + return true; } static int newport_set_font(int unit, struct console_font *op, unsigned int vpitch) diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index e9d5d1f92883..cbb9ef438214 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -298,19 +298,20 @@ static bool sticon_switch(struct vc_data *conp) return true; /* needs refreshing */ } -static int sticon_blank(struct vc_data *c, enum vesa_blank_mode blank, - int mode_switch) +static bool sticon_blank(struct vc_data *c, enum vesa_blank_mode blank, + bool mode_switch) { if (blank == VESA_NO_BLANKING) { if (mode_switch) vga_is_gfx = 0; - return 1; + return true; } sti_clear(sticon_sti, 0, 0, c->vc_rows, c->vc_cols, BLANK, font_data[c->vc_num]); if (mode_switch) vga_is_gfx = 1; - return 1; + + return true; } static u8 sticon_build_attr(struct vc_data *conp, u8 color, diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 84f3682704c7..c9a22118102f 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -797,8 +797,8 @@ static void vga_pal_blank(struct vgastate *state) } } -static int vgacon_blank(struct vc_data *c, enum vesa_blank_mode blank, - int mode_switch) +static bool vgacon_blank(struct vc_data *c, enum vesa_blank_mode blank, + bool mode_switch) { switch (blank) { case VESA_NO_BLANKING: /* Unblank */ diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 69be5f2106bc..eee2adf5c682 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2198,8 +2198,8 @@ static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, } } -static int fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank, - int mode_switch) +static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank, + bool mode_switch) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); struct fbcon_ops *ops = info->fbcon_par; @@ -2238,7 +2238,7 @@ static int fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank, else fbcon_add_cursor_work(info); - return 0; + return false; } static void fbcon_debug_enter(struct vc_data *vc) diff --git a/include/linux/console.h b/include/linux/console.h index 69040d7c8f97..6392bcd2fe7c 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -49,6 +49,9 @@ enum vc_intensity; * Invoked by csi_M and printing to the console. * @con_switch: notifier about the console switch; it is supposed to return * true if a redraw is needed. + * @con_blank: blank/unblank the console. The target mode is passed in @blank. + * @mode_switch is set if changing from/to text/graphics. The hook + * is supposed to return true if a redraw is needed. * @con_set_palette: sets the palette of the console to @table (optional) * @con_scrolldelta: the contents of the console should be scrolled by @lines. * Invoked by user. (optional) @@ -70,8 +73,8 @@ struct consw { unsigned int bottom, enum con_scroll dir, unsigned int lines); bool (*con_switch)(struct vc_data *vc); - int (*con_blank)(struct vc_data *vc, enum vesa_blank_mode blank, - int mode_switch); + bool (*con_blank)(struct vc_data *vc, enum vesa_blank_mode blank, + bool mode_switch); int (*con_font_set)(struct vc_data *vc, struct console_font *font, unsigned int vpitch, unsigned int flags); int (*con_font_get)(struct vc_data *vc, struct console_font *font, -- cgit v1.2.3 From fd0f631fffa87f1c26045c3c88c0c4a7706d14de Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:50 +0100 Subject: tty: vt: make font of consw::con_font_set() const Provided the font parameter of consw::con_font_set() is not supposed to be changed, make it const. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: "James E.J. Bottomley" Cc: Daniel Vetter Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-parisc@vger.kernel.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-37-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 2 +- drivers/video/console/newport_con.c | 5 +++-- drivers/video/console/sticon.c | 4 ++-- drivers/video/console/vgacon.c | 2 +- drivers/video/fbdev/core/fbcon.c | 2 +- include/linux/console.h | 5 +++-- 6 files changed, 11 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index de9148094c2d..0ac537f82f7a 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4625,7 +4625,7 @@ out: return rc; } -static int con_font_set(struct vc_data *vc, struct console_font_op *op) +static int con_font_set(struct vc_data *vc, const struct console_font_op *op) { struct console_font font; int rc = -EINVAL; diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index dbb31bf87bf1..4203bd5fd0a1 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -496,7 +496,8 @@ static bool newport_blank(struct vc_data *c, enum vesa_blank_mode blank, return true; } -static int newport_set_font(int unit, struct console_font *op, unsigned int vpitch) +static int newport_set_font(int unit, const struct console_font *op, + unsigned int vpitch) { int w = op->width; int h = op->height; @@ -568,7 +569,7 @@ static int newport_font_default(struct vc_data *vc, struct console_font *op, cha return newport_set_def_font(vc->vc_num, op); } -static int newport_font_set(struct vc_data *vc, struct console_font *font, +static int newport_font_set(struct vc_data *vc, const struct console_font *font, unsigned int vpitch, unsigned int flags) { return newport_set_font(vc->vc_num, font, vpitch); diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index cbb9ef438214..710201fb8ce4 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -153,7 +153,7 @@ static void sticon_set_def_font(int unit) } } -static int sticon_set_font(struct vc_data *vc, struct console_font *op, +static int sticon_set_font(struct vc_data *vc, const struct console_font *op, unsigned int vpitch) { struct sti_struct *sti = sticon_sti; @@ -253,7 +253,7 @@ static int sticon_font_default(struct vc_data *vc, struct console_font *op, char return 0; } -static int sticon_font_set(struct vc_data *vc, struct console_font *font, +static int sticon_font_set(struct vc_data *vc, const struct console_font *font, unsigned int vpitch, unsigned int flags) { return sticon_set_font(vc, font, vpitch); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index c9a22118102f..4d1c8f5863af 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1039,7 +1039,7 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight) return 0; } -static int vgacon_font_set(struct vc_data *c, struct console_font *font, +static int vgacon_font_set(struct vc_data *c, const struct console_font *font, unsigned int vpitch, unsigned int flags) { unsigned charcount = font->charcount; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index eee2adf5c682..62474630c4d4 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2460,7 +2460,7 @@ err_out: * but lets not assume that, since charcount of 512 is small for unicode support. */ -static int fbcon_set_font(struct vc_data *vc, struct console_font *font, +static int fbcon_set_font(struct vc_data *vc, const struct console_font *font, unsigned int vpitch, unsigned int flags) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); diff --git a/include/linux/console.h b/include/linux/console.h index 6392bcd2fe7c..0a9f4cbdde83 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -75,8 +75,9 @@ struct consw { bool (*con_switch)(struct vc_data *vc); bool (*con_blank)(struct vc_data *vc, enum vesa_blank_mode blank, bool mode_switch); - int (*con_font_set)(struct vc_data *vc, struct console_font *font, - unsigned int vpitch, unsigned int flags); + int (*con_font_set)(struct vc_data *vc, + const struct console_font *font, + unsigned int vpitch, unsigned int flags); int (*con_font_get)(struct vc_data *vc, struct console_font *font, unsigned int vpitch); int (*con_font_default)(struct vc_data *vc, -- cgit v1.2.3 From 4f59617065592c446cd8450e9e6bac229cbc1383 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:51 +0100 Subject: tty: vt: make consw::con_font_default()'s name const It's a name after all and that is not supposed to be changed. So make it const to make this obvious. Signed-off-by: "Jiri Slaby (SUSE)" Cc: "James E.J. Bottomley" Cc: Daniel Vetter Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-parisc@vger.kernel.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-38-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/newport_con.c | 3 ++- drivers/video/console/sticon.c | 3 ++- drivers/video/fbdev/core/fbcon.c | 3 ++- include/linux/console.h | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 4203bd5fd0a1..a51cfc1d560e 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -564,7 +564,8 @@ static int newport_set_def_font(int unit, struct console_font *op) return 0; } -static int newport_font_default(struct vc_data *vc, struct console_font *op, char *name) +static int newport_font_default(struct vc_data *vc, struct console_font *op, + const char *name) { return newport_set_def_font(vc->vc_num, op); } diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 710201fb8ce4..4c7b4959a1aa 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -246,7 +246,8 @@ static int sticon_set_font(struct vc_data *vc, const struct console_font *op, return 0; } -static int sticon_font_default(struct vc_data *vc, struct console_font *op, char *name) +static int sticon_font_default(struct vc_data *vc, struct console_font *op, + const char *name) { sticon_set_def_font(vc->vc_num); diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 62474630c4d4..657160eec0a5 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2533,7 +2533,8 @@ static int fbcon_set_font(struct vc_data *vc, const struct console_font *font, return fbcon_do_set_font(vc, font->width, font->height, charcount, new_data, 1); } -static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name) +static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, + const char *name) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); const struct font_desc *f; diff --git a/include/linux/console.h b/include/linux/console.h index 0a9f4cbdde83..6bb7e5e37ae4 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -81,7 +81,7 @@ struct consw { int (*con_font_get)(struct vc_data *vc, struct console_font *font, unsigned int vpitch); int (*con_font_default)(struct vc_data *vc, - struct console_font *font, char *name); + struct console_font *font, const char *name); int (*con_resize)(struct vc_data *vc, unsigned int width, unsigned int height, bool from_user); void (*con_set_palette)(struct vc_data *vc, -- cgit v1.2.3 From 42822fabfc24f4fc8d5404d9359fa17a0bcfcea8 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:52 +0100 Subject: tty: vt: change consw::con_set_origin() return type The return value of consw::con_set_origin() is only true/false, meaining if vc->vc_origin is set to vc->vc_screenbuf or not. So switch the type and returned values accordingly. And document the hook. Signed-off-by: "Jiri Slaby (SUSE)" Cc: Helge Deller Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-39-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/vgacon.c | 8 ++++---- include/linux/console.h | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 4d1c8f5863af..7597f04b0dc7 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -65,7 +65,7 @@ static struct vgastate vgastate; * Interface used by the world */ -static int vgacon_set_origin(struct vc_data *c); +static bool vgacon_set_origin(struct vc_data *c); static struct uni_pagedict *vgacon_uni_pagedir; static int vgacon_refcount; @@ -1100,15 +1100,15 @@ static int vgacon_resize(struct vc_data *c, unsigned int width, return 0; } -static int vgacon_set_origin(struct vc_data *c) +static bool vgacon_set_origin(struct vc_data *c) { if (vga_is_gfx || /* We don't play origin tricks in graphic modes */ (console_blanked && !vga_palette_blanked)) /* Nor we write to blanked screens */ - return 0; + return false; c->vc_origin = c->vc_visible_origin = vga_vram_base; vga_set_mem_top(c); vga_rolled_over = 0; - return 1; + return true; } static void vgacon_save_screen(struct vc_data *c) diff --git a/include/linux/console.h b/include/linux/console.h index 6bb7e5e37ae4..82e4b554a801 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -55,6 +55,9 @@ enum vc_intensity; * @con_set_palette: sets the palette of the console to @table (optional) * @con_scrolldelta: the contents of the console should be scrolled by @lines. * Invoked by user. (optional) + * @con_set_origin: set origin (see &vc_data::vc_origin) of the @vc. If not + * provided or returns false, the origin is set to + * @vc->vc_screenbuf. (optional) */ struct consw { struct module *owner; @@ -87,7 +90,7 @@ struct consw { void (*con_set_palette)(struct vc_data *vc, const unsigned char *table); void (*con_scrolldelta)(struct vc_data *vc, int lines); - int (*con_set_origin)(struct vc_data *vc); + bool (*con_set_origin)(struct vc_data *vc); void (*con_save_screen)(struct vc_data *vc); u8 (*con_build_attr)(struct vc_data *vc, u8 color, enum vc_intensity intensity, -- cgit v1.2.3 From 7cf01c92addb73c3055ff0fc596441c80ce82113 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:54 +0100 Subject: tty: vt: remove consw::con_screen_pos() After the previous patch, nobody sets that hook. So drop it completely. Signed-off-by: "Jiri Slaby (SUSE)" Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-41-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 12 +++--------- include/linux/console.h | 1 - 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 0ac537f82f7a..4e9d689143e0 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -289,15 +289,9 @@ static inline bool con_should_update(const struct vc_data *vc) static inline unsigned short *screenpos(const struct vc_data *vc, int offset, bool viewed) { - unsigned short *p; - - if (!viewed) - p = (unsigned short *)(vc->vc_origin + offset); - else if (!vc->vc_sw->con_screen_pos) - p = (unsigned short *)(vc->vc_visible_origin + offset); - else - p = vc->vc_sw->con_screen_pos(vc, offset); - return p; + unsigned long origin = viewed ? vc->vc_visible_origin : vc->vc_origin; + + return (unsigned short *)(origin + offset); } static void con_putc(struct vc_data *vc, u16 ca, unsigned int y, unsigned int x) diff --git a/include/linux/console.h b/include/linux/console.h index 82e4b554a801..b2d8621cea57 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -96,7 +96,6 @@ struct consw { enum vc_intensity intensity, bool blink, bool underline, bool reverse, bool italic); void (*con_invert_region)(struct vc_data *vc, u16 *p, int count); - u16 *(*con_screen_pos)(const struct vc_data *vc, int offset); unsigned long (*con_getxy)(struct vc_data *vc, unsigned long position, int *px, int *py); /* -- cgit v1.2.3 From f441aa3b441306e35e8fcbec5ac13c68b5f48245 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:57 +0100 Subject: tty: vt: remove consw::con_getxy() After the previous patch, nobody sets that hook. So drop it completely. Signed-off-by: "Jiri Slaby (SUSE)" Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-44-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 20 +++++--------------- include/linux/console.h | 2 -- 2 files changed, 5 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index f6fa76c0eb5b..cbe1a1106d53 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -593,18 +593,12 @@ static void con_scroll(struct vc_data *vc, unsigned int top, static void do_update_region(struct vc_data *vc, unsigned long start, int count) { unsigned int xx, yy, offset; - u16 *p; + u16 *p = (u16 *)start; + + offset = (start - vc->vc_origin) / 2; + xx = offset % vc->vc_cols; + yy = offset / vc->vc_cols; - p = (u16 *) start; - if (!vc->vc_sw->con_getxy) { - offset = (start - vc->vc_origin) / 2; - xx = offset % vc->vc_cols; - yy = offset / vc->vc_cols; - } else { - int nxx, nyy; - start = vc->vc_sw->con_getxy(vc, start, &nxx, &nyy); - xx = nxx; yy = nyy; - } for(;;) { u16 attrib = scr_readw(p) & 0xff00; int startx = xx; @@ -627,10 +621,6 @@ static void do_update_region(struct vc_data *vc, unsigned long start, int count) break; xx = 0; yy++; - if (vc->vc_sw->con_getxy) { - p = (u16 *)start; - start = vc->vc_sw->con_getxy(vc, start, NULL, NULL); - } } } diff --git a/include/linux/console.h b/include/linux/console.h index b2d8621cea57..fa2cd81102b8 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -96,8 +96,6 @@ struct consw { enum vc_intensity intensity, bool blink, bool underline, bool reverse, bool italic); void (*con_invert_region)(struct vc_data *vc, u16 *p, int count); - unsigned long (*con_getxy)(struct vc_data *vc, unsigned long position, - int *px, int *py); /* * Flush the video console driver's scrollback buffer */ -- cgit v1.2.3 From b23bf1a43bdbce1a281f11169dd9d426018b00c9 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:58 +0100 Subject: tty: vt: remove unused consw::con_flush_scrollback() consw::con_flush_scrollback() is unused since commit 973c096f6a85 (vgacon: remove software scrollback support). Drop it. Signed-off-by: "Jiri Slaby (SUSE)" Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-45-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 27 ++++++++++++--------------- include/linux/console.h | 4 ---- 2 files changed, 12 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index cbe1a1106d53..bfe51af9a0f3 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -888,21 +888,18 @@ static void flush_scrollback(struct vc_data *vc) WARN_CONSOLE_UNLOCKED(); set_origin(vc); - if (vc->vc_sw->con_flush_scrollback) { - vc->vc_sw->con_flush_scrollback(vc); - } else if (con_is_visible(vc)) { - /* - * When no con_flush_scrollback method is provided then the - * legacy way for flushing the scrollback buffer is to use - * a side effect of the con_switch method. We do it only on - * the foreground console as background consoles have no - * scrollback buffers in that case and we obviously don't - * want to switch to them. - */ - hide_cursor(vc); - vc->vc_sw->con_switch(vc); - set_cursor(vc); - } + if (!con_is_visible(vc)) + return; + + /* + * The legacy way for flushing the scrollback buffer is to use a side + * effect of the con_switch method. We do it only on the foreground + * console as background consoles have no scrollback buffers in that + * case and we obviously don't want to switch to them. + */ + hide_cursor(vc); + vc->vc_sw->con_switch(vc); + set_cursor(vc); } /* diff --git a/include/linux/console.h b/include/linux/console.h index fa2cd81102b8..1eac3e6e32a2 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -96,10 +96,6 @@ struct consw { enum vc_intensity intensity, bool blink, bool underline, bool reverse, bool italic); void (*con_invert_region)(struct vc_data *vc, u16 *p, int count); - /* - * Flush the video console driver's scrollback buffer - */ - void (*con_flush_scrollback)(struct vc_data *vc); /* * Prepare the console for the debugger. This includes, but is not * limited to, unblanking the console, loading an appropriate -- cgit v1.2.3 From d1e2221644c490a73d2968fe316f0af170e0ebcf Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:03:59 +0100 Subject: tty: vt: document the rest of struct consw There are still members of struct consw which are not documented yet. Fix that up, so we can generate kernel-doc for that struct. Signed-off-by: "Jiri Slaby (SUSE)" Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-46-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/console.h | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/console.h b/include/linux/console.h index 1eac3e6e32a2..f1a334ad268d 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -37,8 +37,11 @@ enum vc_intensity; /** * struct consw - callbacks for consoles * + * @owner: the module to get references of when this console is used + * @con_startup: set up the console and return its name (like VGA, EGA, ...) * @con_init: initialize the console on @vc. @init is true for the very first * call on this @vc. + * @con_deinit: deinitialize the console from @vc. * @con_clear: erase @count characters at [@x, @y] on @vc. @count >= 1. * @con_putc: emit one character with attributes @ca to [@x, @y] on @vc. * (optional -- @con_putcs would be called instead) @@ -52,12 +55,33 @@ enum vc_intensity; * @con_blank: blank/unblank the console. The target mode is passed in @blank. * @mode_switch is set if changing from/to text/graphics. The hook * is supposed to return true if a redraw is needed. - * @con_set_palette: sets the palette of the console to @table (optional) + * @con_font_set: set console @vc font to @font with height @vpitch. @flags can + * be %KD_FONT_FLAG_DONT_RECALC. (optional) + * @con_font_get: fetch the current font on @vc of height @vpitch into @font. + * (optional) + * @con_font_default: set default font on @vc. @name can be %NULL or font name + * to search for. @font can be filled back. (optional) + * @con_resize: resize the @vc console to @width x @height. @from_user is true + * when this change comes from the user space. + * @con_set_palette: sets the palette of the console @vc to @table (optional) * @con_scrolldelta: the contents of the console should be scrolled by @lines. * Invoked by user. (optional) * @con_set_origin: set origin (see &vc_data::vc_origin) of the @vc. If not * provided or returns false, the origin is set to * @vc->vc_screenbuf. (optional) + * @con_save_screen: save screen content into @vc->vc_screenbuf. Called e.g. + * upon entering graphics. (optional) + * @con_build_attr: build attributes based on @color, @intensity and other + * parameters. The result is used for both normal and erase + * characters. (optional) + * @con_invert_region: invert a region of length @count on @vc starting at @p. + * (optional) + * @con_debug_enter: prepare the console for the debugger. This includes, but + * is not limited to, unblanking the console, loading an + * appropriate palette, and allowing debugger generated output. + * (optional) + * @con_debug_leave: restore the console to its pre-debug state as closely as + * possible. (optional) */ struct consw { struct module *owner; @@ -96,15 +120,7 @@ struct consw { enum vc_intensity intensity, bool blink, bool underline, bool reverse, bool italic); void (*con_invert_region)(struct vc_data *vc, u16 *p, int count); - /* - * Prepare the console for the debugger. This includes, but is not - * limited to, unblanking the console, loading an appropriate - * palette, and allowing debugger generated output. - */ void (*con_debug_enter)(struct vc_data *vc); - /* - * Restore the console to its pre-debug state as closely as possible. - */ void (*con_debug_leave)(struct vc_data *vc); }; -- cgit v1.2.3 From 60234365aee22c9ac576491f787f20a17279d28e Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 22 Jan 2024 12:04:00 +0100 Subject: tty: vt: fix up kernel-doc selection.c and vt.c still uses tabs in the kernel-doc. This misrenders the functions in the output -- sphinx misinterprets the description. So remove these tabs, incl. those around dashes. 'enum' keyword is needed before enum names. Fix that. Superfluous \n after the comments are also removed. They are not completely faulty, but this unifies all the kernel-doc in the files. Finally fix up the cross references. Signed-off-by: "Jiri Slaby (SUSE)" Reviewed-by: Randy Dunlap Tested-by: Helge Deller # parisc STI console Link: https://lore.kernel.org/r/20240122110401.7289-47-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/selection.c | 30 ++++++++-------- drivers/tty/vt/vt.c | 86 +++++++++++++++++++++++----------------------- include/linux/console.h | 6 ++-- 3 files changed, 62 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 34ee09f5a8f4..564341f1a74f 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -73,10 +73,12 @@ sel_pos(int n, bool unicode) } /** - * clear_selection - remove current selection + * clear_selection - remove current selection * - * Remove the current selection highlight, if any from the console - * holding the selection. The caller must hold the console lock. + * Remove the current selection highlight, if any from the console holding the + * selection. + * + * Locking: The caller must hold the console lock. */ void clear_selection(void) { @@ -110,13 +112,13 @@ static inline int inword(const u32 c) } /** - * sel_loadlut() - load the LUT table - * @lut: user table + * sel_loadlut() - load the LUT table + * @lut: user table * - * Load the LUT table from user space. Make a temporary copy so a partial - * update doesn't make a mess. + * Load the LUT table from user space. Make a temporary copy so a partial + * update doesn't make a mess. * - * Locking: The console lock is acquired. + * Locking: The console lock is acquired. */ int sel_loadlut(u32 __user *lut) { @@ -173,14 +175,14 @@ static int store_utf8(u32 c, char *p) } /** - * set_selection_user - set the current selection. - * @sel: user selection info - * @tty: the console tty + * set_selection_user - set the current selection. + * @sel: user selection info + * @tty: the console tty * - * Invoked by the ioctl handle for the vt layer. + * Invoked by the ioctl handle for the vt layer. * - * The entire selection process is managed under the console_lock. It's - * a lot under the lock but its hardly a performance path + * Locking: The entire selection process is managed under the console_lock. + * It's a lot under the lock but its hardly a performance path. */ int set_selection_user(const struct tiocl_selection __user *sel, struct tty_struct *tty) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index bfe51af9a0f3..e9cdcf40fe14 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1115,21 +1115,20 @@ static inline int resize_screen(struct vc_data *vc, int width, int height, } /** - * vc_do_resize - resizing method for the tty - * @tty: tty being resized - * @vc: virtual console private data - * @cols: columns - * @lines: lines - * @from_user: invoked by a user? + * vc_do_resize - resizing method for the tty + * @tty: tty being resized + * @vc: virtual console private data + * @cols: columns + * @lines: lines + * @from_user: invoked by a user? * - * Resize a virtual console, clipping according to the actual constraints. - * If the caller passes a tty structure then update the termios winsize - * information and perform any necessary signal handling. + * Resize a virtual console, clipping according to the actual constraints. If + * the caller passes a tty structure then update the termios winsize + * information and perform any necessary signal handling. * - * Caller must hold the console semaphore. Takes the termios rwsem and - * ctrl.lock of the tty IFF a tty is passed. + * Locking: Caller must hold the console semaphore. Takes the termios rwsem and + * ctrl.lock of the tty IFF a tty is passed. */ - static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, unsigned int cols, unsigned int lines, bool from_user) { @@ -1277,16 +1276,17 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, } /** - * __vc_resize - resize a VT - * @vc: virtual console - * @cols: columns - * @rows: rows - * @from_user: invoked by a user? + * __vc_resize - resize a VT + * @vc: virtual console + * @cols: columns + * @rows: rows + * @from_user: invoked by a user? + * + * Resize a virtual console as seen from the console end of things. We use the + * common vc_do_resize() method to update the structures. * - * Resize a virtual console as seen from the console end of things. We - * use the common vc_do_resize methods to update the structures. The - * caller must hold the console sem to protect console internals and - * vc->port.tty + * Locking: The caller must hold the console sem to protect console internals + * and @vc->port.tty. */ int __vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows, bool from_user) @@ -1296,16 +1296,15 @@ int __vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows, EXPORT_SYMBOL(__vc_resize); /** - * vt_resize - resize a VT - * @tty: tty to resize - * @ws: winsize attributes + * vt_resize - resize a VT + * @tty: tty to resize + * @ws: winsize attributes * - * Resize a virtual terminal. This is called by the tty layer as we - * register our own handler for resizing. The mutual helper does all - * the actual work. + * Resize a virtual terminal. This is called by the tty layer as we register + * our own handler for resizing. The mutual helper does all the actual work. * - * Takes the console sem and the called methods then take the tty - * termios_rwsem and the tty ctrl.lock in that order. + * Locking: Takes the console sem and the called methods then take the tty + * termios_rwsem and the tty ctrl.lock in that order. */ static int vt_resize(struct tty_struct *tty, struct winsize *ws) { @@ -2633,8 +2632,8 @@ static inline int vc_translate_ascii(const struct vc_data *vc, int c) /** - * vc_sanitize_unicode - Replace invalid Unicode code points with U+FFFD - * @c: the received character, or U+FFFD for invalid sequences. + * vc_sanitize_unicode - Replace invalid Unicode code points with ``U+FFFD`` + * @c: the received character, or ``U+FFFD`` for invalid sequences. */ static inline int vc_sanitize_unicode(const int c) { @@ -2645,14 +2644,15 @@ static inline int vc_sanitize_unicode(const int c) } /** - * vc_translate_unicode - Combine UTF-8 into Unicode in @vc_utf_char + * vc_translate_unicode - Combine UTF-8 into Unicode in &vc_data.vc_utf_char * @vc: virtual console * @c: character to translate * @rescan: we return true if we need more (continuation) data * - * @vc_utf_char is the being-constructed unicode character. - * @vc_utf_count is the number of continuation bytes still expected to arrive. - * @vc_npar is the number of continuation bytes arrived so far. + * * &vc_data.vc_utf_char is the being-constructed unicode character. + * * &vc_data.vc_utf_count is the number of continuation bytes still expected to + * arrive. + * * &vc_data.vc_npar is the number of continuation bytes arrived so far. */ static int vc_translate_unicode(struct vc_data *vc, int c, bool *rescan) { @@ -3030,16 +3030,16 @@ struct tty_driver *console_driver; #ifdef CONFIG_VT_CONSOLE /** - * vt_kmsg_redirect() - Sets/gets the kernel message console - * @new: The new virtual terminal number or -1 if the console should stay - * unchanged + * vt_kmsg_redirect() - sets/gets the kernel message console + * @new: the new virtual terminal number or -1 if the console should stay + * unchanged * * By default, the kernel messages are always printed on the current virtual * console. However, the user may modify that default with the - * TIOCL_SETKMSGREDIRECT ioctl call. + * %TIOCL_SETKMSGREDIRECT ioctl call. * * This function sets the kernel message console to be @new. It returns the old - * virtual console number. The virtual terminal number 0 (both as parameter and + * virtual console number. The virtual terminal number %0 (both as parameter and * return value) means no redirection (i.e. always printed on the currently * active console). * @@ -3047,8 +3047,8 @@ struct tty_driver *console_driver; * value is not modified. You may use the macro vt_get_kmsg_redirect() in that * case to make the code more understandable. * - * When the kernel is compiled without CONFIG_VT_CONSOLE, this function ignores - * the parameter and always returns 0. + * When the kernel is compiled without %CONFIG_VT_CONSOLE, this function ignores + * the parameter and always returns %0. */ int vt_kmsg_redirect(int new) { @@ -3960,7 +3960,7 @@ static void vtconsole_deinit_device(struct con_driver *con) * RETURNS: zero if unbound, nonzero if bound * * Drivers can call this and if zero, they should release - * all resources allocated on con_startup() + * all resources allocated on &consw.con_startup() */ int con_is_bound(const struct consw *csw) { diff --git a/include/linux/console.h b/include/linux/console.h index f1a334ad268d..d6d8b7e6b93b 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -155,7 +155,7 @@ static inline void con_debug_leave(void) { } */ /** - * cons_flags - General console flags + * enum cons_flags - General console flags * @CON_PRINTBUFFER: Used by newly registered consoles to avoid duplicate * output of messages that were already shown by boot * consoles or read by userspace via syslog() syscall. @@ -236,7 +236,7 @@ struct nbcon_state { static_assert(sizeof(struct nbcon_state) <= sizeof(int)); /** - * nbcon_prio - console owner priority for nbcon consoles + * enum nbcon_prio - console owner priority for nbcon consoles * @NBCON_PRIO_NONE: Unused * @NBCON_PRIO_NORMAL: Normal (non-emergency) usage * @NBCON_PRIO_EMERGENCY: Emergency output (WARN/OOPS...) @@ -468,7 +468,7 @@ static inline bool console_is_registered(const struct console *con) * for_each_console() - Iterator over registered consoles * @con: struct console pointer used as loop cursor * - * The console list and the console->flags are immutable while iterating. + * The console list and the &console.flags are immutable while iterating. * * Requires console_list_lock to be held. */ -- cgit v1.2.3 From fed99212acae832607817b24fa589f8aaf03103f Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Mon, 22 Jan 2024 19:05:51 +0100 Subject: treewide, serdev: change receive_buf() return type to size_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit receive_buf() is called from ttyport_receive_buf() that expects values ">= 0" from serdev_controller_receive_buf(), change its return type from ssize_t to size_t. The need for this clean-up was noticed while fixing a warning, see commit 94d053942544 ("Bluetooth: btnxpuart: fix recv_buf() return value"). Changing the callback prototype to return an unsigned seems the best way to document the API and ensure that is properly used. GNSS drivers implementation of serdev receive_buf() callback return directly the return value of gnss_insert_raw(). gnss_insert_raw() returns a signed int, however this is not an issue since the value returned is always positive, because of the kfifo_in() implementation. gnss_insert_raw() could be changed to return also an unsigned, however this is not implemented here as request by the GNSS maintainer Johan Hovold. Suggested-by: Jiri Slaby Link: https://lore.kernel.org/all/087be419-ec6b-47ad-851a-5e1e3ea5cfcc@kernel.org/ Signed-off-by: Francesco Dolcini Acked-by: Jonathan Cameron #for-iio Reviewed-by: Johan Hovold Reviewed-by: Rob Herring Reviewed-by: Alex Elder Acked-by: Maximilian Luz # for platform/surface Acked-by: Lee Jones Acked-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240122180551.34429-1-francesco@dolcini.it Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btmtkuart.c | 4 ++-- drivers/bluetooth/btnxpuart.c | 4 ++-- drivers/bluetooth/hci_serdev.c | 4 ++-- drivers/gnss/serial.c | 2 +- drivers/gnss/sirf.c | 2 +- drivers/greybus/gb-beagleplay.c | 6 +++--- drivers/iio/chemical/pms7003.c | 4 ++-- drivers/iio/chemical/scd30_serial.c | 4 ++-- drivers/iio/chemical/sps30_serial.c | 4 ++-- drivers/iio/imu/bno055/bno055_ser_core.c | 4 ++-- drivers/mfd/rave-sp.c | 4 ++-- drivers/net/ethernet/qualcomm/qca_uart.c | 2 +- drivers/nfc/pn533/uart.c | 4 ++-- drivers/nfc/s3fwrn5/uart.c | 4 ++-- drivers/platform/chrome/cros_ec_uart.c | 4 ++-- drivers/platform/surface/aggregator/core.c | 4 ++-- drivers/tty/serdev/serdev-ttyport.c | 10 ++++------ include/linux/serdev.h | 8 ++++---- sound/drivers/serial-generic.c | 4 ++-- 19 files changed, 40 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index 3c84fcbda01a..e6bc4a73c9fc 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -383,8 +383,8 @@ static void btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count) } } -static ssize_t btmtkuart_receive_buf(struct serdev_device *serdev, - const u8 *data, size_t count) +static size_t btmtkuart_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t count) { struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev); diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 1d592ac413d1..056bef5b2919 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1264,8 +1264,8 @@ static const struct h4_recv_pkt nxp_recv_pkts[] = { { NXP_RECV_FW_REQ_V3, .recv = nxp_recv_fw_req_v3 }, }; -static ssize_t btnxpuart_receive_buf(struct serdev_device *serdev, - const u8 *data, size_t count) +static size_t btnxpuart_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t count) { struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev); diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c index 39c8b567da3c..a3c3beb2806d 100644 --- a/drivers/bluetooth/hci_serdev.c +++ b/drivers/bluetooth/hci_serdev.c @@ -271,8 +271,8 @@ static void hci_uart_write_wakeup(struct serdev_device *serdev) * * Return: number of processed bytes */ -static ssize_t hci_uart_receive_buf(struct serdev_device *serdev, - const u8 *data, size_t count) +static size_t hci_uart_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t count) { struct hci_uart *hu = serdev_device_get_drvdata(serdev); diff --git a/drivers/gnss/serial.c b/drivers/gnss/serial.c index baa956494e79..0e43bf6294f8 100644 --- a/drivers/gnss/serial.c +++ b/drivers/gnss/serial.c @@ -80,7 +80,7 @@ static const struct gnss_operations gnss_serial_gnss_ops = { .write_raw = gnss_serial_write_raw, }; -static ssize_t gnss_serial_receive_buf(struct serdev_device *serdev, +static size_t gnss_serial_receive_buf(struct serdev_device *serdev, const u8 *buf, size_t count) { struct gnss_serial *gserial = serdev_device_get_drvdata(serdev); diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index 6801a8fb2040..79375d14bbb6 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -160,7 +160,7 @@ static const struct gnss_operations sirf_gnss_ops = { .write_raw = sirf_write_raw, }; -static ssize_t sirf_receive_buf(struct serdev_device *serdev, +static size_t sirf_receive_buf(struct serdev_device *serdev, const u8 *buf, size_t count) { struct sirf_data *data = serdev_device_get_drvdata(serdev); diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index c3e90025064b..33f8fad70260 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -271,7 +271,7 @@ static void hdlc_rx_frame(struct gb_beagleplay *bg) } } -static ssize_t hdlc_rx(struct gb_beagleplay *bg, const u8 *data, size_t count) +static size_t hdlc_rx(struct gb_beagleplay *bg, const u8 *data, size_t count) { size_t i; u8 c; @@ -331,8 +331,8 @@ static void hdlc_deinit(struct gb_beagleplay *bg) flush_work(&bg->tx_work); } -static ssize_t gb_tty_receive(struct serdev_device *sd, const u8 *data, - size_t count) +static size_t gb_tty_receive(struct serdev_device *sd, const u8 *data, + size_t count) { struct gb_beagleplay *bg = serdev_device_get_drvdata(sd); diff --git a/drivers/iio/chemical/pms7003.c b/drivers/iio/chemical/pms7003.c index b5cf15a515d2..43025866d5b7 100644 --- a/drivers/iio/chemical/pms7003.c +++ b/drivers/iio/chemical/pms7003.c @@ -211,8 +211,8 @@ static bool pms7003_frame_is_okay(struct pms7003_frame *frame) return checksum == pms7003_calc_checksum(frame); } -static ssize_t pms7003_receive_buf(struct serdev_device *serdev, const u8 *buf, - size_t size) +static size_t pms7003_receive_buf(struct serdev_device *serdev, const u8 *buf, + size_t size) { struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev); struct pms7003_state *state = iio_priv(indio_dev); diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c index a47654591e55..2adb76dbb020 100644 --- a/drivers/iio/chemical/scd30_serial.c +++ b/drivers/iio/chemical/scd30_serial.c @@ -174,8 +174,8 @@ static int scd30_serdev_command(struct scd30_state *state, enum scd30_cmd cmd, u return 0; } -static ssize_t scd30_serdev_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t size) +static size_t scd30_serdev_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t size) { struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev); struct scd30_serdev_priv *priv; diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c index 3afa89f8acc3..a6dfbe28c914 100644 --- a/drivers/iio/chemical/sps30_serial.c +++ b/drivers/iio/chemical/sps30_serial.c @@ -210,8 +210,8 @@ static int sps30_serial_command(struct sps30_state *state, unsigned char cmd, return rsp_size; } -static ssize_t sps30_serial_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t size) +static size_t sps30_serial_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t size) { struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); struct sps30_serial_priv *priv; diff --git a/drivers/iio/imu/bno055/bno055_ser_core.c b/drivers/iio/imu/bno055/bno055_ser_core.c index 5677bdf4f846..694ff14a3aa2 100644 --- a/drivers/iio/imu/bno055/bno055_ser_core.c +++ b/drivers/iio/imu/bno055/bno055_ser_core.c @@ -378,8 +378,8 @@ static void bno055_ser_handle_rx(struct bno055_ser_priv *priv, int status) * Also, we assume to RX one pkt per time (i.e. the HW doesn't send anything * unless we require to AND we don't queue more than one request per time). */ -static ssize_t bno055_ser_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t size) +static size_t bno055_ser_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t size) { int status; struct bno055_ser_priv *priv = serdev_device_get_drvdata(serdev); diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c index 6ff84b2600c5..62a6613fb070 100644 --- a/drivers/mfd/rave-sp.c +++ b/drivers/mfd/rave-sp.c @@ -471,8 +471,8 @@ static void rave_sp_receive_frame(struct rave_sp *sp, rave_sp_receive_reply(sp, data, length); } -static ssize_t rave_sp_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t size) +static size_t rave_sp_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t size) { struct device *dev = &serdev->dev; struct rave_sp *sp = dev_get_drvdata(dev); diff --git a/drivers/net/ethernet/qualcomm/qca_uart.c b/drivers/net/ethernet/qualcomm/qca_uart.c index 223321897b96..20f50bde82ac 100644 --- a/drivers/net/ethernet/qualcomm/qca_uart.c +++ b/drivers/net/ethernet/qualcomm/qca_uart.c @@ -58,7 +58,7 @@ struct qcauart { unsigned char *tx_buffer; }; -static ssize_t +static size_t qca_tty_receive(struct serdev_device *serdev, const u8 *data, size_t count) { struct qcauart *qca = serdev_device_get_drvdata(serdev); diff --git a/drivers/nfc/pn533/uart.c b/drivers/nfc/pn533/uart.c index 2eb5978bd79e..cfbbe0713317 100644 --- a/drivers/nfc/pn533/uart.c +++ b/drivers/nfc/pn533/uart.c @@ -203,8 +203,8 @@ static int pn532_uart_rx_is_frame(struct sk_buff *skb) return 0; } -static ssize_t pn532_receive_buf(struct serdev_device *serdev, - const u8 *data, size_t count) +static size_t pn532_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t count) { struct pn532_uart_phy *dev = serdev_device_get_drvdata(serdev); size_t i; diff --git a/drivers/nfc/s3fwrn5/uart.c b/drivers/nfc/s3fwrn5/uart.c index 456d3947116c..9c09c10c2a46 100644 --- a/drivers/nfc/s3fwrn5/uart.c +++ b/drivers/nfc/s3fwrn5/uart.c @@ -51,8 +51,8 @@ static const struct s3fwrn5_phy_ops uart_phy_ops = { .write = s3fwrn82_uart_write, }; -static ssize_t s3fwrn82_uart_read(struct serdev_device *serdev, - const u8 *data, size_t count) +static size_t s3fwrn82_uart_read(struct serdev_device *serdev, + const u8 *data, size_t count) { struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev); size_t i; diff --git a/drivers/platform/chrome/cros_ec_uart.c b/drivers/platform/chrome/cros_ec_uart.c index 68d80559fddc..8ea867c2a01a 100644 --- a/drivers/platform/chrome/cros_ec_uart.c +++ b/drivers/platform/chrome/cros_ec_uart.c @@ -81,8 +81,8 @@ struct cros_ec_uart { struct response_info response; }; -static ssize_t cros_ec_uart_rx_bytes(struct serdev_device *serdev, - const u8 *data, size_t count) +static size_t cros_ec_uart_rx_bytes(struct serdev_device *serdev, + const u8 *data, size_t count) { struct ec_host_response *host_response; struct cros_ec_device *ec_dev = serdev_device_get_drvdata(serdev); diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c index 9591a28bc38a..ba550eaa06fc 100644 --- a/drivers/platform/surface/aggregator/core.c +++ b/drivers/platform/surface/aggregator/core.c @@ -227,8 +227,8 @@ EXPORT_SYMBOL_GPL(ssam_client_bind); /* -- Glue layer (serdev_device -> ssam_controller). ------------------------ */ -static ssize_t ssam_receive_buf(struct serdev_device *dev, const u8 *buf, - size_t n) +static size_t ssam_receive_buf(struct serdev_device *dev, const u8 *buf, + size_t n) { struct ssam_controller *ctrl; int ret; diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index e94e090cf0a1..3d7ae7fa5018 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -27,19 +27,17 @@ static size_t ttyport_receive_buf(struct tty_port *port, const u8 *cp, { struct serdev_controller *ctrl = port->client_data; struct serport *serport = serdev_controller_get_drvdata(ctrl); - int ret; + size_t ret; if (!test_bit(SERPORT_ACTIVE, &serport->flags)) return 0; ret = serdev_controller_receive_buf(ctrl, cp, count); - dev_WARN_ONCE(&ctrl->dev, ret < 0 || ret > count, - "receive_buf returns %d (count = %zu)\n", + dev_WARN_ONCE(&ctrl->dev, ret > count, + "receive_buf returns %zu (count = %zu)\n", ret, count); - if (ret < 0) - return 0; - else if (ret > count) + if (ret > count) return count; return ret; diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 3fab88ba265e..ff78efc1f60d 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -27,7 +27,7 @@ struct serdev_device; * not sleep. */ struct serdev_device_ops { - ssize_t (*receive_buf)(struct serdev_device *, const u8 *, size_t); + size_t (*receive_buf)(struct serdev_device *, const u8 *, size_t); void (*write_wakeup)(struct serdev_device *); }; @@ -185,9 +185,9 @@ static inline void serdev_controller_write_wakeup(struct serdev_controller *ctrl serdev->ops->write_wakeup(serdev); } -static inline ssize_t serdev_controller_receive_buf(struct serdev_controller *ctrl, - const u8 *data, - size_t count) +static inline size_t serdev_controller_receive_buf(struct serdev_controller *ctrl, + const u8 *data, + size_t count) { struct serdev_device *serdev = ctrl->serdev; diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c index d6e5aafd697c..36409a56c675 100644 --- a/sound/drivers/serial-generic.c +++ b/sound/drivers/serial-generic.c @@ -100,8 +100,8 @@ static void snd_serial_generic_write_wakeup(struct serdev_device *serdev) snd_serial_generic_tx_wakeup(drvdata); } -static ssize_t snd_serial_generic_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t count) +static size_t snd_serial_generic_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t count) { int ret; struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev); -- cgit v1.2.3 From 85725449f3e5faf385210d535f266430be71cebb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 10 Jan 2024 14:21:46 +0100 Subject: serial: 8250: Move hp300_setup_serial_console() to MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_SERIAL_8250_HP300=y and CONFIG_SERIAL_8250_CONSOLE=y (e.g. m68k/allyesconfig): drivers/tty/serial/8250/8250_hp300.c:91:12: error: no previous prototype for ‘hp300_setup_serial_console’ [-Werror=missing-prototypes] 91 | int __init hp300_setup_serial_console(void) | ^~~~~~~~~~~~~~~~~~~~~~~~~~ Fix this by moving the existing prototype in arch/m68k/hp300/config.c to , so it is visible to both caller and implementor. While at it, provide a dummy in case CONFIG_SERIAL_8250_CONSOLE is not enabled, to reduce #ifdef clutter in the caller. Exposed by commit 0fcb70851fbfea17 ("Makefile.extrawarn: turn on missing-prototypes globally"). Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/c17469f8e47b2ef49234a85a7a14882ddf374e41.1704892597.git.geert@linux-m68k.org Signed-off-by: Greg Kroah-Hartman --- arch/m68k/hp300/config.c | 6 +----- include/linux/serial_8250.h | 6 ++++++ 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/m68k/hp300/config.c b/arch/m68k/hp300/config.c index e4bd6913f50e..1a2739852351 100644 --- a/arch/m68k/hp300/config.c +++ b/arch/m68k/hp300/config.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -67,9 +68,6 @@ static char *hp300_models[] __initdata = { static char hp300_model_name[13] = "HP9000/"; extern void hp300_reset(void); -#ifdef CONFIG_SERIAL_8250_CONSOLE -extern int hp300_setup_serial_console(void) __init; -#endif int __init hp300_parse_bootinfo(const struct bi_record *record) { @@ -263,7 +261,5 @@ void __init config_hp300(void) } else { panic("Unknown HP9000 Model"); } -#ifdef CONFIG_SERIAL_8250_CONSOLE hp300_setup_serial_console(); -#endif } diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index be65de65fe61..fd59ed2cca53 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -210,6 +210,12 @@ int serial8250_console_exit(struct uart_port *port); void serial8250_set_isa_configurator(void (*v)(int port, struct uart_port *up, u32 *capabilities)); +#ifdef CONFIG_SERIAL_8250_CONSOLE +extern int hp300_setup_serial_console(void) __init; +#else +static inline int hp300_setup_serial_console(void) { return 0; } +#endif + #ifdef CONFIG_SERIAL_8250_RT288X int rt288x_setup(struct uart_port *p); int au_platform_setup(struct plat_serial8250_port *p); -- cgit v1.2.3 From 486676116f4852d4198690c2c98af060cd96ab83 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 12 Jan 2024 15:03:07 -0800 Subject: soc: qcom: geni-se: Add M_TX_FIFO_NOT_EMPTY bit definition According to the docs I have, bit 21 of the status register is asserted when the FIFO is _not_ empty. Add the definition. Signed-off-by: Douglas Anderson Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240112150307.1.I7dc0993c1e758a1efedd651e7e1670deb1b430fb@changeid Signed-off-by: Greg Kroah-Hartman --- include/linux/soc/qcom/geni-se.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h index 29e06905bc1f..0f038a1a0330 100644 --- a/include/linux/soc/qcom/geni-se.h +++ b/include/linux/soc/qcom/geni-se.h @@ -178,6 +178,7 @@ struct geni_se { #define M_GP_IRQ_3_EN BIT(12) #define M_GP_IRQ_4_EN BIT(13) #define M_GP_IRQ_5_EN BIT(14) +#define M_TX_FIFO_NOT_EMPTY_EN BIT(21) #define M_IO_DATA_DEASSERT_EN BIT(22) #define M_IO_DATA_ASSERT_EN BIT(23) #define M_RX_FIFO_RD_ERR_EN BIT(24) -- cgit v1.2.3 From 5c49b6a4a4bcf368f85cfe7a0e5ac3a7016f30fd Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 8 Jan 2024 14:41:02 +0100 Subject: vt: remove superfluous CONFIG_HW_CONSOLE The config HW_CONSOLE is always identical to the config VT and is not visible in the kernel's build menuconfig. So, CONFIG_HW_CONSOLE is redundant. Replace all references to CONFIG_HW_CONSOLE with CONFIG_VT and remove CONFIG_HW_CONSOLE. Signed-off-by: Lukas Bulwahn Reviewed-by: Javier Martinez Canillas Acked-by: Dmitry Torokhov Reviewed-by: Geert Uytterhoeven Acked-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20240108134102.601-1-lukas.bulwahn@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/m68k/amiga/config.c | 2 +- drivers/input/keyboard/amikbd.c | 6 +++--- drivers/tty/Kconfig | 7 +------ drivers/tty/vt/Makefile | 4 ++-- drivers/video/fbdev/tgafb.c | 2 +- include/linux/console.h | 2 +- lib/Kconfig.kgdb | 2 +- 7 files changed, 10 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c index 7791673e547b..99718f3dc686 100644 --- a/arch/m68k/amiga/config.c +++ b/arch/m68k/amiga/config.c @@ -846,6 +846,6 @@ static void amiga_get_hardware_list(struct seq_file *m) * The Amiga keyboard driver needs key_maps, but we cannot export it in * drivers/char/defkeymap.c, as it is autogenerated */ -#ifdef CONFIG_HW_CONSOLE +#ifdef CONFIG_VT EXPORT_SYMBOL_GPL(key_maps); #endif diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c index e305c44cd0aa..ecfae0b0b6aa 100644 --- a/drivers/input/keyboard/amikbd.c +++ b/drivers/input/keyboard/amikbd.c @@ -26,7 +26,7 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Amiga keyboard driver"); MODULE_LICENSE("GPL"); -#ifdef CONFIG_HW_CONSOLE +#ifdef CONFIG_VT static unsigned char amikbd_keycode[0x78] __initdata = { [0] = KEY_GRAVE, [1] = KEY_1, @@ -148,9 +148,9 @@ static void __init amikbd_init_console_keymaps(void) memcpy(key_maps[i], temp_map, sizeof(temp_map)); } } -#else /* !CONFIG_HW_CONSOLE */ +#else /* !CONFIG_VT */ static inline void amikbd_init_console_keymaps(void) {} -#endif /* !CONFIG_HW_CONSOLE */ +#endif /* !CONFIG_VT */ static const char *amikbd_messages[8] = { [0] = KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n", diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 5646dc6242cd..a45d423ad10f 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -75,14 +75,9 @@ config VT_CONSOLE_SLEEP def_bool y depends on VT_CONSOLE && PM_SLEEP -config HW_CONSOLE - bool - depends on VT - default y - config VT_HW_CONSOLE_BINDING bool "Support for binding and unbinding console drivers" - depends on HW_CONSOLE + depends on VT help The virtual terminal is the device that interacts with the physical terminal through console drivers. On these systems, at least one diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile index b3dfe9d5717e..2c8ce8b592ed 100644 --- a/drivers/tty/vt/Makefile +++ b/drivers/tty/vt/Makefile @@ -5,9 +5,9 @@ FONTMAPFILE = cp437.uni obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o \ - selection.o keyboard.o + selection.o keyboard.o \ + vt.o defkeymap.o obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o -obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o # Files generated that shall be removed upon make clean clean-files := consolemap_deftbl.c defkeymap.c diff --git a/drivers/video/fbdev/tgafb.c b/drivers/video/fbdev/tgafb.c index ca43774f3156..dccfc38cfbd5 100644 --- a/drivers/video/fbdev/tgafb.c +++ b/drivers/video/fbdev/tgafb.c @@ -380,7 +380,7 @@ tgafb_set_par(struct fb_info *info) BT463_LOAD_ADDR(par, 0x0000); TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG); -#ifdef CONFIG_HW_CONSOLE +#ifdef CONFIG_VT for (i = 0; i < 16; i++) { int j = color_table[i]; diff --git a/include/linux/console.h b/include/linux/console.h index d6d8b7e6b93b..31a8f5b85f5d 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -141,7 +141,7 @@ int con_is_bound(const struct consw *csw); int do_unregister_con_driver(const struct consw *csw); int do_take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); -#ifdef CONFIG_HW_CONSOLE +#ifdef CONFIG_VT void con_debug_enter(struct vc_data *vc); void con_debug_leave(void); #else diff --git a/lib/Kconfig.kgdb b/lib/Kconfig.kgdb index 3b9a44008433..b5c0e6576749 100644 --- a/lib/Kconfig.kgdb +++ b/lib/Kconfig.kgdb @@ -43,7 +43,7 @@ config KGDB_SERIAL_CONSOLE tristate "KGDB: use kgdb over the serial console" select CONSOLE_POLL select MAGIC_SYSRQ - depends on TTY && HW_CONSOLE + depends on TTY && VT default y help Share a serial console with kgdb. Sysrq-g must be used -- cgit v1.2.3 From 8ddf54a32111f6dbe06cd318af443c6545a6c037 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Fri, 5 Jan 2024 10:42:53 -0700 Subject: bus: mhi: host: Read PK HASH dynamically The OEM PK HASH registers in the BHI region are read once during firmware load (boot), cached, and displayed on demand via sysfs. This has a few problems - if firmware load is skipped, the registers will not be read and if the register values change over the life of the device the local cache will be out of sync. Qualcomm Cloud AI 100 can expose both these problems. It is possible for mhi_async_power_up() to be invoked while the device is in AMSS EE, which would bypass firmware loading. Also, Qualcomm Cloud AI 100 has 5 PK HASH slots which can be dynamically provisioned while the device is active, which would result in the values changing and users may want to know what keys are active. Address these concerns by reading the PK HASH registers on-demand during the sysfs read. This will result in showing the most current information. Signed-off-by: Jeffrey Hugo Reviewed-by: Pranjal Ramajor Asha Kanojiya Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20240105174253.863388-1-quic_jhugo@quicinc.com Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/boot.c | 11 +---------- drivers/bus/mhi/host/init.c | 16 ++++++++++++---- include/linux/mhi.h | 2 -- 3 files changed, 13 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index edc0ec5a0933..dedd29ca8db3 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -395,7 +395,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) void *buf; dma_addr_t dma_addr; size_t size, fw_sz; - int i, ret; + int ret; if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { dev_err(dev, "Device MHI is not in valid state\n"); @@ -408,15 +408,6 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) if (ret) dev_err(dev, "Could not capture serial number via BHI\n"); - for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) { - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), - &mhi_cntrl->oem_pk_hash[i]); - if (ret) { - dev_err(dev, "Could not capture OEM PK HASH via BHI\n"); - break; - } - } - /* wait for ready on pass through or any other execution environment */ if (!MHI_FW_LOAD_CAPABLE(mhi_cntrl->ee)) goto fw_load_ready_state; diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 8e5ec1a409b8..6d3b045ab259 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -98,11 +98,19 @@ static ssize_t oem_pk_hash_show(struct device *dev, { struct mhi_device *mhi_dev = to_mhi_device(dev); struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - int i, cnt = 0; + u32 hash_segment[MHI_MAX_OEM_PK_HASH_SEGMENTS]; + int i, cnt = 0, ret; - for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) - cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n", - i, mhi_cntrl->oem_pk_hash[i]); + for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++) { + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), &hash_segment[i]); + if (ret) { + dev_err(dev, "Could not capture OEM PK HASH\n"); + return ret; + } + } + + for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++) + cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n", i, hash_segment[i]); return cnt; } diff --git a/include/linux/mhi.h b/include/linux/mhi.h index d0f9b522f328..474d32cb0520 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -325,7 +325,6 @@ struct mhi_controller_config { * @major_version: MHI controller major revision number * @minor_version: MHI controller minor revision number * @serial_number: MHI controller serial number obtained from BHI - * @oem_pk_hash: MHI controller OEM PK Hash obtained from BHI * @mhi_event: MHI event ring configurations table * @mhi_cmd: MHI command ring configurations table * @mhi_ctxt: MHI device context, shared memory between host and device @@ -413,7 +412,6 @@ struct mhi_controller { u32 major_version; u32 minor_version; u32 serial_number; - u32 oem_pk_hash[MHI_MAX_OEM_PK_HASH_SEGMENTS]; struct mhi_event *mhi_event; struct mhi_cmd *mhi_cmd; -- cgit v1.2.3 From 1c9f2c7606afe149800986182638f636646dd824 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 10 Jan 2024 08:28:16 -1000 Subject: kernfs: Rearrange kernfs_node fields to reduce its size on 64bit Moving .flags and .mode right below .hash makes kernfs_node smaller by 8 bytes on 64bit. To avoid creating a hole from 8 bytes alignment on 32bit archs, .priv is moved below so that there are two 32bit pointers after the 64bit .id field. v2: Updated to avoid size increase on 32bit noticed by Geert. Signed-off-by: Tejun Heo Cc: Geert Uytterhoeven Link: https://lore.kernel.org/r/ZZ7hwA18nfmFjYpj@slm.duckdns.org Signed-off-by: Greg Kroah-Hartman --- include/linux/kernfs.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 99aaa050ccb7..82e1ce79a70c 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -206,22 +206,22 @@ struct kernfs_node { const void *ns; /* namespace tag */ unsigned int hash; /* ns + name hash */ + unsigned short flags; + umode_t mode; + union { struct kernfs_elem_dir dir; struct kernfs_elem_symlink symlink; struct kernfs_elem_attr attr; }; - void *priv; - /* * 64bit unique ID. On 64bit ino setups, id is the ino. On 32bit, * the low 32bits are ino and upper generation. */ u64 id; - unsigned short flags; - umode_t mode; + void *priv; struct kernfs_iattrs *iattr; }; -- cgit v1.2.3 From 4207b556e62f0a8915afc5da4c5d5ad915a253a5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 9 Jan 2024 11:48:04 -1000 Subject: kernfs: RCU protect kernfs_nodes and avoid kernfs_idr_lock in kernfs_find_and_get_node_by_id() The BPF helper bpf_cgroup_from_id() calls kernfs_find_and_get_node_by_id() which acquires kernfs_idr_lock, which is an non-raw non-IRQ-safe lock. This can lead to deadlocks as bpf_cgroup_from_id() can be called from any BPF programs including e.g. the ones that attach to functions which are holding the scheduler rq lock. Consider the following BPF program: SEC("fentry/__set_cpus_allowed_ptr_locked") int BPF_PROG(__set_cpus_allowed_ptr_locked, struct task_struct *p, struct affinity_context *affn_ctx, struct rq *rq, struct rq_flags *rf) { struct cgroup *cgrp = bpf_cgroup_from_id(p->cgroups->dfl_cgrp->kn->id); if (cgrp) { bpf_printk("%d[%s] in %s", p->pid, p->comm, cgrp->kn->name); bpf_cgroup_release(cgrp); } return 0; } __set_cpus_allowed_ptr_locked() is called with rq lock held and the above BPF program calls bpf_cgroup_from_id() within leading to the following lockdep warning: ===================================================== WARNING: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected 6.7.0-rc3-work-00053-g07124366a1d7-dirty #147 Not tainted ----------------------------------------------------- repro/1620 [HC0[0]:SC0[0]:HE0:SE1] is trying to acquire: ffffffff833b3688 (kernfs_idr_lock){+.+.}-{2:2}, at: kernfs_find_and_get_node_by_id+0x1e/0x70 and this task is already holding: ffff888237ced698 (&rq->__lock){-.-.}-{2:2}, at: task_rq_lock+0x4e/0xf0 which would create a new lock dependency: (&rq->__lock){-.-.}-{2:2} -> (kernfs_idr_lock){+.+.}-{2:2} ... Possible interrupt unsafe locking scenario: CPU0 CPU1 ---- ---- lock(kernfs_idr_lock); local_irq_disable(); lock(&rq->__lock); lock(kernfs_idr_lock); lock(&rq->__lock); *** DEADLOCK *** ... Call Trace: dump_stack_lvl+0x55/0x70 dump_stack+0x10/0x20 __lock_acquire+0x781/0x2a40 lock_acquire+0xbf/0x1f0 _raw_spin_lock+0x2f/0x40 kernfs_find_and_get_node_by_id+0x1e/0x70 cgroup_get_from_id+0x21/0x240 bpf_cgroup_from_id+0xe/0x20 bpf_prog_98652316e9337a5a___set_cpus_allowed_ptr_locked+0x96/0x11a bpf_trampoline_6442545632+0x4f/0x1000 __set_cpus_allowed_ptr_locked+0x5/0x5a0 sched_setaffinity+0x1b3/0x290 __x64_sys_sched_setaffinity+0x4f/0x60 do_syscall_64+0x40/0xe0 entry_SYSCALL_64_after_hwframe+0x46/0x4e Let's fix it by protecting kernfs_node and kernfs_root with RCU and making kernfs_find_and_get_node_by_id() acquire rcu_read_lock() instead of kernfs_idr_lock. This adds an rcu_head to kernfs_node making it larger by 16 bytes on 64bit. Combined with the preceding rearrange patch, the net increase is 8 bytes. Signed-off-by: Tejun Heo Cc: Andrea Righi Cc: Geert Uytterhoeven Link: https://lore.kernel.org/r/20240109214828.252092-4-tj@kernel.org Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 31 ++++++++++++++++++++----------- fs/kernfs/kernfs-internal.h | 2 ++ include/linux/kernfs.h | 2 ++ 3 files changed, 24 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index bce1d7ac95ca..458519e416fe 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -529,6 +529,20 @@ void kernfs_get(struct kernfs_node *kn) } EXPORT_SYMBOL_GPL(kernfs_get); +static void kernfs_free_rcu(struct rcu_head *rcu) +{ + struct kernfs_node *kn = container_of(rcu, struct kernfs_node, rcu); + + kfree_const(kn->name); + + if (kn->iattr) { + simple_xattrs_free(&kn->iattr->xattrs, NULL); + kmem_cache_free(kernfs_iattrs_cache, kn->iattr); + } + + kmem_cache_free(kernfs_node_cache, kn); +} + /** * kernfs_put - put a reference count on a kernfs_node * @kn: the target kernfs_node @@ -557,16 +571,11 @@ void kernfs_put(struct kernfs_node *kn) if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); - kfree_const(kn->name); - - if (kn->iattr) { - simple_xattrs_free(&kn->iattr->xattrs, NULL); - kmem_cache_free(kernfs_iattrs_cache, kn->iattr); - } spin_lock(&kernfs_idr_lock); idr_remove(&root->ino_idr, (u32)kernfs_ino(kn)); spin_unlock(&kernfs_idr_lock); - kmem_cache_free(kernfs_node_cache, kn); + + call_rcu(&kn->rcu, kernfs_free_rcu); kn = parent; if (kn) { @@ -575,7 +584,7 @@ void kernfs_put(struct kernfs_node *kn) } else { /* just released the root kn, free @root too */ idr_destroy(&root->ino_idr); - kfree(root); + kfree_rcu(root, rcu); } } EXPORT_SYMBOL_GPL(kernfs_put); @@ -715,7 +724,7 @@ struct kernfs_node *kernfs_find_and_get_node_by_id(struct kernfs_root *root, ino_t ino = kernfs_id_ino(id); u32 gen = kernfs_id_gen(id); - spin_lock(&kernfs_idr_lock); + rcu_read_lock(); kn = idr_find(&root->ino_idr, (u32)ino); if (!kn) @@ -739,10 +748,10 @@ struct kernfs_node *kernfs_find_and_get_node_by_id(struct kernfs_root *root, if (unlikely(!__kernfs_active(kn) || !atomic_inc_not_zero(&kn->count))) goto err_unlock; - spin_unlock(&kernfs_idr_lock); + rcu_read_unlock(); return kn; err_unlock: - spin_unlock(&kernfs_idr_lock); + rcu_read_unlock(); return NULL; } diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 237f2764b941..b42ee6547cdc 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -49,6 +49,8 @@ struct kernfs_root { struct rw_semaphore kernfs_rwsem; struct rw_semaphore kernfs_iattr_rwsem; struct rw_semaphore kernfs_supers_rwsem; + + struct rcu_head rcu; }; /* +1 to avoid triggering overflow warning when negating it */ diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 82e1ce79a70c..87c79d076d6d 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -223,6 +223,8 @@ struct kernfs_node { void *priv; struct kernfs_iattrs *iattr; + + struct rcu_head rcu; }; /* -- cgit v1.2.3 From 3a480d4bb5b1e1f09426223e68acaa90da32e384 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 5 Jan 2024 11:26:48 +0100 Subject: driver core: cpu: make cpu_subsys const Now that the driver core can properly handle constant struct bus_type, move the cpu_subsys variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/2024010548-crane-snooze-a871@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/base/cpu.c | 2 +- include/linux/cpu.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 47de0f140ba6..ac84854c85d7 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -366,7 +366,7 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env) } #endif -struct bus_type cpu_subsys = { +const struct bus_type cpu_subsys = { .name = "cpu", .dev_name = "cpu", .match = cpu_subsys_match, diff --git a/include/linux/cpu.h b/include/linux/cpu.h index dcb89c987164..0b993a140946 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -128,7 +128,7 @@ static inline void cpu_maps_update_done(void) static inline int add_cpu(unsigned int cpu) { return 0;} #endif /* CONFIG_SMP */ -extern struct bus_type cpu_subsys; +extern const struct bus_type cpu_subsys; extern int lockdep_is_cpus_held(void); -- cgit v1.2.3 From 47878b451216cf5e5a23bb3f931a9f932464e93d Mon Sep 17 00:00:00 2001 From: Adam Skladowski Date: Wed, 20 Dec 2023 11:38:49 +0100 Subject: dt-bindings: interconnect: Add Qualcomm MSM8909 DT bindings Add bindings for Qualcomm MSM8909 Network-On-Chip interconnect devices. [Stephan: Drop separate mm-snoc that exists downstream since it's actually the same NoC as SNoC in hardware] Signed-off-by: Adam Skladowski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20231220-icc-msm8909-v2-1-3b68bbed2891@kernkonzept.com Signed-off-by: Georgi Djakov --- .../devicetree/bindings/interconnect/qcom,rpm.yaml | 3 + include/dt-bindings/interconnect/qcom,msm8909.h | 93 ++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 include/dt-bindings/interconnect/qcom,msm8909.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml b/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml index 08c1c6b9d7cf..5aaa92a7cef7 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml @@ -23,6 +23,9 @@ properties: compatible: enum: + - qcom,msm8909-bimc + - qcom,msm8909-pcnoc + - qcom,msm8909-snoc - qcom,msm8916-bimc - qcom,msm8916-pcnoc - qcom,msm8916-snoc diff --git a/include/dt-bindings/interconnect/qcom,msm8909.h b/include/dt-bindings/interconnect/qcom,msm8909.h new file mode 100644 index 000000000000..76365d8aec21 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8909.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Qualcomm MSM8909 interconnect IDs + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8909_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8909_H + +/* BIMC fabric */ +#define MAS_APPS_PROC 0 +#define MAS_OXILI 1 +#define MAS_SNOC_BIMC_0 2 +#define MAS_SNOC_BIMC_1 3 +#define MAS_TCU_0 4 +#define MAS_TCU_1 5 +#define SLV_EBI 6 +#define SLV_BIMC_SNOC 7 + +/* PCNOC fabric */ +#define MAS_AUDIO 0 +#define MAS_SPDM 1 +#define MAS_DEHR 2 +#define MAS_QPIC 3 +#define MAS_BLSP_1 4 +#define MAS_USB_HS 5 +#define MAS_CRYPTO 6 +#define MAS_SDCC_1 7 +#define MAS_SDCC_2 8 +#define MAS_SNOC_PCNOC 9 +#define PCNOC_M_0 10 +#define PCNOC_M_1 11 +#define PCNOC_INT_0 12 +#define PCNOC_INT_1 13 +#define PCNOC_S_0 14 +#define PCNOC_S_1 15 +#define PCNOC_S_2 16 +#define PCNOC_S_3 17 +#define PCNOC_S_4 18 +#define PCNOC_S_5 19 +#define PCNOC_S_7 20 +#define SLV_TCSR 21 +#define SLV_SDCC_1 22 +#define SLV_BLSP_1 23 +#define SLV_CRYPTO_0_CFG 24 +#define SLV_MESSAGE_RAM 25 +#define SLV_PDM 26 +#define SLV_PRNG 27 +#define SLV_USB_HS 28 +#define SLV_QPIC 29 +#define SLV_SPDM 30 +#define SLV_SDCC_2 31 +#define SLV_AUDIO 32 +#define SLV_DEHR_CFG 33 +#define SLV_SNOC_CFG 34 +#define SLV_QDSS_CFG 35 +#define SLV_USB_PHY 36 +#define SLV_CAMERA_SS_CFG 37 +#define SLV_DISP_SS_CFG 38 +#define SLV_VENUS_CFG 39 +#define SLV_TLMM 40 +#define SLV_GPU_CFG 41 +#define SLV_IMEM_CFG 42 +#define SLV_BIMC_CFG 43 +#define SLV_PMIC_ARB 44 +#define SLV_TCU 45 +#define SLV_PCNOC_SNOC 46 + +/* SNOC fabric */ +#define MAS_QDSS_BAM 0 +#define MAS_BIMC_SNOC 1 +#define MAS_MDP 2 +#define MAS_PCNOC_SNOC 3 +#define MAS_VENUS 4 +#define MAS_VFE 5 +#define MAS_QDSS_ETR 6 +#define MM_INT_0 7 +#define MM_INT_1 8 +#define MM_INT_2 9 +#define MM_INT_BIMC 10 +#define QDSS_INT 11 +#define SNOC_INT_0 12 +#define SNOC_INT_1 13 +#define SNOC_INT_BIMC 14 +#define SLV_KPSS_AHB 15 +#define SLV_SNOC_BIMC_0 16 +#define SLV_SNOC_BIMC_1 17 +#define SLV_IMEM 18 +#define SLV_SNOC_PCNOC 19 +#define SLV_QDSS_STM 20 +#define SLV_CATS_0 21 +#define SLV_CATS_1 22 + +#endif /* __DT_BINDINGS_INTERCONNECT_QCOM_MSM8909_H */ -- cgit v1.2.3 From e92c9326743776ee2ffc6b933b7905c439872571 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 10 Jan 2024 15:16:26 +0200 Subject: dt-bindings: interconnect: Remove bogus interconnect nodes The downstream kernel has infrastructure for passing votes from different interconnect nodes onto different RPMh RSCs. This neither implemented, not is going to be implemented upstream (in favor of a different solution using ICC tags through the same node). Unfortunately, as it happens, meaningless (in the upstream context) parts of the vendor driver were copied, ending up causing havoc - since all "per-RSC" (in quotes because they all point to the main APPS one) BCMs defined within the driver overwrite the value in RPMh on every aggregation. To both avoid keeping bogus code around and possibly introducing impossible-to-track-down bugs (busses shutting down for no reason), get rid of the duplicated ICC node definitions. Signed-off-by: Konrad Dybcio Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240102-topic-x1e_fixes-v1-2-70723e08d5f6@linaro.org Signed-off-by: Georgi Djakov --- .../dt-bindings/interconnect/qcom,x1e80100-rpmh.h | 24 ---------------------- 1 file changed, 24 deletions(-) (limited to 'include') diff --git a/include/dt-bindings/interconnect/qcom,x1e80100-rpmh.h b/include/dt-bindings/interconnect/qcom,x1e80100-rpmh.h index a38c3472698a..7d9710881149 100644 --- a/include/dt-bindings/interconnect/qcom,x1e80100-rpmh.h +++ b/include/dt-bindings/interconnect/qcom,x1e80100-rpmh.h @@ -112,11 +112,6 @@ #define SLAVE_GEM_NOC_CNOC 12 #define SLAVE_LLCC 13 #define SLAVE_MEM_NOC_PCIE_SNOC 14 -#define MASTER_MNOC_HF_MEM_NOC_DISP 15 -#define MASTER_ANOC_PCIE_GEM_NOC_DISP 16 -#define SLAVE_LLCC_DISP 17 -#define MASTER_ANOC_PCIE_GEM_NOC_PCIE 18 -#define SLAVE_LLCC_PCIE 19 #define MASTER_LPIAON_NOC 0 #define SLAVE_LPASS_GEM_NOC 1 @@ -129,10 +124,6 @@ #define MASTER_LLCC 0 #define SLAVE_EBI1 1 -#define MASTER_LLCC_DISP 2 -#define SLAVE_EBI1_DISP 3 -#define MASTER_LLCC_PCIE 4 -#define SLAVE_EBI1_PCIE 5 #define MASTER_AV1_ENC 0 #define MASTER_CAMNOC_HF 1 @@ -147,8 +138,6 @@ #define SLAVE_MNOC_HF_MEM_NOC 10 #define SLAVE_MNOC_SF_MEM_NOC 11 #define SLAVE_SERVICE_MNOC 12 -#define MASTER_MDP_DISP 13 -#define SLAVE_MNOC_HF_MEM_NOC_DISP 14 #define MASTER_CDSP_PROC 0 #define SLAVE_CDSP_MEM_NOC 1 @@ -156,18 +145,11 @@ #define MASTER_PCIE_NORTH 0 #define MASTER_PCIE_SOUTH 1 #define SLAVE_ANOC_PCIE_GEM_NOC 2 -#define MASTER_PCIE_NORTH_PCIE 3 -#define MASTER_PCIE_SOUTH_PCIE 4 -#define SLAVE_ANOC_PCIE_GEM_NOC_PCIE 5 #define MASTER_PCIE_3 0 #define MASTER_PCIE_4 1 #define MASTER_PCIE_5 2 #define SLAVE_PCIE_NORTH 3 -#define MASTER_PCIE_3_PCIE 4 -#define MASTER_PCIE_4_PCIE 5 -#define MASTER_PCIE_5_PCIE 6 -#define SLAVE_PCIE_NORTH_PCIE 7 #define MASTER_PCIE_0 0 #define MASTER_PCIE_1 1 @@ -175,12 +157,6 @@ #define MASTER_PCIE_6A 3 #define MASTER_PCIE_6B 4 #define SLAVE_PCIE_SOUTH 5 -#define MASTER_PCIE_0_PCIE 6 -#define MASTER_PCIE_1_PCIE 7 -#define MASTER_PCIE_2_PCIE 8 -#define MASTER_PCIE_6A_PCIE 9 -#define MASTER_PCIE_6B_PCIE 10 -#define SLAVE_PCIE_SOUTH_PCIE 11 #define MASTER_A1NOC_SNOC 0 #define MASTER_A2NOC_SNOC 1 -- cgit v1.2.3 From dd95255d44c05c9977f962bf0f2afe5e11f8ab3e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 5 Jan 2024 13:33:32 +0100 Subject: coresight: make coresight_bustype const Now that the driver core can properly handle constant struct bus_type, move the coresight_bustype variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Suzuki K Poulose Cc: Mike Leach Cc: James Clark Cc: Leo Yan Cc: Alexander Shishkin Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/2024010531-tinfoil-avert-4a57@gregkh --- drivers/hwtracing/coresight/coresight-core.c | 2 +- include/linux/coresight.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index d7f0e231feb9..5dde597403b3 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1799,7 +1799,7 @@ done: } EXPORT_SYMBOL_GPL(coresight_alloc_device_name); -struct bus_type coresight_bustype = { +const struct bus_type coresight_bustype = { .name = "coresight", }; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a4cb7dd6ca23..e8b6e388218c 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -35,7 +35,7 @@ #define CORESIGHT_UNLOCK 0xc5acce55 -extern struct bus_type coresight_bustype; +extern const struct bus_type coresight_bustype; enum coresight_dev_type { CORESIGHT_DEV_TYPE_SINK, -- cgit v1.2.3 From a0fef3f05cf36338d471e8f35a9ced88a054d583 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 29 Jan 2024 15:40:33 +0000 Subject: coresight: Make language around "activated" sinks consistent Activated has the specific meaning of a sink that's selected for use by the user via sysfs. But comments in some code that's shared by Perf use the same word, so in those cases change them to just say "selected" instead. With selected implying either via Perf or "activated" via sysfs. coresight_get_enabled_sink() doesn't actually get an enabled sink, it only gets an activated one, so change that too. And change the activated variable name to include "sysfs" so it can't be confused as a general status. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20240129154050.569566-3-james.clark@arm.com Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-core.c | 51 +++++++++++----------------- drivers/hwtracing/coresight/coresight-priv.h | 2 -- include/linux/coresight.h | 14 ++++---- 3 files changed, 27 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 1dd3cd46fda4..37e59052e877 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -500,7 +500,7 @@ static void coresight_disable_path_from(struct list_head *path, /* * ETF devices are tricky... They can be a link or a sink, * depending on how they are configured. If an ETF has been - * "activated" it will be configured as a sink, otherwise + * selected as a sink it will be configured as a sink, otherwise * go ahead with the link configuration. */ if (type == CORESIGHT_DEV_TYPE_LINKSINK) @@ -578,7 +578,7 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode, /* * ETF devices are tricky... They can be a link or a sink, * depending on how they are configured. If an ETF has been - * "activated" it will be configured as a sink, otherwise + * selected as a sink it will be configured as a sink, otherwise * go ahead with the link configuration. */ if (type == CORESIGHT_DEV_TYPE_LINKSINK) @@ -635,15 +635,21 @@ struct coresight_device *coresight_get_sink(struct list_head *path) return csdev; } +/** + * coresight_find_activated_sysfs_sink - returns the first sink activated via + * sysfs using connection based search starting from the source reference. + * + * @csdev: Coresight source device reference + */ static struct coresight_device * -coresight_find_enabled_sink(struct coresight_device *csdev) +coresight_find_activated_sysfs_sink(struct coresight_device *csdev) { int i; struct coresight_device *sink = NULL; if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && - csdev->activated) + csdev->sysfs_sink_activated) return csdev; /* @@ -654,7 +660,7 @@ coresight_find_enabled_sink(struct coresight_device *csdev) child_dev = csdev->pdata->out_conns[i]->dest_dev; if (child_dev) - sink = coresight_find_enabled_sink(child_dev); + sink = coresight_find_activated_sysfs_sink(child_dev); if (sink) return sink; } @@ -662,21 +668,6 @@ coresight_find_enabled_sink(struct coresight_device *csdev) return NULL; } -/** - * coresight_get_enabled_sink - returns the first enabled sink using - * connection based search starting from the source reference - * - * @source: Coresight source device reference - */ -struct coresight_device * -coresight_get_enabled_sink(struct coresight_device *source) -{ - if (!source) - return NULL; - - return coresight_find_enabled_sink(source); -} - static int coresight_sink_by_id(struct device *dev, const void *data) { struct coresight_device *csdev = to_coresight_device(dev); @@ -810,11 +801,10 @@ static void coresight_drop_device(struct coresight_device *csdev) * @sink: The final sink we want in this path. * @path: The list to add devices to. * - * The tree of Coresight device is traversed until an activated sink is - * found. From there the sink is added to the list along with all the - * devices that led to that point - the end result is a list from source - * to sink. In that list the source is the first device and the sink the - * last one. + * The tree of Coresight device is traversed until @sink is found. + * From there the sink is added to the list along with all the devices that led + * to that point - the end result is a list from source to sink. In that list + * the source is the first device and the sink the last one. */ static int _coresight_build_path(struct coresight_device *csdev, struct coresight_device *sink, @@ -824,7 +814,7 @@ static int _coresight_build_path(struct coresight_device *csdev, bool found = false; struct coresight_node *node; - /* An activated sink has been found. Enqueue the element */ + /* The sink has been found. Enqueue the element */ if (csdev == sink) goto out; @@ -1145,7 +1135,7 @@ int coresight_enable(struct coresight_device *csdev) goto out; } - sink = coresight_get_enabled_sink(csdev); + sink = coresight_find_activated_sysfs_sink(csdev); if (!sink) { ret = -EINVAL; goto out; @@ -1259,7 +1249,7 @@ static ssize_t enable_sink_show(struct device *dev, { struct coresight_device *csdev = to_coresight_device(dev); - return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->activated); + return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->sysfs_sink_activated); } static ssize_t enable_sink_store(struct device *dev, @@ -1274,10 +1264,7 @@ static ssize_t enable_sink_store(struct device *dev, if (ret) return ret; - if (val) - csdev->activated = true; - else - csdev->activated = false; + csdev->sysfs_sink_activated = !!val; return size; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 30c051055e54..ced5be05a527 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -130,8 +130,6 @@ void coresight_disable_path(struct list_head *path); int coresight_enable_path(struct list_head *path, enum cs_mode mode, void *sink_data); struct coresight_device *coresight_get_sink(struct list_head *path); -struct coresight_device * -coresight_get_enabled_sink(struct coresight_device *source); struct coresight_device *coresight_get_sink_by_id(u32 id); struct coresight_device * coresight_find_default_sink(struct coresight_device *csdev); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e8b6e388218c..516ab45ff3c2 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -229,10 +229,12 @@ struct coresight_sysfs_link { * @refcnt: keep track of what is in use. * @orphan: true if the component has connections that haven't been linked. * @enable: 'true' if component is currently part of an active path. - * @activated: 'true' only if a _sink_ has been activated. A sink can be - * activated but not yet enabled. Enabling for a _sink_ - * happens when a source has been selected and a path is enabled - * from source to that sink. + * @sysfs_sink_activated: 'true' when a sink has been selected for use via sysfs + * by writing a 1 to the 'enable_sink' file. A sink can be + * activated but not yet enabled. Enabling for a _sink_ happens + * when a source has been selected and a path is enabled from + * source to that sink. A sink can also become enabled but not + * activated if it's used via Perf. * @ea: Device attribute for sink representation under PMU directory. * @def_sink: cached reference to default sink found for this device. * @nr_links: number of sysfs links created to other components from this @@ -252,9 +254,9 @@ struct coresight_device { struct device dev; atomic_t refcnt; bool orphan; - bool enable; /* true only if configured as part of a path */ + bool enable; /* sink specific fields */ - bool activated; /* true only if a sink is part of a path */ + bool sysfs_sink_activated; struct dev_ext_attribute *ea; struct coresight_device *def_sink; /* sysfs links between components */ -- cgit v1.2.3 From 9cae77cf23e317f31de036ced7ad2c261317dc76 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 29 Jan 2024 15:40:35 +0000 Subject: coresight: Move mode to struct coresight_device Most devices use mode, so move the mode definition out of the individual devices and up to the Coresight device. This will allow the core code to also know the mode which will be useful in a later commit. This also fixes the inconsistency of the documentation of the mode field on the individual device types. For example ETB10 had "this ETB is being used". Two devices didn't require an atomic mode type, so these usages have been converted to atomic_get() and atomic_set() only to make it compile, but the documentation of the field in struct coresight_device explains this type of usage. In the future, manipulation of the mode could be completely moved out of the individual devices and into the core code because it's almost all duplicate code, and this change is a step towards that. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20240129154050.569566-5-james.clark@arm.com Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-etb10.c | 18 +++++++-------- drivers/hwtracing/coresight/coresight-etm.h | 2 -- drivers/hwtracing/coresight/coresight-etm3x-core.c | 13 +++++------ .../hwtracing/coresight/coresight-etm3x-sysfs.c | 4 ++-- drivers/hwtracing/coresight/coresight-etm4x-core.c | 16 ++++++------- drivers/hwtracing/coresight/coresight-etm4x.h | 1 - drivers/hwtracing/coresight/coresight-stm.c | 20 ++++++++--------- drivers/hwtracing/coresight/coresight-tmc-core.c | 2 +- drivers/hwtracing/coresight/coresight-tmc-etf.c | 26 +++++++++++----------- drivers/hwtracing/coresight/coresight-tmc-etr.c | 20 ++++++++--------- drivers/hwtracing/coresight/coresight-tmc.h | 2 -- drivers/hwtracing/coresight/ultrasoc-smb.c | 13 ++++++----- drivers/hwtracing/coresight/ultrasoc-smb.h | 2 -- include/linux/coresight.h | 6 +++++ 14 files changed, 69 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 05e21cffd33b..145fb815ec1c 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -76,7 +76,6 @@ DEFINE_CORESIGHT_DEVLIST(etb_devs, "etb"); * @pid: Process ID of the process being monitored by the session * that is using this component. * @buf: area of memory where ETB buffer content gets sent. - * @mode: this ETB is being used. * @buffer_depth: size of @buf. * @trigger_cntr: amount of words to store after a trigger. */ @@ -89,7 +88,6 @@ struct etb_drvdata { local_t reading; pid_t pid; u8 *buf; - u32 mode; u32 buffer_depth; u32 trigger_cntr; }; @@ -150,17 +148,17 @@ static int etb_enable_sysfs(struct coresight_device *csdev) spin_lock_irqsave(&drvdata->spinlock, flags); /* Don't messup with perf sessions. */ - if (drvdata->mode == CS_MODE_PERF) { + if (local_read(&csdev->mode) == CS_MODE_PERF) { ret = -EBUSY; goto out; } - if (drvdata->mode == CS_MODE_DISABLED) { + if (local_read(&csdev->mode) == CS_MODE_DISABLED) { ret = etb_enable_hw(drvdata); if (ret) goto out; - drvdata->mode = CS_MODE_SYSFS; + local_set(&csdev->mode, CS_MODE_SYSFS); } atomic_inc(&csdev->refcnt); @@ -181,7 +179,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) spin_lock_irqsave(&drvdata->spinlock, flags); /* No need to continue if the component is already in used by sysFS. */ - if (drvdata->mode == CS_MODE_SYSFS) { + if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { ret = -EBUSY; goto out; } @@ -216,7 +214,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) if (!ret) { /* Associate with monitored process. */ drvdata->pid = pid; - drvdata->mode = CS_MODE_PERF; + local_set(&drvdata->csdev->mode, CS_MODE_PERF); atomic_inc(&csdev->refcnt); } @@ -362,11 +360,11 @@ static int etb_disable(struct coresight_device *csdev) } /* Complain if we (somehow) got out of sync */ - WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); + WARN_ON_ONCE(local_read(&csdev->mode) == CS_MODE_DISABLED); etb_disable_hw(drvdata); /* Dissociate from monitored process. */ drvdata->pid = -1; - drvdata->mode = CS_MODE_DISABLED; + local_set(&csdev->mode, CS_MODE_DISABLED); spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_dbg(&csdev->dev, "ETB disabled\n"); @@ -589,7 +587,7 @@ static void etb_dump(struct etb_drvdata *drvdata) unsigned long flags; spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->mode == CS_MODE_SYSFS) { + if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { __etb_disable_hw(drvdata); etb_dump_hw(drvdata); __etb_enable_hw(drvdata); diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 9a0d08b092ae..e02c3ea972c9 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -215,7 +215,6 @@ struct etm_config { * @port_size: port size as reported by ETMCR bit 4-6 and 21. * @arch: ETM/PTM version number. * @use_cpu14: true if management registers need to be accessed via CP14. - * @mode: this tracer's mode, i.e sysFS, Perf or disabled. * @sticky_enable: true if ETM base configuration has been done. * @boot_enable:true if we should start tracing at boot time. * @os_unlock: true if access to management registers is allowed. @@ -238,7 +237,6 @@ struct etm_drvdata { int port_size; u8 arch; bool use_cp14; - local_t mode; bool sticky_enable; bool boot_enable; bool os_unlock; diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index 74ae26911b8a..333d0a32c95c 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -559,7 +559,7 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event, u32 val; struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + val = local_cmpxchg(&drvdata->csdev->mode, CS_MODE_DISABLED, mode); /* Someone is already using the tracer */ if (val) @@ -578,7 +578,7 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event, /* The tracer didn't start */ if (ret) - local_set(&drvdata->mode, CS_MODE_DISABLED); + local_set(&drvdata->csdev->mode, CS_MODE_DISABLED); return ret; } @@ -672,14 +672,13 @@ static void etm_disable(struct coresight_device *csdev, struct perf_event *event) { enum cs_mode mode; - struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); /* * For as long as the tracer isn't disabled another entity can't * change its status. As such we can read the status here without * fearing it will change under us. */ - mode = local_read(&drvdata->mode); + mode = local_read(&csdev->mode); switch (mode) { case CS_MODE_DISABLED: @@ -696,7 +695,7 @@ static void etm_disable(struct coresight_device *csdev, } if (mode) - local_set(&drvdata->mode, CS_MODE_DISABLED); + local_set(&csdev->mode, CS_MODE_DISABLED); } static const struct coresight_ops_source etm_source_ops = { @@ -730,7 +729,7 @@ static int etm_starting_cpu(unsigned int cpu) etmdrvdata[cpu]->os_unlock = true; } - if (local_read(&etmdrvdata[cpu]->mode)) + if (local_read(&etmdrvdata[cpu]->csdev->mode)) etm_enable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); return 0; @@ -742,7 +741,7 @@ static int etm_dying_cpu(unsigned int cpu) return 0; spin_lock(&etmdrvdata[cpu]->spinlock); - if (local_read(&etmdrvdata[cpu]->mode)) + if (local_read(&etmdrvdata[cpu]->csdev->mode)) etm_disable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); return 0; diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 2f271b7fb048..6c8429c980b1 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -722,7 +722,7 @@ static ssize_t cntr_val_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!local_read(&drvdata->mode)) { + if (!local_read(&drvdata->csdev->mode)) { spin_lock(&drvdata->spinlock); for (i = 0; i < drvdata->nr_cntr; i++) ret += sprintf(buf, "counter %d: %x\n", @@ -941,7 +941,7 @@ static ssize_t seq_curr_state_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!local_read(&drvdata->mode)) { + if (!local_read(&drvdata->csdev->mode)) { val = config->seq_curr_state; goto out; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index c5ea808ea662..7ceff627cd95 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -841,9 +841,8 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event, { int ret; u32 val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + val = local_cmpxchg(&csdev->mode, CS_MODE_DISABLED, mode); /* Someone is already using the tracer */ if (val) @@ -862,7 +861,7 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event, /* The tracer didn't start */ if (ret) - local_set(&drvdata->mode, CS_MODE_DISABLED); + local_set(&csdev->mode, CS_MODE_DISABLED); return ret; } @@ -1004,14 +1003,13 @@ static void etm4_disable(struct coresight_device *csdev, struct perf_event *event) { enum cs_mode mode; - struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); /* * For as long as the tracer isn't disabled another entity can't * change its status. As such we can read the status here without * fearing it will change under us. */ - mode = local_read(&drvdata->mode); + mode = local_read(&csdev->mode); switch (mode) { case CS_MODE_DISABLED: @@ -1025,7 +1023,7 @@ static void etm4_disable(struct coresight_device *csdev, } if (mode) - local_set(&drvdata->mode, CS_MODE_DISABLED); + local_set(&csdev->mode, CS_MODE_DISABLED); } static const struct coresight_ops_source etm4_source_ops = { @@ -1663,7 +1661,7 @@ static int etm4_starting_cpu(unsigned int cpu) if (!etmdrvdata[cpu]->os_unlock) etm4_os_unlock(etmdrvdata[cpu]); - if (local_read(&etmdrvdata[cpu]->mode)) + if (local_read(&etmdrvdata[cpu]->csdev->mode)) etm4_enable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); return 0; @@ -1675,7 +1673,7 @@ static int etm4_dying_cpu(unsigned int cpu) return 0; spin_lock(&etmdrvdata[cpu]->spinlock); - if (local_read(&etmdrvdata[cpu]->mode)) + if (local_read(&etmdrvdata[cpu]->csdev->mode)) etm4_disable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); return 0; @@ -1833,7 +1831,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) * Save and restore the ETM Trace registers only if * the ETM is active. */ - if (local_read(&drvdata->mode) && drvdata->save_state) + if (local_read(&drvdata->csdev->mode) && drvdata->save_state) ret = __etm4_cpu_save(drvdata); return ret; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index da17b6c49b0f..9ea678bc2e8e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -1016,7 +1016,6 @@ struct etmv4_drvdata { void __iomem *base; struct coresight_device *csdev; spinlock_t spinlock; - local_t mode; int cpu; u8 arch; u8 nr_pe; diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 891ee64efed8..f7fc645ea7a4 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -119,7 +119,6 @@ DEFINE_CORESIGHT_DEVLIST(stm_devs, "stm"); * @spinlock: only one at a time pls. * @chs: the channels accociated to this STM. * @stm: structure associated to the generic STM interface. - * @mode: this tracer's mode (enum cs_mode), i.e sysFS, or disabled. * @traceid: value of the current ID for this component. * @write_bytes: Maximus bytes this STM can write at a time. * @stmsper: settings for register STMSPER. @@ -136,7 +135,6 @@ struct stm_drvdata { spinlock_t spinlock; struct channel_space chs; struct stm_data stm; - local_t mode; u8 traceid; u32 write_bytes; u32 stmsper; @@ -201,7 +199,7 @@ static int stm_enable(struct coresight_device *csdev, struct perf_event *event, if (mode != CS_MODE_SYSFS) return -EINVAL; - val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + val = local_cmpxchg(&csdev->mode, CS_MODE_DISABLED, mode); /* Someone is already using the tracer */ if (val) @@ -266,7 +264,7 @@ static void stm_disable(struct coresight_device *csdev, * change its status. As such we can read the status here without * fearing it will change under us. */ - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { + if (local_read(&csdev->mode) == CS_MODE_SYSFS) { spin_lock(&drvdata->spinlock); stm_disable_hw(drvdata); spin_unlock(&drvdata->spinlock); @@ -276,7 +274,7 @@ static void stm_disable(struct coresight_device *csdev, pm_runtime_put(csdev->dev.parent); - local_set(&drvdata->mode, CS_MODE_DISABLED); + local_set(&csdev->mode, CS_MODE_DISABLED); dev_dbg(&csdev->dev, "STM tracing disabled\n"); } } @@ -373,7 +371,7 @@ static long stm_generic_set_options(struct stm_data *stm_data, { struct stm_drvdata *drvdata = container_of(stm_data, struct stm_drvdata, stm); - if (!(drvdata && local_read(&drvdata->mode))) + if (!(drvdata && local_read(&drvdata->csdev->mode))) return -EINVAL; if (channel >= drvdata->numsp) @@ -408,7 +406,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, struct stm_drvdata, stm); unsigned int stm_flags; - if (!(drvdata && local_read(&drvdata->mode))) + if (!(drvdata && local_read(&drvdata->csdev->mode))) return -EACCES; if (channel >= drvdata->numsp) @@ -515,7 +513,7 @@ static ssize_t port_select_show(struct device *dev, struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val; - if (!local_read(&drvdata->mode)) { + if (!local_read(&drvdata->csdev->mode)) { val = drvdata->stmspscr; } else { spin_lock(&drvdata->spinlock); @@ -541,7 +539,7 @@ static ssize_t port_select_store(struct device *dev, spin_lock(&drvdata->spinlock); drvdata->stmspscr = val; - if (local_read(&drvdata->mode)) { + if (local_read(&drvdata->csdev->mode)) { CS_UNLOCK(drvdata->base); /* Process as per ARM's TRM recommendation */ stmsper = readl_relaxed(drvdata->base + STMSPER); @@ -562,7 +560,7 @@ static ssize_t port_enable_show(struct device *dev, struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val; - if (!local_read(&drvdata->mode)) { + if (!local_read(&drvdata->csdev->mode)) { val = drvdata->stmsper; } else { spin_lock(&drvdata->spinlock); @@ -588,7 +586,7 @@ static ssize_t port_enable_store(struct device *dev, spin_lock(&drvdata->spinlock); drvdata->stmsper = val; - if (local_read(&drvdata->mode)) { + if (local_read(&drvdata->csdev->mode)) { CS_UNLOCK(drvdata->base); writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER); CS_LOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 39bae35d4ffd..e8b2bbed047f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -558,7 +558,7 @@ static void tmc_shutdown(struct amba_device *adev) spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->mode == CS_MODE_DISABLED) + if (local_read(&drvdata->csdev->mode) == CS_MODE_DISABLED) goto out; if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 7406b65e2cdd..2a7e516052a2 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -89,7 +89,7 @@ static void __tmc_etb_disable_hw(struct tmc_drvdata *drvdata) * When operating in sysFS mode the content of the buffer needs to be * read before the TMC is disabled. */ - if (drvdata->mode == CS_MODE_SYSFS) + if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) tmc_etb_dump_hw(drvdata); tmc_disable_hw(drvdata); @@ -205,7 +205,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) * sink is already enabled no memory is needed and the HW need not be * touched. */ - if (drvdata->mode == CS_MODE_SYSFS) { + if (local_read(&csdev->mode) == CS_MODE_SYSFS) { atomic_inc(&csdev->refcnt); goto out; } @@ -228,7 +228,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) ret = tmc_etb_enable_hw(drvdata); if (!ret) { - drvdata->mode = CS_MODE_SYSFS; + local_set(&csdev->mode, CS_MODE_SYSFS); atomic_inc(&csdev->refcnt); } else { /* Free up the buffer if we failed to enable */ @@ -262,7 +262,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) * No need to continue if the ETB/ETF is already operated * from sysFS. */ - if (drvdata->mode == CS_MODE_SYSFS) { + if (local_read(&csdev->mode) == CS_MODE_SYSFS) { ret = -EBUSY; break; } @@ -292,7 +292,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) if (!ret) { /* Associate with monitored process. */ drvdata->pid = pid; - drvdata->mode = CS_MODE_PERF; + local_set(&csdev->mode, CS_MODE_PERF); atomic_inc(&csdev->refcnt); } } while (0); @@ -344,11 +344,11 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev) } /* Complain if we (somehow) got out of sync */ - WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); + WARN_ON_ONCE(local_read(&csdev->mode) == CS_MODE_DISABLED); tmc_etb_disable_hw(drvdata); /* Dissociate from monitored process. */ drvdata->pid = -1; - drvdata->mode = CS_MODE_DISABLED; + local_set(&csdev->mode, CS_MODE_DISABLED); spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -374,7 +374,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, if (atomic_read(&csdev->refcnt) == 0) { ret = tmc_etf_enable_hw(drvdata); if (!ret) { - drvdata->mode = CS_MODE_SYSFS; + local_set(&csdev->mode, CS_MODE_SYSFS); first_enable = true; } } @@ -403,7 +403,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, if (atomic_dec_return(&csdev->refcnt) == 0) { tmc_etf_disable_hw(drvdata); - drvdata->mode = CS_MODE_DISABLED; + local_set(&csdev->mode, CS_MODE_DISABLED); last_disable = true; } spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -483,7 +483,7 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, return 0; /* This shouldn't happen */ - if (WARN_ON_ONCE(drvdata->mode != CS_MODE_PERF)) + if (WARN_ON_ONCE(local_read(&csdev->mode) != CS_MODE_PERF)) return 0; spin_lock_irqsave(&drvdata->spinlock, flags); @@ -629,7 +629,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Don't interfere if operated from Perf */ - if (drvdata->mode == CS_MODE_PERF) { + if (local_read(&drvdata->csdev->mode) == CS_MODE_PERF) { ret = -EINVAL; goto out; } @@ -641,7 +641,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (drvdata->mode == CS_MODE_SYSFS) { + if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { /* There is no point in reading a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode != TMC_MODE_CIRCULAR_BUFFER) { @@ -673,7 +673,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); /* Re-enable the TMC if need be */ - if (drvdata->mode == CS_MODE_SYSFS) { + if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { /* There is no point in reading a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode != TMC_MODE_CIRCULAR_BUFFER) { diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index af02ba5d5f15..3dc989d4fcab 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1143,7 +1143,7 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata) * When operating in sysFS mode the content of the buffer needs to be * read before the TMC is disabled. */ - if (drvdata->mode == CS_MODE_SYSFS) + if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) tmc_etr_sync_sysfs_buf(drvdata); tmc_disable_hw(drvdata); @@ -1189,7 +1189,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) spin_lock_irqsave(&drvdata->spinlock, flags); } - if (drvdata->reading || drvdata->mode == CS_MODE_PERF) { + if (drvdata->reading || local_read(&csdev->mode) == CS_MODE_PERF) { ret = -EBUSY; goto out; } @@ -1230,14 +1230,14 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) * sink is already enabled no memory is needed and the HW need not be * touched, even if the buffer size has changed. */ - if (drvdata->mode == CS_MODE_SYSFS) { + if (local_read(&csdev->mode) == CS_MODE_SYSFS) { atomic_inc(&csdev->refcnt); goto out; } ret = tmc_etr_enable_hw(drvdata, sysfs_buf); if (!ret) { - drvdata->mode = CS_MODE_SYSFS; + local_set(&csdev->mode, CS_MODE_SYSFS); atomic_inc(&csdev->refcnt); } @@ -1652,7 +1652,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) spin_lock_irqsave(&drvdata->spinlock, flags); /* Don't use this sink if it is already claimed by sysFS */ - if (drvdata->mode == CS_MODE_SYSFS) { + if (local_read(&csdev->mode) == CS_MODE_SYSFS) { rc = -EBUSY; goto unlock_out; } @@ -1684,7 +1684,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) if (!rc) { /* Associate with monitored process. */ drvdata->pid = pid; - drvdata->mode = CS_MODE_PERF; + local_set(&csdev->mode, CS_MODE_PERF); drvdata->perf_buf = etr_perf->etr_buf; atomic_inc(&csdev->refcnt); } @@ -1725,11 +1725,11 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) } /* Complain if we (somehow) got out of sync */ - WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); + WARN_ON_ONCE(local_read(&csdev->mode) == CS_MODE_DISABLED); tmc_etr_disable_hw(drvdata); /* Dissociate from monitored process. */ drvdata->pid = -1; - drvdata->mode = CS_MODE_DISABLED; + local_set(&csdev->mode, CS_MODE_DISABLED); /* Reset perf specific data */ drvdata->perf_buf = NULL; @@ -1777,7 +1777,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) } /* Disable the TMC if we are trying to read from a running session. */ - if (drvdata->mode == CS_MODE_SYSFS) + if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) __tmc_etr_disable_hw(drvdata); drvdata->reading = true; @@ -1799,7 +1799,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); /* RE-enable the TMC if need be */ - if (drvdata->mode == CS_MODE_SYSFS) { + if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { /* * The trace run will continue with the same allocated trace * buffer. Since the tracer is still enabled drvdata::buf can't diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 8dcb426ac3e7..cef979c897e6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -178,7 +178,6 @@ struct etr_buf { * @size: trace buffer size for this TMC (common for all modes). * @max_burst_size: The maximum burst size that can be initiated by * TMC-ETR on AXI bus. - * @mode: how this TMC is being used. * @config_type: TMC variant, must be of type @tmc_config_type. * @memwidth: width of the memory interface databus, in bytes. * @trigger_cntr: amount of words to store after a trigger. @@ -203,7 +202,6 @@ struct tmc_drvdata { u32 len; u32 size; u32 max_burst_size; - u32 mode; enum tmc_config_type config_type; enum tmc_mem_intf_width memwidth; u32 trigger_cntr; diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index f0b6806202d1..bef633960173 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -207,11 +207,11 @@ static void smb_enable_sysfs(struct coresight_device *csdev) { struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); - if (drvdata->mode != CS_MODE_DISABLED) + if (local_read(&csdev->mode) != CS_MODE_DISABLED) return; smb_enable_hw(drvdata); - drvdata->mode = CS_MODE_SYSFS; + local_set(&csdev->mode, CS_MODE_SYSFS); } static int smb_enable_perf(struct coresight_device *csdev, void *data) @@ -234,7 +234,7 @@ static int smb_enable_perf(struct coresight_device *csdev, void *data) if (drvdata->pid == -1) { smb_enable_hw(drvdata); drvdata->pid = pid; - drvdata->mode = CS_MODE_PERF; + local_set(&csdev->mode, CS_MODE_PERF); } return 0; @@ -253,7 +253,8 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode, return -EBUSY; /* Do nothing, the SMB is already enabled as other mode */ - if (drvdata->mode != CS_MODE_DISABLED && drvdata->mode != mode) + if (local_read(&csdev->mode) != CS_MODE_DISABLED && + local_read(&csdev->mode) != mode) return -EBUSY; switch (mode) { @@ -289,13 +290,13 @@ static int smb_disable(struct coresight_device *csdev) return -EBUSY; /* Complain if we (somehow) got out of sync */ - WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); + WARN_ON_ONCE(local_read(&csdev->mode) == CS_MODE_DISABLED); smb_disable_hw(drvdata); /* Dissociate from the target process. */ drvdata->pid = -1; - drvdata->mode = CS_MODE_DISABLED; + local_set(&csdev->mode, CS_MODE_DISABLED); dev_dbg(&csdev->dev, "Ultrasoc SMB disabled\n"); return 0; diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.h b/drivers/hwtracing/coresight/ultrasoc-smb.h index 82a44c14a882..a91d39cfccb8 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.h +++ b/drivers/hwtracing/coresight/ultrasoc-smb.h @@ -109,7 +109,6 @@ struct smb_data_buffer { * @reading: Synchronise user space access to SMB buffer. * @pid: Process ID of the process being monitored by the * session that is using this component. - * @mode: How this SMB is being used, perf mode or sysfs mode. */ struct smb_drv_data { void __iomem *base; @@ -119,7 +118,6 @@ struct smb_drv_data { spinlock_t spinlock; bool reading; pid_t pid; - enum cs_mode mode; }; #endif diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 516ab45ff3c2..01f67862ea2f 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -226,6 +226,11 @@ struct coresight_sysfs_link { * by @coresight_ops. * @access: Device i/o access abstraction for this device. * @dev: The device entity associated to this component. + * @mode: This tracer's mode, i.e sysFS, Perf or disabled. This is + * actually an 'enum cs_mode', but is stored in an atomic type. + * This is always accessed through local_read() and local_set(), + * but wherever it's done from within the Coresight device's lock, + * a non-atomic read would also work. * @refcnt: keep track of what is in use. * @orphan: true if the component has connections that haven't been linked. * @enable: 'true' if component is currently part of an active path. @@ -252,6 +257,7 @@ struct coresight_device { const struct coresight_ops *ops; struct csdev_access access; struct device dev; + local_t mode; atomic_t refcnt; bool orphan; bool enable; -- cgit v1.2.3 From d5e83f97eb5669bfdd894ec980083f65517df2fb Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 29 Jan 2024 15:40:36 +0000 Subject: coresight: Remove the 'enable' field. 'enable', which probably should have been 'enabled', is only ever read in the core code in relation to controlling sources, and specifically only sources in sysfs mode. Confusingly it's not labelled as such and relying on it can be a source of bugs like the one fixed by commit 078dbba3f0c9 ("coresight: Fix crash when Perf and sysfs modes are used concurrently"). Most importantly, it can only be used when the coresight_mutex is held which is only done when enabling and disabling paths in sysfs mode, and not Perf mode. So to prevent its usage spreading and leaking out to other devices, remove it. It's use is equivalent to checking if the mode is currently sysfs, as due to the coresight_mutex lock, mode == CS_MODE_SYSFS can only become true or untrue when that lock is held, and when mode == CS_MODE_SYSFS the device is both enabled and in sysfs mode. The one place it was used outside of the core code is in TPDA, but that pattern is more appropriately represented using refcounts inside the device's own spinlock. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20240129154050.569566-6-james.clark@arm.com Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-core.c | 86 ++++++++++------------------ drivers/hwtracing/coresight/coresight-tpda.c | 12 +++- include/linux/coresight.h | 2 - 3 files changed, 38 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 5e93b52df105..b8d6520d47c9 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -279,29 +279,18 @@ EXPORT_SYMBOL_GPL(coresight_add_helper); static int coresight_enable_sink(struct coresight_device *csdev, enum cs_mode mode, void *data) { - int ret = sink_ops(csdev)->enable(csdev, mode, data); - - if (ret) - return ret; - - csdev->enable = true; - - return 0; + return sink_ops(csdev)->enable(csdev, mode, data); } static void coresight_disable_sink(struct coresight_device *csdev) { - int ret = sink_ops(csdev)->disable(csdev); - if (ret) - return; - csdev->enable = false; + sink_ops(csdev)->disable(csdev); } static int coresight_enable_link(struct coresight_device *csdev, struct coresight_device *parent, struct coresight_device *child) { - int ret = 0; int link_subtype; struct coresight_connection *inconn, *outconn; @@ -317,19 +306,13 @@ static int coresight_enable_link(struct coresight_device *csdev, if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && IS_ERR(outconn)) return PTR_ERR(outconn); - ret = link_ops(csdev)->enable(csdev, inconn, outconn); - if (!ret) - csdev->enable = true; - - return ret; + return link_ops(csdev)->enable(csdev, inconn, outconn); } static void coresight_disable_link(struct coresight_device *csdev, struct coresight_device *parent, struct coresight_device *child) { - int i; - int link_subtype; struct coresight_connection *inconn, *outconn; if (!parent || !child) @@ -337,26 +320,8 @@ static void coresight_disable_link(struct coresight_device *csdev, inconn = coresight_find_out_connection(parent, csdev); outconn = coresight_find_out_connection(csdev, child); - link_subtype = csdev->subtype.link_subtype; link_ops(csdev)->disable(csdev, inconn, outconn); - - if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) { - for (i = 0; i < csdev->pdata->nr_inconns; i++) - if (atomic_read(&csdev->pdata->in_conns[i]->dest_refcnt) != - 0) - return; - } else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) { - for (i = 0; i < csdev->pdata->nr_outconns; i++) - if (atomic_read(&csdev->pdata->out_conns[i]->src_refcnt) != - 0) - return; - } else { - if (atomic_read(&csdev->refcnt) != 0) - return; - } - - csdev->enable = false; } int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode, @@ -364,11 +329,16 @@ int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode, { int ret; - if (!csdev->enable) { + /* + * Comparison with CS_MODE_SYSFS works without taking any device + * specific spinlock because the truthyness of that comparison can only + * change with coresight_mutex held, which we already have here. + */ + lockdep_assert_held(&coresight_mutex); + if (local_read(&csdev->mode) != CS_MODE_SYSFS) { ret = source_ops(csdev)->enable(csdev, data, mode); if (ret) return ret; - csdev->enable = true; } atomic_inc(&csdev->refcnt); @@ -385,22 +355,12 @@ static bool coresight_is_helper(struct coresight_device *csdev) static int coresight_enable_helper(struct coresight_device *csdev, enum cs_mode mode, void *data) { - int ret = helper_ops(csdev)->enable(csdev, mode, data); - - if (ret) - return ret; - - csdev->enable = true; - return 0; + return helper_ops(csdev)->enable(csdev, mode, data); } static void coresight_disable_helper(struct coresight_device *csdev) { - int ret = helper_ops(csdev)->disable(csdev, NULL); - - if (ret) - return; - csdev->enable = false; + helper_ops(csdev)->disable(csdev, NULL); } static void coresight_disable_helpers(struct coresight_device *csdev) @@ -445,11 +405,15 @@ EXPORT_SYMBOL_GPL(coresight_disable_source); static bool coresight_disable_source_sysfs(struct coresight_device *csdev, void *data) { + lockdep_assert_held(&coresight_mutex); + if (local_read(&csdev->mode) != CS_MODE_SYSFS) + return false; + if (atomic_dec_return(&csdev->refcnt) == 0) { coresight_disable_source(csdev, data); - csdev->enable = false; + return true; } - return !csdev->enable; + return false; } /* @@ -1097,7 +1061,13 @@ int coresight_enable(struct coresight_device *csdev) if (ret) goto out; - if (csdev->enable) { + /* + * mode == SYSFS implies that it's already enabled. Don't look at the + * refcount to determine this because we don't claim the source until + * coresight_enable_source() so can still race with Perf mode which + * doesn't hold coresight_mutex. + */ + if (local_read(&csdev->mode) == CS_MODE_SYSFS) { /* * There could be multiple applications driving the software * source. So keep the refcount for each such user when the @@ -1183,7 +1153,7 @@ void coresight_disable(struct coresight_device *csdev) if (ret) goto out; - if (!csdev->enable || !coresight_disable_source_sysfs(csdev, NULL)) + if (!coresight_disable_source_sysfs(csdev, NULL)) goto out; switch (csdev->subtype.source_subtype) { @@ -1249,7 +1219,9 @@ static ssize_t enable_source_show(struct device *dev, { struct coresight_device *csdev = to_coresight_device(dev); - return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->enable); + guard(mutex)(&coresight_mutex); + return scnprintf(buf, PAGE_SIZE, "%u\n", + local_read(&csdev->mode) == CS_MODE_SYSFS); } static ssize_t enable_source_store(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index 4ac954f4bc13..c813ec427b8d 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -148,7 +148,11 @@ static int __tpda_enable(struct tpda_drvdata *drvdata, int port) CS_UNLOCK(drvdata->base); - if (!drvdata->csdev->enable) + /* + * Only do pre-port enable for first port that calls enable when the + * device's main refcount is still 0 + */ + if (!atomic_read(&drvdata->csdev->refcnt)) tpda_enable_pre_port(drvdata); ret = tpda_enable_port(drvdata, port); @@ -169,6 +173,7 @@ static int tpda_enable(struct coresight_device *csdev, ret = __tpda_enable(drvdata, in->dest_port); if (!ret) { atomic_inc(&in->dest_refcnt); + atomic_inc(&csdev->refcnt); dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port); } } @@ -197,9 +202,10 @@ static void tpda_disable(struct coresight_device *csdev, struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); spin_lock(&drvdata->spinlock); - if (atomic_dec_return(&in->dest_refcnt) == 0) + if (atomic_dec_return(&in->dest_refcnt) == 0) { __tpda_disable(drvdata, in->dest_port); - + atomic_dec(&csdev->refcnt); + } spin_unlock(&drvdata->spinlock); dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 01f67862ea2f..d1fd7070099c 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -233,7 +233,6 @@ struct coresight_sysfs_link { * a non-atomic read would also work. * @refcnt: keep track of what is in use. * @orphan: true if the component has connections that haven't been linked. - * @enable: 'true' if component is currently part of an active path. * @sysfs_sink_activated: 'true' when a sink has been selected for use via sysfs * by writing a 1 to the 'enable_sink' file. A sink can be * activated but not yet enabled. Enabling for a _sink_ happens @@ -260,7 +259,6 @@ struct coresight_device { local_t mode; atomic_t refcnt; bool orphan; - bool enable; /* sink specific fields */ bool sysfs_sink_activated; struct dev_ext_attribute *ea; -- cgit v1.2.3 From 1f5149c7751c50aba1a871143ffa6cb36af3fb49 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 29 Jan 2024 15:40:37 +0000 Subject: coresight: Move all sysfs code to sysfs file At the moment the core file contains both sysfs functionality and core functionality, while the Perf mode is in a separate file in coresight-etm-perf.c Many of the functions have ambiguous names like coresight_enable_source() which actually only work in relation to the sysfs mode. To avoid further confusion, move everything that isn't core functionality into the sysfs file and append _sysfs to the ambiguous functions. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20240129154050.569566-7-james.clark@arm.com Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-core.c | 394 +-------------------- drivers/hwtracing/coresight/coresight-etm3x-core.c | 4 +- drivers/hwtracing/coresight/coresight-etm4x-core.c | 4 +- drivers/hwtracing/coresight/coresight-priv.h | 5 +- drivers/hwtracing/coresight/coresight-stm.c | 4 +- drivers/hwtracing/coresight/coresight-sysfs.c | 390 ++++++++++++++++++++ include/linux/coresight.h | 8 +- 7 files changed, 407 insertions(+), 402 deletions(-) (limited to 'include') diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index b8d6520d47c9..b83613e34289 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -25,15 +24,12 @@ #include "coresight-priv.h" #include "coresight-syscfg.h" -static DEFINE_MUTEX(coresight_mutex); -static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); - /* - * Use IDR to map the hash of the source's device name - * to the pointer of path for the source. The idr is for - * the sources which aren't associated with CPU. + * Mutex used to lock all sysfs enable and disable actions and loading and + * unloading devices by the Coresight core. */ -static DEFINE_IDR(path_idr); +DEFINE_MUTEX(coresight_mutex); +static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); /** * struct coresight_node - elements of a path, from source to sink @@ -45,12 +41,6 @@ struct coresight_node { struct list_head link; }; -/* - * When operating Coresight drivers from the sysFS interface, only a single - * path can exist from a tracer (associated to a CPU) to a sink. - */ -static DEFINE_PER_CPU(struct list_head *, tracer_path); - /* * When losing synchronisation a new barrier packet needs to be inserted at the * beginning of the data collected in a buffer. That way the decoder knows that @@ -61,34 +51,6 @@ EXPORT_SYMBOL_GPL(coresight_barrier_pkt); static const struct cti_assoc_op *cti_assoc_ops; -ssize_t coresight_simple_show_pair(struct device *_dev, - struct device_attribute *attr, char *buf) -{ - struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); - struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr); - u64 val; - - pm_runtime_get_sync(_dev->parent); - val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off); - pm_runtime_put_sync(_dev->parent); - return sysfs_emit(buf, "0x%llx\n", val); -} -EXPORT_SYMBOL_GPL(coresight_simple_show_pair); - -ssize_t coresight_simple_show32(struct device *_dev, - struct device_attribute *attr, char *buf) -{ - struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); - struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr); - u64 val; - - pm_runtime_get_sync(_dev->parent); - val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off); - pm_runtime_put_sync(_dev->parent); - return sysfs_emit(buf, "0x%llx\n", val); -} -EXPORT_SYMBOL_GPL(coresight_simple_show32); - void coresight_set_cti_ops(const struct cti_assoc_op *cti_op) { cti_assoc_ops = cti_op; @@ -324,29 +286,6 @@ static void coresight_disable_link(struct coresight_device *csdev, link_ops(csdev)->disable(csdev, inconn, outconn); } -int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode, - void *data) -{ - int ret; - - /* - * Comparison with CS_MODE_SYSFS works without taking any device - * specific spinlock because the truthyness of that comparison can only - * change with coresight_mutex held, which we already have here. - */ - lockdep_assert_held(&coresight_mutex); - if (local_read(&csdev->mode) != CS_MODE_SYSFS) { - ret = source_ops(csdev)->enable(csdev, data, mode); - if (ret) - return ret; - } - - atomic_inc(&csdev->refcnt); - - return 0; -} -EXPORT_SYMBOL_GPL(coresight_enable_source); - static bool coresight_is_helper(struct coresight_device *csdev) { return csdev->type == CORESIGHT_DEV_TYPE_HELPER; @@ -392,30 +331,6 @@ void coresight_disable_source(struct coresight_device *csdev, void *data) } EXPORT_SYMBOL_GPL(coresight_disable_source); -/** - * coresight_disable_source_sysfs - Drop the reference count by 1 and disable - * the device if there are no users left. - * - * @csdev: The coresight device to disable - * @data: Opaque data to pass on to the disable function of the source device. - * For example in perf mode this is a pointer to the struct perf_event. - * - * Returns true if the device has been disabled. - */ -static bool coresight_disable_source_sysfs(struct coresight_device *csdev, - void *data) -{ - lockdep_assert_held(&coresight_mutex); - if (local_read(&csdev->mode) != CS_MODE_SYSFS) - return false; - - if (atomic_dec_return(&csdev->refcnt) == 0) { - coresight_disable_source(csdev, data); - return true; - } - return false; -} - /* * coresight_disable_path_from : Disable components in the given path beyond * @nd in the list. If @nd is NULL, all the components, except the SOURCE are @@ -572,39 +487,6 @@ struct coresight_device *coresight_get_sink(struct list_head *path) return csdev; } -/** - * coresight_find_activated_sysfs_sink - returns the first sink activated via - * sysfs using connection based search starting from the source reference. - * - * @csdev: Coresight source device reference - */ -static struct coresight_device * -coresight_find_activated_sysfs_sink(struct coresight_device *csdev) -{ - int i; - struct coresight_device *sink = NULL; - - if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || - csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && - csdev->sysfs_sink_activated) - return csdev; - - /* - * Recursively explore each port found on this element. - */ - for (i = 0; i < csdev->pdata->nr_outconns; i++) { - struct coresight_device *child_dev; - - child_dev = csdev->pdata->out_conns[i]->dest_dev; - if (child_dev) - sink = coresight_find_activated_sysfs_sink(child_dev); - if (sink) - return sink; - } - - return NULL; -} - static int coresight_sink_by_id(struct device *dev, const void *data) { struct coresight_device *csdev = to_coresight_device(dev); @@ -1015,274 +897,6 @@ static void coresight_clear_default_sink(struct coresight_device *csdev) } } -/** coresight_validate_source - make sure a source has the right credentials - * @csdev: the device structure for a source. - * @function: the function this was called from. - * - * Assumes the coresight_mutex is held. - */ -static int coresight_validate_source(struct coresight_device *csdev, - const char *function) -{ - u32 type, subtype; - - type = csdev->type; - subtype = csdev->subtype.source_subtype; - - if (type != CORESIGHT_DEV_TYPE_SOURCE) { - dev_err(&csdev->dev, "wrong device type in %s\n", function); - return -EINVAL; - } - - if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC && - subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE && - subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM && - subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) { - dev_err(&csdev->dev, "wrong device subtype in %s\n", function); - return -EINVAL; - } - - return 0; -} - -int coresight_enable(struct coresight_device *csdev) -{ - int cpu, ret = 0; - struct coresight_device *sink; - struct list_head *path; - enum coresight_dev_subtype_source subtype; - u32 hash; - - subtype = csdev->subtype.source_subtype; - - mutex_lock(&coresight_mutex); - - ret = coresight_validate_source(csdev, __func__); - if (ret) - goto out; - - /* - * mode == SYSFS implies that it's already enabled. Don't look at the - * refcount to determine this because we don't claim the source until - * coresight_enable_source() so can still race with Perf mode which - * doesn't hold coresight_mutex. - */ - if (local_read(&csdev->mode) == CS_MODE_SYSFS) { - /* - * There could be multiple applications driving the software - * source. So keep the refcount for each such user when the - * source is already enabled. - */ - if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) - atomic_inc(&csdev->refcnt); - goto out; - } - - sink = coresight_find_activated_sysfs_sink(csdev); - if (!sink) { - ret = -EINVAL; - goto out; - } - - path = coresight_build_path(csdev, sink); - if (IS_ERR(path)) { - pr_err("building path(s) failed\n"); - ret = PTR_ERR(path); - goto out; - } - - ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); - if (ret) - goto err_path; - - ret = coresight_enable_source(csdev, CS_MODE_SYSFS, NULL); - if (ret) - goto err_source; - - switch (subtype) { - case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: - /* - * When working from sysFS it is important to keep track - * of the paths that were created so that they can be - * undone in 'coresight_disable()'. Since there can only - * be a single session per tracer (when working from sysFS) - * a per-cpu variable will do just fine. - */ - cpu = source_ops(csdev)->cpu_id(csdev); - per_cpu(tracer_path, cpu) = path; - break; - case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: - case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: - case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: - /* - * Use the hash of source's device name as ID - * and map the ID to the pointer of the path. - */ - hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); - ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); - if (ret) - goto err_source; - break; - default: - /* We can't be here */ - break; - } - -out: - mutex_unlock(&coresight_mutex); - return ret; - -err_source: - coresight_disable_path(path); - -err_path: - coresight_release_path(path); - goto out; -} -EXPORT_SYMBOL_GPL(coresight_enable); - -void coresight_disable(struct coresight_device *csdev) -{ - int cpu, ret; - struct list_head *path = NULL; - u32 hash; - - mutex_lock(&coresight_mutex); - - ret = coresight_validate_source(csdev, __func__); - if (ret) - goto out; - - if (!coresight_disable_source_sysfs(csdev, NULL)) - goto out; - - switch (csdev->subtype.source_subtype) { - case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: - cpu = source_ops(csdev)->cpu_id(csdev); - path = per_cpu(tracer_path, cpu); - per_cpu(tracer_path, cpu) = NULL; - break; - case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: - case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: - case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: - hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); - /* Find the path by the hash. */ - path = idr_find(&path_idr, hash); - if (path == NULL) { - pr_err("Path is not found for %s\n", dev_name(&csdev->dev)); - goto out; - } - idr_remove(&path_idr, hash); - break; - default: - /* We can't be here */ - break; - } - - coresight_disable_path(path); - coresight_release_path(path); - -out: - mutex_unlock(&coresight_mutex); -} -EXPORT_SYMBOL_GPL(coresight_disable); - -static ssize_t enable_sink_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct coresight_device *csdev = to_coresight_device(dev); - - return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->sysfs_sink_activated); -} - -static ssize_t enable_sink_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct coresight_device *csdev = to_coresight_device(dev); - - ret = kstrtoul(buf, 10, &val); - if (ret) - return ret; - - csdev->sysfs_sink_activated = !!val; - - return size; - -} -static DEVICE_ATTR_RW(enable_sink); - -static ssize_t enable_source_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct coresight_device *csdev = to_coresight_device(dev); - - guard(mutex)(&coresight_mutex); - return scnprintf(buf, PAGE_SIZE, "%u\n", - local_read(&csdev->mode) == CS_MODE_SYSFS); -} - -static ssize_t enable_source_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret = 0; - unsigned long val; - struct coresight_device *csdev = to_coresight_device(dev); - - ret = kstrtoul(buf, 10, &val); - if (ret) - return ret; - - if (val) { - ret = coresight_enable(csdev); - if (ret) - return ret; - } else { - coresight_disable(csdev); - } - - return size; -} -static DEVICE_ATTR_RW(enable_source); - -static struct attribute *coresight_sink_attrs[] = { - &dev_attr_enable_sink.attr, - NULL, -}; -ATTRIBUTE_GROUPS(coresight_sink); - -static struct attribute *coresight_source_attrs[] = { - &dev_attr_enable_source.attr, - NULL, -}; -ATTRIBUTE_GROUPS(coresight_source); - -static struct device_type coresight_dev_type[] = { - { - .name = "sink", - .groups = coresight_sink_groups, - }, - { - .name = "link", - }, - { - .name = "linksink", - .groups = coresight_sink_groups, - }, - { - .name = "source", - .groups = coresight_source_groups, - }, - { - .name = "helper", - } -}; -/* Ensure the enum matches the names and groups */ -static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX); - static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index 333d0a32c95c..de53c3e8db29 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -714,7 +714,7 @@ static int etm_online_cpu(unsigned int cpu) return 0; if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable) - coresight_enable(etmdrvdata[cpu]->csdev); + coresight_enable_sysfs(etmdrvdata[cpu]->csdev); return 0; } @@ -924,7 +924,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) dev_info(&drvdata->csdev->dev, "%s initialized\n", (char *)coresight_get_uci_data(id)); if (boot_enable) { - coresight_enable(drvdata->csdev); + coresight_enable_sysfs(drvdata->csdev); drvdata->boot_enable = true; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 7ceff627cd95..1c64b54459d7 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1648,7 +1648,7 @@ static int etm4_online_cpu(unsigned int cpu) return etm4_probe_cpu(cpu); if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable) - coresight_enable(etmdrvdata[cpu]->csdev); + coresight_enable_sysfs(etmdrvdata[cpu]->csdev); return 0; } @@ -2096,7 +2096,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) drvdata->cpu, type_name, major, minor); if (boot_enable) { - coresight_enable(drvdata->csdev); + coresight_enable_sysfs(drvdata->csdev); drvdata->boot_enable = true; } diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index ced5be05a527..eb365236f9a9 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -12,6 +12,9 @@ #include #include +extern struct mutex coresight_mutex; +extern struct device_type coresight_dev_type[]; + /* * Coresight management registers (0xf00-0xfcc) * 0xfa0 - 0xfa4: Management registers in PFTv1.0 @@ -229,8 +232,6 @@ void coresight_add_helper(struct coresight_device *csdev, void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev); struct coresight_device *coresight_get_percpu_sink(int cpu); -int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode, - void *data); void coresight_disable_source(struct coresight_device *csdev, void *data); #endif diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index f7fc645ea7a4..b2ccbe989ff8 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -332,7 +332,7 @@ static int stm_generic_link(struct stm_data *stm_data, if (!drvdata || !drvdata->csdev) return -EINVAL; - return coresight_enable(drvdata->csdev); + return coresight_enable_sysfs(drvdata->csdev); } static void stm_generic_unlink(struct stm_data *stm_data, @@ -343,7 +343,7 @@ static void stm_generic_unlink(struct stm_data *stm_data, if (!drvdata || !drvdata->csdev) return; - coresight_disable(drvdata->csdev); + coresight_disable_sysfs(drvdata->csdev); } static phys_addr_t diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index dd78e9fcfc4d..92cdf8139f23 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -5,10 +5,400 @@ */ #include +#include #include #include "coresight-priv.h" +/* + * Use IDR to map the hash of the source's device name + * to the pointer of path for the source. The idr is for + * the sources which aren't associated with CPU. + */ +static DEFINE_IDR(path_idr); + +/* + * When operating Coresight drivers from the sysFS interface, only a single + * path can exist from a tracer (associated to a CPU) to a sink. + */ +static DEFINE_PER_CPU(struct list_head *, tracer_path); + +ssize_t coresight_simple_show_pair(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); + struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr); + u64 val; + + pm_runtime_get_sync(_dev->parent); + val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off); + pm_runtime_put_sync(_dev->parent); + return sysfs_emit(buf, "0x%llx\n", val); +} +EXPORT_SYMBOL_GPL(coresight_simple_show_pair); + +ssize_t coresight_simple_show32(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); + struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr); + u64 val; + + pm_runtime_get_sync(_dev->parent); + val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off); + pm_runtime_put_sync(_dev->parent); + return sysfs_emit(buf, "0x%llx\n", val); +} +EXPORT_SYMBOL_GPL(coresight_simple_show32); + +static int coresight_enable_source_sysfs(struct coresight_device *csdev, + enum cs_mode mode, void *data) +{ + int ret; + + /* + * Comparison with CS_MODE_SYSFS works without taking any device + * specific spinlock because the truthyness of that comparison can only + * change with coresight_mutex held, which we already have here. + */ + lockdep_assert_held(&coresight_mutex); + if (local_read(&csdev->mode) != CS_MODE_SYSFS) { + ret = source_ops(csdev)->enable(csdev, data, mode); + if (ret) + return ret; + } + + atomic_inc(&csdev->refcnt); + + return 0; +} + +/** + * coresight_disable_source_sysfs - Drop the reference count by 1 and disable + * the device if there are no users left. + * + * @csdev: The coresight device to disable + * @data: Opaque data to pass on to the disable function of the source device. + * For example in perf mode this is a pointer to the struct perf_event. + * + * Returns true if the device has been disabled. + */ +static bool coresight_disable_source_sysfs(struct coresight_device *csdev, + void *data) +{ + lockdep_assert_held(&coresight_mutex); + if (local_read(&csdev->mode) != CS_MODE_SYSFS) + return false; + + if (atomic_dec_return(&csdev->refcnt) == 0) { + coresight_disable_source(csdev, data); + return true; + } + return false; +} + +/** + * coresight_find_activated_sysfs_sink - returns the first sink activated via + * sysfs using connection based search starting from the source reference. + * + * @csdev: Coresight source device reference + */ +static struct coresight_device * +coresight_find_activated_sysfs_sink(struct coresight_device *csdev) +{ + int i; + struct coresight_device *sink = NULL; + + if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && + csdev->sysfs_sink_activated) + return csdev; + + /* + * Recursively explore each port found on this element. + */ + for (i = 0; i < csdev->pdata->nr_outconns; i++) { + struct coresight_device *child_dev; + + child_dev = csdev->pdata->out_conns[i]->dest_dev; + if (child_dev) + sink = coresight_find_activated_sysfs_sink(child_dev); + if (sink) + return sink; + } + + return NULL; +} + +/** coresight_validate_source - make sure a source has the right credentials to + * be used via sysfs. + * @csdev: the device structure for a source. + * @function: the function this was called from. + * + * Assumes the coresight_mutex is held. + */ +static int coresight_validate_source_sysfs(struct coresight_device *csdev, + const char *function) +{ + u32 type, subtype; + + type = csdev->type; + subtype = csdev->subtype.source_subtype; + + if (type != CORESIGHT_DEV_TYPE_SOURCE) { + dev_err(&csdev->dev, "wrong device type in %s\n", function); + return -EINVAL; + } + + if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC && + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE && + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM && + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) { + dev_err(&csdev->dev, "wrong device subtype in %s\n", function); + return -EINVAL; + } + + return 0; +} + +int coresight_enable_sysfs(struct coresight_device *csdev) +{ + int cpu, ret = 0; + struct coresight_device *sink; + struct list_head *path; + enum coresight_dev_subtype_source subtype; + u32 hash; + + subtype = csdev->subtype.source_subtype; + + mutex_lock(&coresight_mutex); + + ret = coresight_validate_source_sysfs(csdev, __func__); + if (ret) + goto out; + + /* + * mode == SYSFS implies that it's already enabled. Don't look at the + * refcount to determine this because we don't claim the source until + * coresight_enable_source() so can still race with Perf mode which + * doesn't hold coresight_mutex. + */ + if (local_read(&csdev->mode) == CS_MODE_SYSFS) { + /* + * There could be multiple applications driving the software + * source. So keep the refcount for each such user when the + * source is already enabled. + */ + if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) + atomic_inc(&csdev->refcnt); + goto out; + } + + sink = coresight_find_activated_sysfs_sink(csdev); + if (!sink) { + ret = -EINVAL; + goto out; + } + + path = coresight_build_path(csdev, sink); + if (IS_ERR(path)) { + pr_err("building path(s) failed\n"); + ret = PTR_ERR(path); + goto out; + } + + ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); + if (ret) + goto err_path; + + ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, NULL); + if (ret) + goto err_source; + + switch (subtype) { + case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: + /* + * When working from sysFS it is important to keep track + * of the paths that were created so that they can be + * undone in 'coresight_disable()'. Since there can only + * be a single session per tracer (when working from sysFS) + * a per-cpu variable will do just fine. + */ + cpu = source_ops(csdev)->cpu_id(csdev); + per_cpu(tracer_path, cpu) = path; + break; + case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: + case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: + case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: + /* + * Use the hash of source's device name as ID + * and map the ID to the pointer of the path. + */ + hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); + ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); + if (ret) + goto err_source; + break; + default: + /* We can't be here */ + break; + } + +out: + mutex_unlock(&coresight_mutex); + return ret; + +err_source: + coresight_disable_path(path); + +err_path: + coresight_release_path(path); + goto out; +} +EXPORT_SYMBOL_GPL(coresight_enable_sysfs); + +void coresight_disable_sysfs(struct coresight_device *csdev) +{ + int cpu, ret; + struct list_head *path = NULL; + u32 hash; + + mutex_lock(&coresight_mutex); + + ret = coresight_validate_source_sysfs(csdev, __func__); + if (ret) + goto out; + + if (!coresight_disable_source_sysfs(csdev, NULL)) + goto out; + + switch (csdev->subtype.source_subtype) { + case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: + cpu = source_ops(csdev)->cpu_id(csdev); + path = per_cpu(tracer_path, cpu); + per_cpu(tracer_path, cpu) = NULL; + break; + case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: + case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: + case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: + hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); + /* Find the path by the hash. */ + path = idr_find(&path_idr, hash); + if (path == NULL) { + pr_err("Path is not found for %s\n", dev_name(&csdev->dev)); + goto out; + } + idr_remove(&path_idr, hash); + break; + default: + /* We can't be here */ + break; + } + + coresight_disable_path(path); + coresight_release_path(path); + +out: + mutex_unlock(&coresight_mutex); +} +EXPORT_SYMBOL_GPL(coresight_disable_sysfs); + +static ssize_t enable_sink_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct coresight_device *csdev = to_coresight_device(dev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->sysfs_sink_activated); +} + +static ssize_t enable_sink_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct coresight_device *csdev = to_coresight_device(dev); + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + csdev->sysfs_sink_activated = !!val; + + return size; + +} +static DEVICE_ATTR_RW(enable_sink); + +static ssize_t enable_source_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct coresight_device *csdev = to_coresight_device(dev); + + guard(mutex)(&coresight_mutex); + return scnprintf(buf, PAGE_SIZE, "%u\n", + local_read(&csdev->mode) == CS_MODE_SYSFS); +} + +static ssize_t enable_source_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret = 0; + unsigned long val; + struct coresight_device *csdev = to_coresight_device(dev); + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + if (val) { + ret = coresight_enable_sysfs(csdev); + if (ret) + return ret; + } else { + coresight_disable_sysfs(csdev); + } + + return size; +} +static DEVICE_ATTR_RW(enable_source); + +static struct attribute *coresight_sink_attrs[] = { + &dev_attr_enable_sink.attr, + NULL, +}; +ATTRIBUTE_GROUPS(coresight_sink); + +static struct attribute *coresight_source_attrs[] = { + &dev_attr_enable_source.attr, + NULL, +}; +ATTRIBUTE_GROUPS(coresight_source); + +struct device_type coresight_dev_type[] = { + { + .name = "sink", + .groups = coresight_sink_groups, + }, + { + .name = "link", + }, + { + .name = "linksink", + .groups = coresight_sink_groups, + }, + { + .name = "source", + .groups = coresight_source_groups, + }, + { + .name = "helper", + } +}; +/* Ensure the enum matches the names and groups */ +static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX); + /* * Connections group - links attribute. * Count of created links between coresight components in the group. diff --git a/include/linux/coresight.h b/include/linux/coresight.h index d1fd7070099c..365b28022c5b 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -578,8 +578,8 @@ static inline bool coresight_is_percpu_sink(struct coresight_device *csdev) extern struct coresight_device * coresight_register(struct coresight_desc *desc); extern void coresight_unregister(struct coresight_device *csdev); -extern int coresight_enable(struct coresight_device *csdev); -extern void coresight_disable(struct coresight_device *csdev); +extern int coresight_enable_sysfs(struct coresight_device *csdev); +extern void coresight_disable_sysfs(struct coresight_device *csdev); extern int coresight_timeout(struct csdev_access *csa, u32 offset, int position, int value); @@ -609,8 +609,8 @@ static inline struct coresight_device * coresight_register(struct coresight_desc *desc) { return NULL; } static inline void coresight_unregister(struct coresight_device *csdev) {} static inline int -coresight_enable(struct coresight_device *csdev) { return -ENOSYS; } -static inline void coresight_disable(struct coresight_device *csdev) {} +coresight_enable_sysfs(struct coresight_device *csdev) { return -ENOSYS; } +static inline void coresight_disable_sysfs(struct coresight_device *csdev) {} static inline int coresight_timeout(struct csdev_access *csa, u32 offset, int position, int value) -- cgit v1.2.3 From 4545b38ef004a586295750ea49a505b6396a7c90 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 29 Jan 2024 15:40:38 +0000 Subject: coresight: Remove atomic type from refcnt Refcnt is only ever accessed from either inside the coresight_mutex, or the device's spinlock, making the atomic type and atomic_dec_return() calls confusing and unnecessary. The only point of synchronisation outside of these two types of locks is already done with a compare and swap on 'mode', which a comment has been added for. There was one instance of refcnt being used outside of a lock in TPIU, but that can easily be fixed by making it the same as all the other devices and adding a spinlock. Potentially in the future all the refcounting and locking can be moved up into the core code, and all the mostly duplicate code from the individual devices can be removed. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20240129154050.569566-8-james.clark@arm.com Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-etb10.c | 11 ++++++----- drivers/hwtracing/coresight/coresight-sysfs.c | 7 ++++--- drivers/hwtracing/coresight/coresight-tmc-etf.c | 20 +++++++++++--------- drivers/hwtracing/coresight/coresight-tmc-etr.c | 13 +++++++------ drivers/hwtracing/coresight/coresight-tpda.c | 7 ++++--- drivers/hwtracing/coresight/coresight-tpiu.c | 14 ++++++++++++-- drivers/hwtracing/coresight/ultrasoc-smb.c | 9 +++++---- include/linux/coresight.h | 13 ++++++++++--- 8 files changed, 59 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 145fb815ec1c..306d2af47d63 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -161,7 +161,7 @@ static int etb_enable_sysfs(struct coresight_device *csdev) local_set(&csdev->mode, CS_MODE_SYSFS); } - atomic_inc(&csdev->refcnt); + csdev->refcnt++; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); return ret; @@ -197,7 +197,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) * use for this session. */ if (drvdata->pid == pid) { - atomic_inc(&csdev->refcnt); + csdev->refcnt++; goto out; } @@ -215,7 +215,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) /* Associate with monitored process. */ drvdata->pid = pid; local_set(&drvdata->csdev->mode, CS_MODE_PERF); - atomic_inc(&csdev->refcnt); + csdev->refcnt++; } out: @@ -354,7 +354,8 @@ static int etb_disable(struct coresight_device *csdev) spin_lock_irqsave(&drvdata->spinlock, flags); - if (atomic_dec_return(&csdev->refcnt)) { + csdev->refcnt--; + if (csdev->refcnt) { spin_unlock_irqrestore(&drvdata->spinlock, flags); return -EBUSY; } @@ -445,7 +446,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev, spin_lock_irqsave(&drvdata->spinlock, flags); /* Don't do anything if another tracer is using this sink */ - if (atomic_read(&csdev->refcnt) != 1) + if (csdev->refcnt != 1) goto out; __etb_disable_hw(drvdata); diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 92cdf8139f23..5992f2c2200a 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -68,7 +68,7 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev, return ret; } - atomic_inc(&csdev->refcnt); + csdev->refcnt++; return 0; } @@ -90,7 +90,8 @@ static bool coresight_disable_source_sysfs(struct coresight_device *csdev, if (local_read(&csdev->mode) != CS_MODE_SYSFS) return false; - if (atomic_dec_return(&csdev->refcnt) == 0) { + csdev->refcnt--; + if (csdev->refcnt == 0) { coresight_disable_source(csdev, data); return true; } @@ -190,7 +191,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev) * source is already enabled. */ if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) - atomic_inc(&csdev->refcnt); + csdev->refcnt++; goto out; } diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 2a7e516052a2..f3281c958a57 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -206,7 +206,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) * touched. */ if (local_read(&csdev->mode) == CS_MODE_SYSFS) { - atomic_inc(&csdev->refcnt); + csdev->refcnt++; goto out; } @@ -229,7 +229,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) ret = tmc_etb_enable_hw(drvdata); if (!ret) { local_set(&csdev->mode, CS_MODE_SYSFS); - atomic_inc(&csdev->refcnt); + csdev->refcnt++; } else { /* Free up the buffer if we failed to enable */ used = false; @@ -284,7 +284,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) * use for this session. */ if (drvdata->pid == pid) { - atomic_inc(&csdev->refcnt); + csdev->refcnt++; break; } @@ -293,7 +293,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) /* Associate with monitored process. */ drvdata->pid = pid; local_set(&csdev->mode, CS_MODE_PERF); - atomic_inc(&csdev->refcnt); + csdev->refcnt++; } } while (0); spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -338,7 +338,8 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev) return -EBUSY; } - if (atomic_dec_return(&csdev->refcnt)) { + csdev->refcnt--; + if (csdev->refcnt) { spin_unlock_irqrestore(&drvdata->spinlock, flags); return -EBUSY; } @@ -371,7 +372,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, return -EBUSY; } - if (atomic_read(&csdev->refcnt) == 0) { + if (csdev->refcnt == 0) { ret = tmc_etf_enable_hw(drvdata); if (!ret) { local_set(&csdev->mode, CS_MODE_SYSFS); @@ -379,7 +380,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, } } if (!ret) - atomic_inc(&csdev->refcnt); + csdev->refcnt++; spin_unlock_irqrestore(&drvdata->spinlock, flags); if (first_enable) @@ -401,7 +402,8 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, return; } - if (atomic_dec_return(&csdev->refcnt) == 0) { + csdev->refcnt--; + if (csdev->refcnt == 0) { tmc_etf_disable_hw(drvdata); local_set(&csdev->mode, CS_MODE_DISABLED); last_disable = true; @@ -489,7 +491,7 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, spin_lock_irqsave(&drvdata->spinlock, flags); /* Don't do anything if another tracer is using this sink */ - if (atomic_read(&csdev->refcnt) != 1) + if (csdev->refcnt != 1) goto out; CS_UNLOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 3dc989d4fcab..88a0fc375b4d 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1231,14 +1231,14 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) * touched, even if the buffer size has changed. */ if (local_read(&csdev->mode) == CS_MODE_SYSFS) { - atomic_inc(&csdev->refcnt); + csdev->refcnt++; goto out; } ret = tmc_etr_enable_hw(drvdata, sysfs_buf); if (!ret) { local_set(&csdev->mode, CS_MODE_SYSFS); - atomic_inc(&csdev->refcnt); + csdev->refcnt++; } out: @@ -1564,7 +1564,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev, spin_lock_irqsave(&drvdata->spinlock, flags); /* Don't do anything if another tracer is using this sink */ - if (atomic_read(&csdev->refcnt) != 1) { + if (csdev->refcnt != 1) { spin_unlock_irqrestore(&drvdata->spinlock, flags); goto out; } @@ -1676,7 +1676,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) * use for this session. */ if (drvdata->pid == pid) { - atomic_inc(&csdev->refcnt); + csdev->refcnt++; goto unlock_out; } @@ -1686,7 +1686,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) drvdata->pid = pid; local_set(&csdev->mode, CS_MODE_PERF); drvdata->perf_buf = etr_perf->etr_buf; - atomic_inc(&csdev->refcnt); + csdev->refcnt++; } unlock_out: @@ -1719,7 +1719,8 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) return -EBUSY; } - if (atomic_dec_return(&csdev->refcnt)) { + csdev->refcnt--; + if (csdev->refcnt) { spin_unlock_irqrestore(&drvdata->spinlock, flags); return -EBUSY; } diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index c813ec427b8d..f8f6413cc711 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -152,7 +152,8 @@ static int __tpda_enable(struct tpda_drvdata *drvdata, int port) * Only do pre-port enable for first port that calls enable when the * device's main refcount is still 0 */ - if (!atomic_read(&drvdata->csdev->refcnt)) + lockdep_assert_held(&drvdata->spinlock); + if (!drvdata->csdev->refcnt) tpda_enable_pre_port(drvdata); ret = tpda_enable_port(drvdata, port); @@ -173,7 +174,7 @@ static int tpda_enable(struct coresight_device *csdev, ret = __tpda_enable(drvdata, in->dest_port); if (!ret) { atomic_inc(&in->dest_refcnt); - atomic_inc(&csdev->refcnt); + csdev->refcnt++; dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port); } } @@ -204,7 +205,7 @@ static void tpda_disable(struct coresight_device *csdev, spin_lock(&drvdata->spinlock); if (atomic_dec_return(&in->dest_refcnt) == 0) { __tpda_disable(drvdata, in->dest_port); - atomic_dec(&csdev->refcnt); + csdev->refcnt--; } spin_unlock(&drvdata->spinlock); diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 7e69048ac944..29024f880fda 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -58,6 +58,7 @@ struct tpiu_drvdata { void __iomem *base; struct clk *atclk; struct coresight_device *csdev; + spinlock_t spinlock; }; static void tpiu_enable_hw(struct csdev_access *csa) @@ -72,8 +73,11 @@ static void tpiu_enable_hw(struct csdev_access *csa) static int tpiu_enable(struct coresight_device *csdev, enum cs_mode mode, void *__unused) { + struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + guard(spinlock)(&drvdata->spinlock); tpiu_enable_hw(&csdev->access); - atomic_inc(&csdev->refcnt); + csdev->refcnt++; dev_dbg(&csdev->dev, "TPIU enabled\n"); return 0; } @@ -96,7 +100,11 @@ static void tpiu_disable_hw(struct csdev_access *csa) static int tpiu_disable(struct coresight_device *csdev) { - if (atomic_dec_return(&csdev->refcnt)) + struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + guard(spinlock)(&drvdata->spinlock); + csdev->refcnt--; + if (csdev->refcnt) return -EBUSY; tpiu_disable_hw(&csdev->access); @@ -132,6 +140,8 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) if (!drvdata) return -ENOMEM; + spin_lock_init(&drvdata->spinlock); + drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ if (!IS_ERR(drvdata->atclk)) { ret = clk_prepare_enable(drvdata->atclk); diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index bef633960173..a92c5b750f81 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -103,7 +103,7 @@ static int smb_open(struct inode *inode, struct file *file) if (drvdata->reading) return -EBUSY; - if (atomic_read(&drvdata->csdev->refcnt)) + if (drvdata->csdev->refcnt) return -EBUSY; smb_update_data_size(drvdata); @@ -271,7 +271,7 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode, if (ret) return ret; - atomic_inc(&csdev->refcnt); + csdev->refcnt++; dev_dbg(&csdev->dev, "Ultrasoc SMB enabled\n"); return ret; @@ -286,7 +286,8 @@ static int smb_disable(struct coresight_device *csdev) if (drvdata->reading) return -EBUSY; - if (atomic_dec_return(&csdev->refcnt)) + csdev->refcnt--; + if (csdev->refcnt) return -EBUSY; /* Complain if we (somehow) got out of sync */ @@ -381,7 +382,7 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev, guard(spinlock)(&drvdata->spinlock); /* Don't do anything if another tracer is using this sink. */ - if (atomic_read(&csdev->refcnt) != 1) + if (csdev->refcnt != 1) return 0; smb_disable_hw(drvdata); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 365b28022c5b..74bcec526aa9 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -230,8 +230,15 @@ struct coresight_sysfs_link { * actually an 'enum cs_mode', but is stored in an atomic type. * This is always accessed through local_read() and local_set(), * but wherever it's done from within the Coresight device's lock, - * a non-atomic read would also work. - * @refcnt: keep track of what is in use. + * a non-atomic read would also work. This is the main point of + * synchronisation between code happening inside the sysfs mode's + * coresight_mutex and outside when running in Perf mode. A compare + * and exchange swap is done to atomically claim one mode or the + * other. + * @refcnt: keep track of what is in use. Only access this outside of the + * device's spinlock when the coresight_mutex held and mode == + * CS_MODE_SYSFS. Otherwise it must be accessed from inside the + * spinlock. * @orphan: true if the component has connections that haven't been linked. * @sysfs_sink_activated: 'true' when a sink has been selected for use via sysfs * by writing a 1 to the 'enable_sink' file. A sink can be @@ -257,7 +264,7 @@ struct coresight_device { struct csdev_access access; struct device dev; local_t mode; - atomic_t refcnt; + int refcnt; bool orphan; /* sink specific fields */ bool sysfs_sink_activated; -- cgit v1.2.3 From 053ad9ad1d13f253605d7644de3aa20d958569ef Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 29 Jan 2024 15:40:39 +0000 Subject: coresight: Remove unused stubs These are a bit annoying to keep up to date when the function signatures change. But if CONFIG_CORESIGHT isn't enabled, then they're not used anyway so just delete them. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20240129154050.569566-9-james.clark@arm.com Signed-off-by: Suzuki K Poulose --- include/linux/coresight.h | 79 ----------------------------------------------- 1 file changed, 79 deletions(-) (limited to 'include') diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 74bcec526aa9..ecf4b8aecca8 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -391,8 +391,6 @@ struct coresight_ops { const struct coresight_ops_helper *helper_ops; }; -#if IS_ENABLED(CONFIG_CORESIGHT) - static inline u32 csdev_access_relaxed_read32(struct csdev_access *csa, u32 offset) { @@ -611,83 +609,6 @@ void coresight_relaxed_write64(struct coresight_device *csdev, u64 val, u32 offset); void coresight_write64(struct coresight_device *csdev, u64 val, u32 offset); -#else -static inline struct coresight_device * -coresight_register(struct coresight_desc *desc) { return NULL; } -static inline void coresight_unregister(struct coresight_device *csdev) {} -static inline int -coresight_enable_sysfs(struct coresight_device *csdev) { return -ENOSYS; } -static inline void coresight_disable_sysfs(struct coresight_device *csdev) {} - -static inline int coresight_timeout(struct csdev_access *csa, u32 offset, - int position, int value) -{ - return 1; -} - -static inline int coresight_claim_device_unlocked(struct coresight_device *csdev) -{ - return -EINVAL; -} - -static inline int coresight_claim_device(struct coresight_device *csdev) -{ - return -EINVAL; -} - -static inline void coresight_disclaim_device(struct coresight_device *csdev) {} -static inline void coresight_disclaim_device_unlocked(struct coresight_device *csdev) {} - -static inline bool coresight_loses_context_with_cpu(struct device *dev) -{ - return false; -} - -static inline u32 coresight_relaxed_read32(struct coresight_device *csdev, u32 offset) -{ - WARN_ON_ONCE(1); - return 0; -} - -static inline u32 coresight_read32(struct coresight_device *csdev, u32 offset) -{ - WARN_ON_ONCE(1); - return 0; -} - -static inline void coresight_write32(struct coresight_device *csdev, u32 val, u32 offset) -{ -} - -static inline void coresight_relaxed_write32(struct coresight_device *csdev, - u32 val, u32 offset) -{ -} - -static inline u64 coresight_relaxed_read64(struct coresight_device *csdev, - u32 offset) -{ - WARN_ON_ONCE(1); - return 0; -} - -static inline u64 coresight_read64(struct coresight_device *csdev, u32 offset) -{ - WARN_ON_ONCE(1); - return 0; -} - -static inline void coresight_relaxed_write64(struct coresight_device *csdev, - u64 val, u32 offset) -{ -} - -static inline void coresight_write64(struct coresight_device *csdev, u64 val, u32 offset) -{ -} - -#endif /* IS_ENABLED(CONFIG_CORESIGHT) */ - extern int coresight_get_cpu(struct device *dev); struct coresight_platform_data *coresight_get_platform_data(struct device *dev); -- cgit v1.2.3 From d724f65218b994da234081df5dfe417c23802a65 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 29 Jan 2024 15:40:41 +0000 Subject: coresight: Add helper for atomically taking the device Now that mode is in struct coresight_device, this pattern can be wrapped in a helper. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20240129154050.569566-11-james.clark@arm.com Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-etm3x-core.c | 8 +++----- drivers/hwtracing/coresight/coresight-etm4x-core.c | 8 +++----- drivers/hwtracing/coresight/coresight-stm.c | 8 +++----- include/linux/coresight.h | 11 +++++++++++ 4 files changed, 20 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index de53c3e8db29..c301f6a5f065 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -556,14 +556,12 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode) { int ret; - u32 val; struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - val = local_cmpxchg(&drvdata->csdev->mode, CS_MODE_DISABLED, mode); - - /* Someone is already using the tracer */ - if (val) + if (!coresight_take_mode(csdev, mode)) { + /* Someone is already using the tracer */ return -EBUSY; + } switch (mode) { case CS_MODE_SYSFS: diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 1c64b54459d7..572ac4a5e4ff 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -840,13 +840,11 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode) { int ret; - u32 val; - val = local_cmpxchg(&csdev->mode, CS_MODE_DISABLED, mode); - - /* Someone is already using the tracer */ - if (val) + if (!coresight_take_mode(csdev, mode)) { + /* Someone is already using the tracer */ return -EBUSY; + } switch (mode) { case CS_MODE_SYSFS: diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index b2ccbe989ff8..ff578762f6bf 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -193,17 +193,15 @@ static void stm_enable_hw(struct stm_drvdata *drvdata) static int stm_enable(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode) { - u32 val; struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); if (mode != CS_MODE_SYSFS) return -EINVAL; - val = local_cmpxchg(&csdev->mode, CS_MODE_DISABLED, mode); - - /* Someone is already using the tracer */ - if (val) + if (!coresight_take_mode(csdev, mode)) { + /* Someone is already using the tracer */ return -EBUSY; + } pm_runtime_get_sync(csdev->dev.parent); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index ecf4b8aecca8..414bcbbdaf62 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -580,6 +580,17 @@ static inline bool coresight_is_percpu_sink(struct coresight_device *csdev) (csdev->subtype.sink_subtype == CORESIGHT_DEV_SUBTYPE_SINK_PERCPU_SYSMEM); } +/* + * Atomically try to take the device and set a new mode. Returns true on + * success, false if the device is already taken by someone else. + */ +static inline bool coresight_take_mode(struct coresight_device *csdev, + enum cs_mode new_mode) +{ + return local_cmpxchg(&csdev->mode, CS_MODE_DISABLED, new_mode) == + CS_MODE_DISABLED; +} + extern struct coresight_device * coresight_register(struct coresight_desc *desc); extern void coresight_unregister(struct coresight_device *csdev); -- cgit v1.2.3 From c95c2733e5feb1f6848923f166849b2d1c7bf682 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 29 Jan 2024 15:40:42 +0000 Subject: coresight: Add a helper for getting csdev->mode Now that mode is in struct coresight_device accesses can be wrapped. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20240129154050.569566-12-james.clark@arm.com Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-etb10.c | 10 +++++----- drivers/hwtracing/coresight/coresight-etm3x-core.c | 6 +++--- drivers/hwtracing/coresight/coresight-etm3x-sysfs.c | 4 ++-- drivers/hwtracing/coresight/coresight-etm4x-core.c | 8 ++++---- drivers/hwtracing/coresight/coresight-stm.c | 14 +++++++------- drivers/hwtracing/coresight/coresight-sysfs.c | 8 ++++---- drivers/hwtracing/coresight/coresight-tmc-core.c | 2 +- drivers/hwtracing/coresight/coresight-tmc-etf.c | 16 ++++++++-------- drivers/hwtracing/coresight/coresight-tmc-etr.c | 14 +++++++------- drivers/hwtracing/coresight/ultrasoc-smb.c | 8 ++++---- include/linux/coresight.h | 5 +++++ 11 files changed, 50 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 306d2af47d63..e3a54f7038ad 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -148,12 +148,12 @@ static int etb_enable_sysfs(struct coresight_device *csdev) spin_lock_irqsave(&drvdata->spinlock, flags); /* Don't messup with perf sessions. */ - if (local_read(&csdev->mode) == CS_MODE_PERF) { + if (coresight_get_mode(csdev) == CS_MODE_PERF) { ret = -EBUSY; goto out; } - if (local_read(&csdev->mode) == CS_MODE_DISABLED) { + if (coresight_get_mode(csdev) == CS_MODE_DISABLED) { ret = etb_enable_hw(drvdata); if (ret) goto out; @@ -179,7 +179,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) spin_lock_irqsave(&drvdata->spinlock, flags); /* No need to continue if the component is already in used by sysFS. */ - if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { ret = -EBUSY; goto out; } @@ -361,7 +361,7 @@ static int etb_disable(struct coresight_device *csdev) } /* Complain if we (somehow) got out of sync */ - WARN_ON_ONCE(local_read(&csdev->mode) == CS_MODE_DISABLED); + WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); etb_disable_hw(drvdata); /* Dissociate from monitored process. */ drvdata->pid = -1; @@ -588,7 +588,7 @@ static void etb_dump(struct etb_drvdata *drvdata) unsigned long flags; spin_lock_irqsave(&drvdata->spinlock, flags); - if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { __etb_disable_hw(drvdata); etb_dump_hw(drvdata); __etb_enable_hw(drvdata); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index c301f6a5f065..64ff53887ef5 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -676,7 +676,7 @@ static void etm_disable(struct coresight_device *csdev, * change its status. As such we can read the status here without * fearing it will change under us. */ - mode = local_read(&csdev->mode); + mode = coresight_get_mode(csdev); switch (mode) { case CS_MODE_DISABLED: @@ -727,7 +727,7 @@ static int etm_starting_cpu(unsigned int cpu) etmdrvdata[cpu]->os_unlock = true; } - if (local_read(&etmdrvdata[cpu]->csdev->mode)) + if (coresight_get_mode(etmdrvdata[cpu]->csdev)) etm_enable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); return 0; @@ -739,7 +739,7 @@ static int etm_dying_cpu(unsigned int cpu) return 0; spin_lock(&etmdrvdata[cpu]->spinlock); - if (local_read(&etmdrvdata[cpu]->csdev->mode)) + if (coresight_get_mode(etmdrvdata[cpu]->csdev)) etm_disable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); return 0; diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 6c8429c980b1..68c644be9813 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -722,7 +722,7 @@ static ssize_t cntr_val_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!local_read(&drvdata->csdev->mode)) { + if (!coresight_get_mode(drvdata->csdev)) { spin_lock(&drvdata->spinlock); for (i = 0; i < drvdata->nr_cntr; i++) ret += sprintf(buf, "counter %d: %x\n", @@ -941,7 +941,7 @@ static ssize_t seq_curr_state_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!local_read(&drvdata->csdev->mode)) { + if (!coresight_get_mode(drvdata->csdev)) { val = config->seq_curr_state; goto out; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 572ac4a5e4ff..e65232e982a7 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1007,7 +1007,7 @@ static void etm4_disable(struct coresight_device *csdev, * change its status. As such we can read the status here without * fearing it will change under us. */ - mode = local_read(&csdev->mode); + mode = coresight_get_mode(csdev); switch (mode) { case CS_MODE_DISABLED: @@ -1659,7 +1659,7 @@ static int etm4_starting_cpu(unsigned int cpu) if (!etmdrvdata[cpu]->os_unlock) etm4_os_unlock(etmdrvdata[cpu]); - if (local_read(&etmdrvdata[cpu]->csdev->mode)) + if (coresight_get_mode(etmdrvdata[cpu]->csdev)) etm4_enable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); return 0; @@ -1671,7 +1671,7 @@ static int etm4_dying_cpu(unsigned int cpu) return 0; spin_lock(&etmdrvdata[cpu]->spinlock); - if (local_read(&etmdrvdata[cpu]->csdev->mode)) + if (coresight_get_mode(etmdrvdata[cpu]->csdev)) etm4_disable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); return 0; @@ -1829,7 +1829,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) * Save and restore the ETM Trace registers only if * the ETM is active. */ - if (local_read(&drvdata->csdev->mode) && drvdata->save_state) + if (coresight_get_mode(drvdata->csdev) && drvdata->save_state) ret = __etm4_cpu_save(drvdata); return ret; } diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index ff578762f6bf..1d02d42bea83 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -262,7 +262,7 @@ static void stm_disable(struct coresight_device *csdev, * change its status. As such we can read the status here without * fearing it will change under us. */ - if (local_read(&csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { spin_lock(&drvdata->spinlock); stm_disable_hw(drvdata); spin_unlock(&drvdata->spinlock); @@ -369,7 +369,7 @@ static long stm_generic_set_options(struct stm_data *stm_data, { struct stm_drvdata *drvdata = container_of(stm_data, struct stm_drvdata, stm); - if (!(drvdata && local_read(&drvdata->csdev->mode))) + if (!(drvdata && coresight_get_mode(drvdata->csdev))) return -EINVAL; if (channel >= drvdata->numsp) @@ -404,7 +404,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, struct stm_drvdata, stm); unsigned int stm_flags; - if (!(drvdata && local_read(&drvdata->csdev->mode))) + if (!(drvdata && coresight_get_mode(drvdata->csdev))) return -EACCES; if (channel >= drvdata->numsp) @@ -511,7 +511,7 @@ static ssize_t port_select_show(struct device *dev, struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val; - if (!local_read(&drvdata->csdev->mode)) { + if (!coresight_get_mode(drvdata->csdev)) { val = drvdata->stmspscr; } else { spin_lock(&drvdata->spinlock); @@ -537,7 +537,7 @@ static ssize_t port_select_store(struct device *dev, spin_lock(&drvdata->spinlock); drvdata->stmspscr = val; - if (local_read(&drvdata->csdev->mode)) { + if (coresight_get_mode(drvdata->csdev)) { CS_UNLOCK(drvdata->base); /* Process as per ARM's TRM recommendation */ stmsper = readl_relaxed(drvdata->base + STMSPER); @@ -558,7 +558,7 @@ static ssize_t port_enable_show(struct device *dev, struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val; - if (!local_read(&drvdata->csdev->mode)) { + if (!coresight_get_mode(drvdata->csdev)) { val = drvdata->stmsper; } else { spin_lock(&drvdata->spinlock); @@ -584,7 +584,7 @@ static ssize_t port_enable_store(struct device *dev, spin_lock(&drvdata->spinlock); drvdata->stmsper = val; - if (local_read(&drvdata->csdev->mode)) { + if (coresight_get_mode(drvdata->csdev)) { CS_UNLOCK(drvdata->base); writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER); CS_LOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index fa52297c73d2..f9444e2cb1d9 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -62,7 +62,7 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev, * change with coresight_mutex held, which we already have here. */ lockdep_assert_held(&coresight_mutex); - if (local_read(&csdev->mode) != CS_MODE_SYSFS) { + if (coresight_get_mode(csdev) != CS_MODE_SYSFS) { ret = source_ops(csdev)->enable(csdev, data, mode); if (ret) return ret; @@ -87,7 +87,7 @@ static bool coresight_disable_source_sysfs(struct coresight_device *csdev, void *data) { lockdep_assert_held(&coresight_mutex); - if (local_read(&csdev->mode) != CS_MODE_SYSFS) + if (coresight_get_mode(csdev) != CS_MODE_SYSFS) return false; csdev->refcnt--; @@ -184,7 +184,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev) * coresight_enable_source() so can still race with Perf mode which * doesn't hold coresight_mutex. */ - if (local_read(&csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { /* * There could be multiple applications driving the software * source. So keep the refcount for each such user when the @@ -338,7 +338,7 @@ static ssize_t enable_source_show(struct device *dev, guard(mutex)(&coresight_mutex); return scnprintf(buf, PAGE_SIZE, "%u\n", - local_read(&csdev->mode) == CS_MODE_SYSFS); + coresight_get_mode(csdev) == CS_MODE_SYSFS); } static ssize_t enable_source_store(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index e8b2bbed047f..72005b0c633e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -558,7 +558,7 @@ static void tmc_shutdown(struct amba_device *adev) spin_lock_irqsave(&drvdata->spinlock, flags); - if (local_read(&drvdata->csdev->mode) == CS_MODE_DISABLED) + if (coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED) goto out; if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index f3281c958a57..77ef67c976e9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -89,7 +89,7 @@ static void __tmc_etb_disable_hw(struct tmc_drvdata *drvdata) * When operating in sysFS mode the content of the buffer needs to be * read before the TMC is disabled. */ - if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) tmc_etb_dump_hw(drvdata); tmc_disable_hw(drvdata); @@ -205,7 +205,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) * sink is already enabled no memory is needed and the HW need not be * touched. */ - if (local_read(&csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { csdev->refcnt++; goto out; } @@ -262,7 +262,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) * No need to continue if the ETB/ETF is already operated * from sysFS. */ - if (local_read(&csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { ret = -EBUSY; break; } @@ -345,7 +345,7 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev) } /* Complain if we (somehow) got out of sync */ - WARN_ON_ONCE(local_read(&csdev->mode) == CS_MODE_DISABLED); + WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); tmc_etb_disable_hw(drvdata); /* Dissociate from monitored process. */ drvdata->pid = -1; @@ -485,7 +485,7 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, return 0; /* This shouldn't happen */ - if (WARN_ON_ONCE(local_read(&csdev->mode) != CS_MODE_PERF)) + if (WARN_ON_ONCE(coresight_get_mode(csdev) != CS_MODE_PERF)) return 0; spin_lock_irqsave(&drvdata->spinlock, flags); @@ -631,7 +631,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Don't interfere if operated from Perf */ - if (local_read(&drvdata->csdev->mode) == CS_MODE_PERF) { + if (coresight_get_mode(drvdata->csdev) == CS_MODE_PERF) { ret = -EINVAL; goto out; } @@ -643,7 +643,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { /* There is no point in reading a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode != TMC_MODE_CIRCULAR_BUFFER) { @@ -675,7 +675,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); /* Re-enable the TMC if need be */ - if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { /* There is no point in reading a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode != TMC_MODE_CIRCULAR_BUFFER) { diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 88a0fc375b4d..383cb8647589 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1143,7 +1143,7 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata) * When operating in sysFS mode the content of the buffer needs to be * read before the TMC is disabled. */ - if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) tmc_etr_sync_sysfs_buf(drvdata); tmc_disable_hw(drvdata); @@ -1189,7 +1189,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) spin_lock_irqsave(&drvdata->spinlock, flags); } - if (drvdata->reading || local_read(&csdev->mode) == CS_MODE_PERF) { + if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) { ret = -EBUSY; goto out; } @@ -1230,7 +1230,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) * sink is already enabled no memory is needed and the HW need not be * touched, even if the buffer size has changed. */ - if (local_read(&csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { csdev->refcnt++; goto out; } @@ -1652,7 +1652,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) spin_lock_irqsave(&drvdata->spinlock, flags); /* Don't use this sink if it is already claimed by sysFS */ - if (local_read(&csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { rc = -EBUSY; goto unlock_out; } @@ -1726,7 +1726,7 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) } /* Complain if we (somehow) got out of sync */ - WARN_ON_ONCE(local_read(&csdev->mode) == CS_MODE_DISABLED); + WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); tmc_etr_disable_hw(drvdata); /* Dissociate from monitored process. */ drvdata->pid = -1; @@ -1778,7 +1778,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) } /* Disable the TMC if we are trying to read from a running session. */ - if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) __tmc_etr_disable_hw(drvdata); drvdata->reading = true; @@ -1800,7 +1800,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); /* RE-enable the TMC if need be */ - if (local_read(&drvdata->csdev->mode) == CS_MODE_SYSFS) { + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { /* * The trace run will continue with the same allocated trace * buffer. Since the tracer is still enabled drvdata::buf can't diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index a92c5b750f81..903cfffe0bcd 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -207,7 +207,7 @@ static void smb_enable_sysfs(struct coresight_device *csdev) { struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); - if (local_read(&csdev->mode) != CS_MODE_DISABLED) + if (coresight_get_mode(csdev) != CS_MODE_DISABLED) return; smb_enable_hw(drvdata); @@ -253,8 +253,8 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode, return -EBUSY; /* Do nothing, the SMB is already enabled as other mode */ - if (local_read(&csdev->mode) != CS_MODE_DISABLED && - local_read(&csdev->mode) != mode) + if (coresight_get_mode(csdev) != CS_MODE_DISABLED && + coresight_get_mode(csdev) != mode) return -EBUSY; switch (mode) { @@ -291,7 +291,7 @@ static int smb_disable(struct coresight_device *csdev) return -EBUSY; /* Complain if we (somehow) got out of sync */ - WARN_ON_ONCE(local_read(&csdev->mode) == CS_MODE_DISABLED); + WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); smb_disable_hw(drvdata); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 414bcbbdaf62..a49e4e20e899 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -591,6 +591,11 @@ static inline bool coresight_take_mode(struct coresight_device *csdev, CS_MODE_DISABLED; } +static inline enum cs_mode coresight_get_mode(struct coresight_device *csdev) +{ + return local_read(&csdev->mode); +} + extern struct coresight_device * coresight_register(struct coresight_desc *desc); extern void coresight_unregister(struct coresight_device *csdev); -- cgit v1.2.3 From bcaabb95f0c9883fb8e1112bd13eaba9cfd62c15 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 29 Jan 2024 15:40:43 +0000 Subject: coresight: Add helper for setting csdev->mode Now that mode is in struct coresight_device, sets can be wrapped. This also allows us to add a sanity check that there have been no concurrent modifications of mode. Currently all usages of local_set() were inside the device's spin locks so this new warning shouldn't be triggered. coresight_take_mode() could maybe have been used in place of adding the warning, but there may be use cases which set the mode to the same mode which are valid but would fail in coresight_take_mode() because it requires the device to only be in the disabled state. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20240129154050.569566-13-james.clark@arm.com Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-etb10.c | 6 +++--- drivers/hwtracing/coresight/coresight-etm3x-core.c | 4 ++-- drivers/hwtracing/coresight/coresight-etm4x-core.c | 4 ++-- drivers/hwtracing/coresight/coresight-stm.c | 2 +- drivers/hwtracing/coresight/coresight-tmc-etf.c | 10 +++++----- drivers/hwtracing/coresight/coresight-tmc-etr.c | 6 +++--- drivers/hwtracing/coresight/ultrasoc-smb.c | 6 +++--- include/linux/coresight.h | 16 ++++++++++++++++ 8 files changed, 35 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index e3a54f7038ad..3aab182b562f 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -158,7 +158,7 @@ static int etb_enable_sysfs(struct coresight_device *csdev) if (ret) goto out; - local_set(&csdev->mode, CS_MODE_SYSFS); + coresight_set_mode(csdev, CS_MODE_SYSFS); } csdev->refcnt++; @@ -214,7 +214,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) if (!ret) { /* Associate with monitored process. */ drvdata->pid = pid; - local_set(&drvdata->csdev->mode, CS_MODE_PERF); + coresight_set_mode(drvdata->csdev, CS_MODE_PERF); csdev->refcnt++; } @@ -365,7 +365,7 @@ static int etb_disable(struct coresight_device *csdev) etb_disable_hw(drvdata); /* Dissociate from monitored process. */ drvdata->pid = -1; - local_set(&csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(csdev, CS_MODE_DISABLED); spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_dbg(&csdev->dev, "ETB disabled\n"); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index 64ff53887ef5..9d5c1391ffb1 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -576,7 +576,7 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event, /* The tracer didn't start */ if (ret) - local_set(&drvdata->csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED); return ret; } @@ -693,7 +693,7 @@ static void etm_disable(struct coresight_device *csdev, } if (mode) - local_set(&csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(csdev, CS_MODE_DISABLED); } static const struct coresight_ops_source etm_source_ops = { diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index e65232e982a7..f087bc8d1da6 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -859,7 +859,7 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event, /* The tracer didn't start */ if (ret) - local_set(&csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(csdev, CS_MODE_DISABLED); return ret; } @@ -1021,7 +1021,7 @@ static void etm4_disable(struct coresight_device *csdev, } if (mode) - local_set(&csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(csdev, CS_MODE_DISABLED); } static const struct coresight_ops_source etm4_source_ops = { diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 1d02d42bea83..974d37e5f94c 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -272,7 +272,7 @@ static void stm_disable(struct coresight_device *csdev, pm_runtime_put(csdev->dev.parent); - local_set(&csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(csdev, CS_MODE_DISABLED); dev_dbg(&csdev->dev, "STM tracing disabled\n"); } } diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 77ef67c976e9..d4f641cd9de6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -228,7 +228,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) ret = tmc_etb_enable_hw(drvdata); if (!ret) { - local_set(&csdev->mode, CS_MODE_SYSFS); + coresight_set_mode(csdev, CS_MODE_SYSFS); csdev->refcnt++; } else { /* Free up the buffer if we failed to enable */ @@ -292,7 +292,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) if (!ret) { /* Associate with monitored process. */ drvdata->pid = pid; - local_set(&csdev->mode, CS_MODE_PERF); + coresight_set_mode(csdev, CS_MODE_PERF); csdev->refcnt++; } } while (0); @@ -349,7 +349,7 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev) tmc_etb_disable_hw(drvdata); /* Dissociate from monitored process. */ drvdata->pid = -1; - local_set(&csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(csdev, CS_MODE_DISABLED); spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -375,7 +375,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, if (csdev->refcnt == 0) { ret = tmc_etf_enable_hw(drvdata); if (!ret) { - local_set(&csdev->mode, CS_MODE_SYSFS); + coresight_set_mode(csdev, CS_MODE_SYSFS); first_enable = true; } } @@ -405,7 +405,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, csdev->refcnt--; if (csdev->refcnt == 0) { tmc_etf_disable_hw(drvdata); - local_set(&csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(csdev, CS_MODE_DISABLED); last_disable = true; } spin_unlock_irqrestore(&drvdata->spinlock, flags); diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 383cb8647589..e75428fa1592 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1237,7 +1237,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) ret = tmc_etr_enable_hw(drvdata, sysfs_buf); if (!ret) { - local_set(&csdev->mode, CS_MODE_SYSFS); + coresight_set_mode(csdev, CS_MODE_SYSFS); csdev->refcnt++; } @@ -1684,7 +1684,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) if (!rc) { /* Associate with monitored process. */ drvdata->pid = pid; - local_set(&csdev->mode, CS_MODE_PERF); + coresight_set_mode(csdev, CS_MODE_PERF); drvdata->perf_buf = etr_perf->etr_buf; csdev->refcnt++; } @@ -1730,7 +1730,7 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) tmc_etr_disable_hw(drvdata); /* Dissociate from monitored process. */ drvdata->pid = -1; - local_set(&csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(csdev, CS_MODE_DISABLED); /* Reset perf specific data */ drvdata->perf_buf = NULL; diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 903cfffe0bcd..f9ebf20c91e6 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -211,7 +211,7 @@ static void smb_enable_sysfs(struct coresight_device *csdev) return; smb_enable_hw(drvdata); - local_set(&csdev->mode, CS_MODE_SYSFS); + coresight_set_mode(csdev, CS_MODE_SYSFS); } static int smb_enable_perf(struct coresight_device *csdev, void *data) @@ -234,7 +234,7 @@ static int smb_enable_perf(struct coresight_device *csdev, void *data) if (drvdata->pid == -1) { smb_enable_hw(drvdata); drvdata->pid = pid; - local_set(&csdev->mode, CS_MODE_PERF); + coresight_set_mode(csdev, CS_MODE_PERF); } return 0; @@ -297,7 +297,7 @@ static int smb_disable(struct coresight_device *csdev) /* Dissociate from the target process. */ drvdata->pid = -1; - local_set(&csdev->mode, CS_MODE_DISABLED); + coresight_set_mode(csdev, CS_MODE_DISABLED); dev_dbg(&csdev->dev, "Ultrasoc SMB disabled\n"); return 0; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a49e4e20e899..5f288d475490 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -596,6 +596,22 @@ static inline enum cs_mode coresight_get_mode(struct coresight_device *csdev) return local_read(&csdev->mode); } +static inline void coresight_set_mode(struct coresight_device *csdev, + enum cs_mode new_mode) +{ + enum cs_mode current_mode = coresight_get_mode(csdev); + + /* + * Changing to a new mode must be done from an already disabled state + * unless it's synchronized with coresight_take_mode(). Otherwise the + * device is already in use and signifies a locking issue. + */ + WARN(new_mode != CS_MODE_DISABLED && current_mode != CS_MODE_DISABLED && + current_mode != new_mode, "Device already in use\n"); + + local_set(&csdev->mode, new_mode); +} + extern struct coresight_device * coresight_register(struct coresight_desc *desc); extern void coresight_unregister(struct coresight_device *csdev); -- cgit v1.2.3 From 4ff4c745a16c4c151a71863420811e7f406c3ec2 Mon Sep 17 00:00:00 2001 From: Andrea Parri Date: Wed, 31 Jan 2024 15:49:35 +0100 Subject: locking: Introduce prepare_sync_core_cmd() Introduce an architecture function that architectures can use to set up ("prepare") SYNC_CORE commands. The function will be used by RISC-V to update its "deferred icache- flush" data structures (icache_stale_mask). Architectures defining prepare_sync_core_cmd() static inline need to select ARCH_HAS_PREPARE_SYNC_CORE_CMD. Suggested-by: Mathieu Desnoyers Signed-off-by: Andrea Parri Reviewed-by: Mathieu Desnoyers Link: https://lore.kernel.org/r/20240131144936.29190-4-parri.andrea@gmail.com Signed-off-by: Palmer Dabbelt --- include/linux/sync_core.h | 16 +++++++++++++++- init/Kconfig | 3 +++ kernel/sched/membarrier.c | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sync_core.h b/include/linux/sync_core.h index 013da4b8b327..67bb9794b875 100644 --- a/include/linux/sync_core.h +++ b/include/linux/sync_core.h @@ -17,5 +17,19 @@ static inline void sync_core_before_usermode(void) } #endif -#endif /* _LINUX_SYNC_CORE_H */ +#ifdef CONFIG_ARCH_HAS_PREPARE_SYNC_CORE_CMD +#include +#else +/* + * This is a dummy prepare_sync_core_cmd() implementation that can be used on + * all architectures which provide unconditional core serializing instructions + * in switch_mm(). + * If your architecture doesn't provide such core serializing instructions in + * switch_mm(), you may need to write your own functions. + */ +static inline void prepare_sync_core_cmd(struct mm_struct *mm) +{ +} +#endif +#endif /* _LINUX_SYNC_CORE_H */ diff --git a/init/Kconfig b/init/Kconfig index 8df18f3a9748..c3994b92333d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1970,6 +1970,9 @@ source "kernel/Kconfig.locks" config ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE bool +config ARCH_HAS_PREPARE_SYNC_CORE_CMD + bool + config ARCH_HAS_SYNC_CORE_BEFORE_USERMODE bool diff --git a/kernel/sched/membarrier.c b/kernel/sched/membarrier.c index f3d91628d6b8..6d1f31b3a967 100644 --- a/kernel/sched/membarrier.c +++ b/kernel/sched/membarrier.c @@ -320,6 +320,7 @@ static int membarrier_private_expedited(int flags, int cpu_id) MEMBARRIER_STATE_PRIVATE_EXPEDITED_SYNC_CORE_READY)) return -EPERM; ipi_func = ipi_sync_core; + prepare_sync_core_cmd(mm); } else if (flags == MEMBARRIER_FLAG_RSEQ) { if (!IS_ENABLED(CONFIG_RSEQ)) return -EINVAL; -- cgit v1.2.3 From 2444a80c1cc2c4240f60f2162abef3797c1803de Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 14 Feb 2024 08:48:28 +0000 Subject: kobject: make uevent_seqnum atomic We will soon no longer acquire uevent_sock_mutex for most kobject_uevent_net_broadcast() calls, and also while calling uevent_net_broadcast(). Make uevent_seqnum an atomic64_t to get its own protection. This fixes a race while reading /sys/kernel/uevent_seqnum. Signed-off-by: Eric Dumazet Cc: Greg Kroah-Hartman Cc: Christian Brauner Reviewed-by: Christian Brauner Link: https://lore.kernel.org/r/20240214084829.684541-2-edumazet@google.com Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 2 +- kernel/ksysfs.c | 2 +- lib/kobject_uevent.c | 17 +++++++++-------- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index c30affcc43b4..c8219505a79f 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -38,7 +38,7 @@ extern char uevent_helper[]; #endif /* counter to tag the uevent, read only except for the kobject core */ -extern u64 uevent_seqnum; +extern atomic64_t uevent_seqnum; /* * The actions here must match the index to the string array diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index 1d4bc493b2f4..32ae7fa74a9c 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -39,7 +39,7 @@ static struct kobj_attribute _name##_attr = __ATTR_RW(_name) static ssize_t uevent_seqnum_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sysfs_emit(buf, "%llu\n", (unsigned long long)uevent_seqnum); + return sysfs_emit(buf, "%llu\n", (u64)atomic64_read(&uevent_seqnum)); } KERNEL_ATTR_RO(uevent_seqnum); diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index fb9a2f06dd1e..9cb1a7fdaeba 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -30,7 +30,7 @@ #include -u64 uevent_seqnum; +atomic64_t uevent_seqnum; #ifdef CONFIG_UEVENT_HELPER char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; #endif @@ -44,7 +44,7 @@ struct uevent_sock { static LIST_HEAD(uevent_sock_list); #endif -/* This lock protects uevent_seqnum and uevent_sock_list */ +/* This lock protects uevent_sock_list */ static DEFINE_MUTEX(uevent_sock_mutex); /* the strings here must match the enum in include/linux/kobject.h */ @@ -583,13 +583,13 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, break; } - mutex_lock(&uevent_sock_mutex); /* we will send an event, so request a new sequence number */ - retval = add_uevent_var(env, "SEQNUM=%llu", ++uevent_seqnum); - if (retval) { - mutex_unlock(&uevent_sock_mutex); + retval = add_uevent_var(env, "SEQNUM=%llu", + atomic64_inc_return(&uevent_seqnum)); + if (retval) goto exit; - } + + mutex_lock(&uevent_sock_mutex); retval = kobject_uevent_net_broadcast(kobj, env, action_string, devpath); mutex_unlock(&uevent_sock_mutex); @@ -688,7 +688,8 @@ static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb, int ret; /* bump and prepare sequence number */ - ret = snprintf(buf, sizeof(buf), "SEQNUM=%llu", ++uevent_seqnum); + ret = snprintf(buf, sizeof(buf), "SEQNUM=%llu", + atomic64_inc_return(&uevent_seqnum)); if (ret < 0 || (size_t)ret >= sizeof(buf)) return -ENOMEM; ret++; -- cgit v1.2.3 From 99f638dd49ca80538addec6b3733ddb5784c9373 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 30 Jan 2024 13:23:37 +0100 Subject: usb: gadget: Support already-mapped DMA SGs Add a new 'sg_was_mapped' field to the struct usb_request. This field can be used to indicate that the scatterlist associated to the USB transfer has already been mapped into the DMA space, and it does not have to be done internally. Signed-off-by: Paul Cercueil Link: https://lore.kernel.org/r/20240130122340.54813-2-paul@crapouillou.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 7 ++++++- include/linux/usb/gadget.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index d59f94464b87..9d4150124fdb 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -903,6 +903,11 @@ int usb_gadget_map_request_by_dev(struct device *dev, if (req->length == 0) return 0; + if (req->sg_was_mapped) { + req->num_mapped_sgs = req->num_sgs; + return 0; + } + if (req->num_sgs) { int mapped; @@ -948,7 +953,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_map_request); void usb_gadget_unmap_request_by_dev(struct device *dev, struct usb_request *req, int is_in) { - if (req->length == 0) + if (req->length == 0 || req->sg_was_mapped) return; if (req->num_mapped_sgs) { diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index a771ccc038ac..c529e4e06997 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -52,6 +52,7 @@ struct usb_ep; * @short_not_ok: When reading data, makes short packets be * treated as errors (queue stops advancing till cleanup). * @dma_mapped: Indicates if request has been mapped to DMA (internal) + * @sg_was_mapped: Set if the scatterlist has been mapped before the request * @complete: Function called when request completes, so this request and * its buffer may be re-used. The function will always be called with * interrupts disabled, and it must not sleep. @@ -111,6 +112,7 @@ struct usb_request { unsigned zero:1; unsigned short_not_ok:1; unsigned dma_mapped:1; + unsigned sg_was_mapped:1; void (*complete)(struct usb_ep *ep, struct usb_request *req); -- cgit v1.2.3 From 7b07a2a7ca02a20124b552be96c5a56910795488 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 30 Jan 2024 13:23:39 +0100 Subject: usb: gadget: functionfs: Add DMABUF import interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces three new ioctls. They all should be called on a data endpoint (ie. not ep0). They are: - FUNCTIONFS_DMABUF_ATTACH, which takes the file descriptor of a DMABUF object to attach to the endpoint. - FUNCTIONFS_DMABUF_DETACH, which takes the file descriptor of the DMABUF to detach from the endpoint. Note that closing the endpoint's file descriptor will automatically detach all attached DMABUFs. - FUNCTIONFS_DMABUF_TRANSFER, which requests a data transfer from / to the given DMABUF. Its argument is a structure that packs the DMABUF's file descriptor, the size in bytes to transfer (which should generally be set to the size of the DMABUF), and a 'flags' field which is unused for now. Before this ioctl can be used, the related DMABUF must be attached with FUNCTIONFS_DMABUF_ATTACH. These three ioctls enable the FunctionFS code to transfer data between the USB stack and a DMABUF object, which can be provided by a driver from a completely different subsystem, in a zero-copy fashion. Signed-off-by: Paul Cercueil Acked-by: Daniel Vetter Acked-by: Christian König Link: https://lore.kernel.org/r/20240130122340.54813-4-paul@crapouillou.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/function/f_fs.c | 467 ++++++++++++++++++++++++++++++++++++ include/uapi/linux/usb/functionfs.h | 41 ++++ 3 files changed, 509 insertions(+) (limited to 'include') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b3592bcb0f96..566ff0b1282a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -190,6 +190,7 @@ config USB_F_MASS_STORAGE tristate config USB_F_FS + select DMA_SHARED_BUFFER tristate config USB_F_UAC1 diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 491a1e0f4fa3..bffbc1dc651f 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -15,6 +15,9 @@ /* #define VERBOSE_DEBUG */ #include +#include +#include +#include #include #include #include @@ -43,6 +46,8 @@ #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ +MODULE_IMPORT_NS(DMA_BUF); + /* Reference counter handling */ static void ffs_data_get(struct ffs_data *ffs); static void ffs_data_put(struct ffs_data *ffs); @@ -124,6 +129,25 @@ struct ffs_ep { u8 num; }; +struct ffs_dmabuf_priv { + struct list_head entry; + struct kref ref; + struct ffs_data *ffs; + struct dma_buf_attachment *attach; + struct sg_table *sgt; + enum dma_data_direction dir; + spinlock_t lock; + u64 context; + struct usb_request *req; /* P: ffs->eps_lock */ + struct usb_ep *ep; /* P: ffs->eps_lock */ +}; + +struct ffs_dma_fence { + struct dma_fence base; + struct ffs_dmabuf_priv *priv; + struct work_struct work; +}; + struct ffs_epfile { /* Protects ep->ep and ep->req. */ struct mutex mutex; @@ -197,6 +221,11 @@ struct ffs_epfile { unsigned char isoc; /* P: ffs->eps_lock */ unsigned char _pad; + + /* Protects dmabufs */ + struct mutex dmabufs_mutex; + struct list_head dmabufs; /* P: dmabufs_mutex */ + atomic_t seqno; }; struct ffs_buffer { @@ -1271,10 +1300,58 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) return res; } +static void ffs_dmabuf_release(struct kref *ref) +{ + struct ffs_dmabuf_priv *priv = container_of(ref, struct ffs_dmabuf_priv, ref); + struct dma_buf_attachment *attach = priv->attach; + struct dma_buf *dmabuf = attach->dmabuf; + + pr_vdebug("FFS DMABUF release\n"); + dma_resv_lock(dmabuf->resv, NULL); + dma_buf_unmap_attachment(attach, priv->sgt, priv->dir); + dma_resv_unlock(dmabuf->resv); + + dma_buf_detach(attach->dmabuf, attach); + dma_buf_put(dmabuf); + kfree(priv); +} + +static void ffs_dmabuf_get(struct dma_buf_attachment *attach) +{ + struct ffs_dmabuf_priv *priv = attach->importer_priv; + + kref_get(&priv->ref); +} + +static void ffs_dmabuf_put(struct dma_buf_attachment *attach) +{ + struct ffs_dmabuf_priv *priv = attach->importer_priv; + + kref_put(&priv->ref, ffs_dmabuf_release); +} + static int ffs_epfile_release(struct inode *inode, struct file *file) { struct ffs_epfile *epfile = inode->i_private; + struct ffs_dmabuf_priv *priv, *tmp; + struct ffs_data *ffs = epfile->ffs; + + mutex_lock(&epfile->dmabufs_mutex); + + /* Close all attached DMABUFs */ + list_for_each_entry_safe(priv, tmp, &epfile->dmabufs, entry) { + /* Cancel any pending transfer */ + spin_lock_irq(&ffs->eps_lock); + if (priv->ep && priv->req) + usb_ep_dequeue(priv->ep, priv->req); + spin_unlock_irq(&ffs->eps_lock); + + list_del(&priv->entry); + ffs_dmabuf_put(priv->attach); + } + + mutex_unlock(&epfile->dmabufs_mutex); __ffs_epfile_read_buffer_free(epfile); ffs_data_closed(epfile->ffs); @@ -1282,6 +1359,356 @@ ffs_epfile_release(struct inode *inode, struct file *file) return 0; } +static void ffs_dmabuf_cleanup(struct work_struct *work) +{ + struct ffs_dma_fence *dma_fence = + container_of(work, struct ffs_dma_fence, work); + struct ffs_dmabuf_priv *priv = dma_fence->priv; + struct dma_buf_attachment *attach = priv->attach; + struct dma_fence *fence = &dma_fence->base; + + ffs_dmabuf_put(attach); + dma_fence_put(fence); +} + +static void ffs_dmabuf_signal_done(struct ffs_dma_fence *dma_fence, int ret) +{ + struct ffs_dmabuf_priv *priv = dma_fence->priv; + struct dma_fence *fence = &dma_fence->base; + bool cookie = dma_fence_begin_signalling(); + + dma_fence_get(fence); + fence->error = ret; + dma_fence_signal(fence); + dma_fence_end_signalling(cookie); + + /* + * The fence will be unref'd in ffs_dmabuf_cleanup. + * It can't be done here, as the unref functions might try to lock + * the resv object, which would deadlock. + */ + INIT_WORK(&dma_fence->work, ffs_dmabuf_cleanup); + queue_work(priv->ffs->io_completion_wq, &dma_fence->work); +} + +static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep, + struct usb_request *req) +{ + pr_vdebug("FFS: DMABUF transfer complete, status=%d\n", req->status); + ffs_dmabuf_signal_done(req->context, req->status); + usb_ep_free_request(ep, req); +} + +static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence) +{ + return "functionfs"; +} + +static const char *ffs_dmabuf_get_timeline_name(struct dma_fence *fence) +{ + return ""; +} + +static void ffs_dmabuf_fence_release(struct dma_fence *fence) +{ + struct ffs_dma_fence *dma_fence = + container_of(fence, struct ffs_dma_fence, base); + + kfree(dma_fence); +} + +static const struct dma_fence_ops ffs_dmabuf_fence_ops = { + .get_driver_name = ffs_dmabuf_get_driver_name, + .get_timeline_name = ffs_dmabuf_get_timeline_name, + .release = ffs_dmabuf_fence_release, +}; + +static int ffs_dma_resv_lock(struct dma_buf *dmabuf, bool nonblock) +{ + if (!nonblock) + return dma_resv_lock_interruptible(dmabuf->resv, NULL); + + if (!dma_resv_trylock(dmabuf->resv)) + return -EBUSY; + + return 0; +} + +static struct dma_buf_attachment * +ffs_dmabuf_find_attachment(struct ffs_epfile *epfile, struct dma_buf *dmabuf) +{ + struct device *dev = epfile->ffs->gadget->dev.parent; + struct dma_buf_attachment *attach = NULL; + struct ffs_dmabuf_priv *priv; + + mutex_lock(&epfile->dmabufs_mutex); + + list_for_each_entry(priv, &epfile->dmabufs, entry) { + if (priv->attach->dev == dev + && priv->attach->dmabuf == dmabuf) { + attach = priv->attach; + break; + } + } + + if (attach) + ffs_dmabuf_get(attach); + + mutex_unlock(&epfile->dmabufs_mutex); + + return attach ?: ERR_PTR(-EPERM); +} + +static int ffs_dmabuf_attach(struct file *file, int fd) +{ + bool nonblock = file->f_flags & O_NONBLOCK; + struct ffs_epfile *epfile = file->private_data; + struct usb_gadget *gadget = epfile->ffs->gadget; + struct dma_buf_attachment *attach; + struct ffs_dmabuf_priv *priv; + enum dma_data_direction dir; + struct sg_table *sg_table; + struct dma_buf *dmabuf; + int err; + + if (!gadget || !gadget->sg_supported) + return -EPERM; + + dmabuf = dma_buf_get(fd); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + attach = dma_buf_attach(dmabuf, gadget->dev.parent); + if (IS_ERR(attach)) { + err = PTR_ERR(attach); + goto err_dmabuf_put; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + goto err_dmabuf_detach; + } + + dir = epfile->in ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + err = ffs_dma_resv_lock(dmabuf, nonblock); + if (err) + goto err_free_priv; + + sg_table = dma_buf_map_attachment(attach, dir); + dma_resv_unlock(dmabuf->resv); + + if (IS_ERR(sg_table)) { + err = PTR_ERR(sg_table); + goto err_free_priv; + } + + attach->importer_priv = priv; + + priv->sgt = sg_table; + priv->dir = dir; + priv->ffs = epfile->ffs; + priv->attach = attach; + spin_lock_init(&priv->lock); + kref_init(&priv->ref); + priv->context = dma_fence_context_alloc(1); + + mutex_lock(&epfile->dmabufs_mutex); + list_add(&priv->entry, &epfile->dmabufs); + mutex_unlock(&epfile->dmabufs_mutex); + + return 0; + +err_free_priv: + kfree(priv); +err_dmabuf_detach: + dma_buf_detach(dmabuf, attach); +err_dmabuf_put: + dma_buf_put(dmabuf); + + return err; +} + +static int ffs_dmabuf_detach(struct file *file, int fd) +{ + struct ffs_epfile *epfile = file->private_data; + struct ffs_data *ffs = epfile->ffs; + struct device *dev = ffs->gadget->dev.parent; + struct ffs_dmabuf_priv *priv, *tmp; + struct dma_buf *dmabuf; + int ret = -EPERM; + + dmabuf = dma_buf_get(fd); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + mutex_lock(&epfile->dmabufs_mutex); + + list_for_each_entry_safe(priv, tmp, &epfile->dmabufs, entry) { + if (priv->attach->dev == dev + && priv->attach->dmabuf == dmabuf) { + /* Cancel any pending transfer */ + spin_lock_irq(&ffs->eps_lock); + if (priv->ep && priv->req) + usb_ep_dequeue(priv->ep, priv->req); + spin_unlock_irq(&ffs->eps_lock); + + list_del(&priv->entry); + + /* Unref the reference from ffs_dmabuf_attach() */ + ffs_dmabuf_put(priv->attach); + ret = 0; + break; + } + } + + mutex_unlock(&epfile->dmabufs_mutex); + dma_buf_put(dmabuf); + + return ret; +} + +static int ffs_dmabuf_transfer(struct file *file, + const struct usb_ffs_dmabuf_transfer_req *req) +{ + bool nonblock = file->f_flags & O_NONBLOCK; + struct ffs_epfile *epfile = file->private_data; + struct dma_buf_attachment *attach; + struct ffs_dmabuf_priv *priv; + struct ffs_dma_fence *fence; + struct usb_request *usb_req; + struct dma_buf *dmabuf; + struct ffs_ep *ep; + bool cookie; + u32 seqno; + int ret; + + if (req->flags & ~USB_FFS_DMABUF_TRANSFER_MASK) + return -EINVAL; + + dmabuf = dma_buf_get(req->fd); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + if (req->length > dmabuf->size || req->length == 0) { + ret = -EINVAL; + goto err_dmabuf_put; + } + + attach = ffs_dmabuf_find_attachment(epfile, dmabuf); + if (IS_ERR(attach)) { + ret = PTR_ERR(attach); + goto err_dmabuf_put; + } + + priv = attach->importer_priv; + + ep = ffs_epfile_wait_ep(file); + if (IS_ERR(ep)) { + ret = PTR_ERR(ep); + goto err_attachment_put; + } + + ret = ffs_dma_resv_lock(dmabuf, nonblock); + if (ret) + goto err_attachment_put; + + /* Make sure we don't have writers */ + if (!dma_resv_test_signaled(dmabuf->resv, DMA_RESV_USAGE_WRITE)) { + pr_vdebug("FFS WRITE fence is not signaled\n"); + ret = -EBUSY; + goto err_resv_unlock; + } + + /* If we're writing to the DMABUF, make sure we don't have readers */ + if (epfile->in && + !dma_resv_test_signaled(dmabuf->resv, DMA_RESV_USAGE_READ)) { + pr_vdebug("FFS READ fence is not signaled\n"); + ret = -EBUSY; + goto err_resv_unlock; + } + + ret = dma_resv_reserve_fences(dmabuf->resv, 1); + if (ret) + goto err_resv_unlock; + + fence = kmalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) { + ret = -ENOMEM; + goto err_resv_unlock; + } + + fence->priv = priv; + + spin_lock_irq(&epfile->ffs->eps_lock); + + /* In the meantime, endpoint got disabled or changed. */ + if (epfile->ep != ep) { + ret = -ESHUTDOWN; + goto err_fence_put; + } + + usb_req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC); + if (!usb_req) { + ret = -ENOMEM; + goto err_fence_put; + } + + /* + * usb_ep_queue() guarantees that all transfers are processed in the + * order they are enqueued, so we can use a simple incrementing + * sequence number for the dma_fence. + */ + seqno = atomic_add_return(1, &epfile->seqno); + + dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops, + &priv->lock, priv->context, seqno); + + dma_resv_add_fence(dmabuf->resv, &fence->base, + dma_resv_usage_rw(epfile->in)); + dma_resv_unlock(dmabuf->resv); + + /* Now that the dma_fence is in place, queue the transfer. */ + + usb_req->length = req->length; + usb_req->buf = NULL; + usb_req->sg = priv->sgt->sgl; + usb_req->num_sgs = sg_nents_for_len(priv->sgt->sgl, req->length); + usb_req->sg_was_mapped = true; + usb_req->context = fence; + usb_req->complete = ffs_epfile_dmabuf_io_complete; + + cookie = dma_fence_begin_signalling(); + ret = usb_ep_queue(ep->ep, usb_req, GFP_ATOMIC); + dma_fence_end_signalling(cookie); + if (!ret) { + priv->req = usb_req; + priv->ep = ep->ep; + } else { + pr_warn("FFS: Failed to queue DMABUF: %d\n", ret); + ffs_dmabuf_signal_done(fence, ret); + usb_ep_free_request(ep->ep, usb_req); + } + + spin_unlock_irq(&epfile->ffs->eps_lock); + dma_buf_put(dmabuf); + + return ret; + +err_fence_put: + spin_unlock_irq(&epfile->ffs->eps_lock); + dma_fence_put(&fence->base); +err_resv_unlock: + dma_resv_unlock(dmabuf->resv); +err_attachment_put: + ffs_dmabuf_put(attach); +err_dmabuf_put: + dma_buf_put(dmabuf); + + return ret; +} + static long ffs_epfile_ioctl(struct file *file, unsigned code, unsigned long value) { @@ -1292,6 +1719,44 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) return -ENODEV; + switch (code) { + case FUNCTIONFS_DMABUF_ATTACH: + { + int fd; + + if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) { + ret = -EFAULT; + break; + } + + return ffs_dmabuf_attach(file, fd); + } + case FUNCTIONFS_DMABUF_DETACH: + { + int fd; + + if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) { + ret = -EFAULT; + break; + } + + return ffs_dmabuf_detach(file, fd); + } + case FUNCTIONFS_DMABUF_TRANSFER: + { + struct usb_ffs_dmabuf_transfer_req req; + + if (copy_from_user(&req, (void __user *)value, sizeof(req))) { + ret = -EFAULT; + break; + } + + return ffs_dmabuf_transfer(file, &req); + } + default: + break; + } + /* Wait for endpoint to be enabled */ ep = ffs_epfile_wait_ep(file); if (IS_ERR(ep)) @@ -1869,6 +2334,8 @@ static int ffs_epfiles_create(struct ffs_data *ffs) for (i = 1; i <= count; ++i, ++epfile) { epfile->ffs = ffs; mutex_init(&epfile->mutex); + mutex_init(&epfile->dmabufs_mutex); + INIT_LIST_HEAD(&epfile->dmabufs); if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); else diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 078098e73fd3..9f88de9c3d66 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -86,6 +86,22 @@ struct usb_ext_prop_desc { __le16 wPropertyNameLength; } __attribute__((packed)); +/* Flags for usb_ffs_dmabuf_transfer_req->flags (none for now) */ +#define USB_FFS_DMABUF_TRANSFER_MASK 0x0 + +/** + * struct usb_ffs_dmabuf_transfer_req - Transfer request for a DMABUF object + * @fd: file descriptor of the DMABUF object + * @flags: one or more USB_FFS_DMABUF_TRANSFER_* flags + * @length: number of bytes used in this DMABUF for the data transfer. + * Should generally be set to the DMABUF's size. + */ +struct usb_ffs_dmabuf_transfer_req { + int fd; + __u32 flags; + __u64 length; +} __attribute__((packed)); + #ifndef __KERNEL__ /* @@ -290,6 +306,31 @@ struct usb_functionfs_event { #define FUNCTIONFS_ENDPOINT_DESC _IOR('g', 130, \ struct usb_endpoint_descriptor) +/* + * Attach the DMABUF object, identified by its file descriptor, to the + * data endpoint. Returns zero on success, and a negative errno value + * on error. + */ +#define FUNCTIONFS_DMABUF_ATTACH _IOW('g', 131, int) + +/* + * Detach the given DMABUF object, identified by its file descriptor, + * from the data endpoint. Returns zero on success, and a negative + * errno value on error. Note that closing the endpoint's file + * descriptor will automatically detach all attached DMABUFs. + */ +#define FUNCTIONFS_DMABUF_DETACH _IOW('g', 132, int) + +/* + * Enqueue the previously attached DMABUF to the transfer queue. + * The argument is a structure that packs the DMABUF's file descriptor, + * the size in bytes to transfer (which should generally correspond to + * the size of the DMABUF), and a 'flags' field which is unused + * for now. Returns zero on success, and a negative errno value on + * error. + */ +#define FUNCTIONFS_DMABUF_TRANSFER _IOW('g', 133, \ + struct usb_ffs_dmabuf_transfer_req) #endif /* _UAPI__LINUX_FUNCTIONFS_H__ */ -- cgit v1.2.3 From 1dae0cb79ceacbdc7495c5f83ca71e1c12a24d7c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Jan 2024 15:05:28 +0000 Subject: iio: locking: introduce __cleanup() based direct mode claiming infrastructure Allows use of: iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { } to automatically call iio_device_release_direct_mode() based on scope. Typically seen in combination with local device specific locks which are already have automated cleanup options via guard(mutex)(&st->lock) and scoped_guard(). Using both together allows most error handling to be automated. Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20240128150537.44592-2-jic23@kernel.org Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index c5b36d2c1e73..4f89279e531c 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -9,6 +9,7 @@ #include #include +#include #include #include /* IIO TODO LIST */ @@ -638,6 +639,33 @@ int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev, int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp); int iio_device_claim_direct_mode(struct iio_dev *indio_dev); void iio_device_release_direct_mode(struct iio_dev *indio_dev); + +/* + * This autocleanup logic is normally used via + * iio_device_claim_direct_scoped(). + */ +DEFINE_GUARD(iio_claim_direct, struct iio_dev *, iio_device_claim_direct_mode(_T), + iio_device_release_direct_mode(_T)) + +DEFINE_GUARD_COND(iio_claim_direct, _try, ({ + struct iio_dev *dev; + int d = iio_device_claim_direct_mode(_T); + + if (d < 0) + dev = NULL; + else + dev = _T; + dev; + })) + +/** + * iio_device_claim_direct_scoped() - Scoped call to iio_device_claim_direct. + * @fail: What to do on failure to claim device. + * @iio_dev: Pointer to the IIO devices structure + */ +#define iio_device_claim_direct_scoped(fail, iio_dev) \ + scoped_cond_guard(iio_claim_direct_try, fail, iio_dev) + int iio_device_claim_buffer_mode(struct iio_dev *indio_dev); void iio_device_release_buffer_mode(struct iio_dev *indio_dev); -- cgit v1.2.3 From 89b1b86fc77367fac470258acdf470ffe2edc8d4 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Thu, 8 Feb 2024 16:37:19 -0300 Subject: iio: core: make iio_bus_type const Now that the driver core can properly handle constant struct bus_type, move the iio_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20240208-bus_cleanup-iio-v1-1-4a167c3b5fb3@marliere.net Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 +- include/linux/iio/iio.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index e8551a1636ba..9b2877fe8689 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -42,7 +42,7 @@ static DEFINE_IDA(iio_ida); static dev_t iio_devt; #define IIO_DEV_MAX 256 -struct bus_type iio_bus_type = { +const struct bus_type iio_bus_type = { .name = "iio", }; EXPORT_SYMBOL(iio_bus_type); diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 4f89279e531c..e370a7bb3300 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -669,7 +669,7 @@ DEFINE_GUARD_COND(iio_claim_direct, _try, ({ int iio_device_claim_buffer_mode(struct iio_dev *indio_dev); void iio_device_release_buffer_mode(struct iio_dev *indio_dev); -extern struct bus_type iio_bus_type; +extern const struct bus_type iio_bus_type; /** * iio_device_put() - reference counted deallocation of struct device -- cgit v1.2.3 From 3765d426fe864e109d00aeb48f22413b896b2eb9 Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Mon, 5 Feb 2024 10:59:25 -0800 Subject: iio: hid-sensor-als: Add light color temperature support On some platforms, ambient color sensors also support light color temperature. Add support of light color temperature. Signed-off-by: Basavaraj Natikar Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20240205185926.3030521-4-srinivas.pandruvada@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 21 +++++++++++++++++++++ include/linux/hid-sensor-ids.h | 1 + 2 files changed, 22 insertions(+) (limited to 'include') diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index bd4cb0883121..f9d0071d9fa9 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -16,6 +16,7 @@ enum { CHANNEL_SCAN_INDEX_INTENSITY, CHANNEL_SCAN_INDEX_ILLUM, + CHANNEL_SCAN_INDEX_COLOR_TEMP, CHANNEL_SCAN_INDEX_MAX }; @@ -43,6 +44,7 @@ struct als_state { static const u32 als_usage_ids[] = { HID_USAGE_SENSOR_LIGHT_ILLUM, HID_USAGE_SENSOR_LIGHT_ILLUM, + HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE, }; static const u32 als_sensitivity_addresses[] = { @@ -74,6 +76,16 @@ static const struct iio_chan_spec als_channels[] = { BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), .scan_index = CHANNEL_SCAN_INDEX_ILLUM, }, + { + .type = IIO_COLORTEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), + .scan_index = CHANNEL_SCAN_INDEX_COLOR_TEMP, + }, IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) }; @@ -112,6 +124,11 @@ static int als_read_raw(struct iio_dev *indio_dev, min = als_state->als[chan->scan_index].logical_minimum; address = HID_USAGE_SENSOR_LIGHT_ILLUM; break; + case CHANNEL_SCAN_INDEX_COLOR_TEMP: + report_id = als_state->als[chan->scan_index].report_id; + min = als_state->als[chan->scan_index].logical_minimum; + address = HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE; + break; default: report_id = -1; break; @@ -232,6 +249,10 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, als_state->scan.illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data; ret = 0; break; + case HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE: + als_state->scan.illum[CHANNEL_SCAN_INDEX_COLOR_TEMP] = sample_data; + ret = 0; + break; case HID_USAGE_SENSOR_TIME_TIMESTAMP: als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes, *(s64 *)raw_data); diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 13b1e65fbdcc..8af4fb3e0254 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -21,6 +21,7 @@ #define HID_USAGE_SENSOR_ALS 0x200041 #define HID_USAGE_SENSOR_DATA_LIGHT 0x2004d0 #define HID_USAGE_SENSOR_LIGHT_ILLUM 0x2004d1 +#define HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE 0x2004d2 /* PROX (200011) */ #define HID_USAGE_SENSOR_PROX 0x200011 -- cgit v1.2.3 From 2ec17b1950bb824f9a8d5f055e466d02c40eb64c Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Mon, 5 Feb 2024 10:59:26 -0800 Subject: iio: hid-sensor-als: Add light chromaticity support On some platforms, ambient color sensors also support the x and y light colors, which represent the coordinates on the CIE 1931 chromaticity diagram. Add light chromaticity x and y. Signed-off-by: Basavaraj Natikar Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20240205185926.3030521-5-srinivas.pandruvada@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 46 ++++++++++++++++++++++++++++++++++++++ include/linux/hid-sensor-ids.h | 3 +++ 2 files changed, 49 insertions(+) (limited to 'include') diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index f9d0071d9fa9..06580dfdfab6 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -17,6 +17,8 @@ enum { CHANNEL_SCAN_INDEX_INTENSITY, CHANNEL_SCAN_INDEX_ILLUM, CHANNEL_SCAN_INDEX_COLOR_TEMP, + CHANNEL_SCAN_INDEX_CHROMATICITY_X, + CHANNEL_SCAN_INDEX_CHROMATICITY_Y, CHANNEL_SCAN_INDEX_MAX }; @@ -45,6 +47,8 @@ static const u32 als_usage_ids[] = { HID_USAGE_SENSOR_LIGHT_ILLUM, HID_USAGE_SENSOR_LIGHT_ILLUM, HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE, + HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X, + HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y, }; static const u32 als_sensitivity_addresses[] = { @@ -86,6 +90,30 @@ static const struct iio_chan_spec als_channels[] = { BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), .scan_index = CHANNEL_SCAN_INDEX_COLOR_TEMP, }, + { + .type = IIO_CHROMATICITY, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), + .scan_index = CHANNEL_SCAN_INDEX_CHROMATICITY_X, + }, + { + .type = IIO_CHROMATICITY, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), + .scan_index = CHANNEL_SCAN_INDEX_CHROMATICITY_Y, + }, IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) }; @@ -129,6 +157,16 @@ static int als_read_raw(struct iio_dev *indio_dev, min = als_state->als[chan->scan_index].logical_minimum; address = HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE; break; + case CHANNEL_SCAN_INDEX_CHROMATICITY_X: + report_id = als_state->als[chan->scan_index].report_id; + min = als_state->als[chan->scan_index].logical_minimum; + address = HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X; + break; + case CHANNEL_SCAN_INDEX_CHROMATICITY_Y: + report_id = als_state->als[chan->scan_index].report_id; + min = als_state->als[chan->scan_index].logical_minimum; + address = HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y; + break; default: report_id = -1; break; @@ -253,6 +291,14 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, als_state->scan.illum[CHANNEL_SCAN_INDEX_COLOR_TEMP] = sample_data; ret = 0; break; + case HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X: + als_state->scan.illum[CHANNEL_SCAN_INDEX_CHROMATICITY_X] = sample_data; + ret = 0; + break; + case HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y: + als_state->scan.illum[CHANNEL_SCAN_INDEX_CHROMATICITY_Y] = sample_data; + ret = 0; + break; case HID_USAGE_SENSOR_TIME_TIMESTAMP: als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes, *(s64 *)raw_data); diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 8af4fb3e0254..6730ee900ee1 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -22,6 +22,9 @@ #define HID_USAGE_SENSOR_DATA_LIGHT 0x2004d0 #define HID_USAGE_SENSOR_LIGHT_ILLUM 0x2004d1 #define HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE 0x2004d2 +#define HID_USAGE_SENSOR_LIGHT_CHROMATICITY 0x2004d3 +#define HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X 0x2004d4 +#define HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y 0x2004d5 /* PROX (200011) */ #define HID_USAGE_SENSOR_PROX 0x200011 -- cgit v1.2.3 From 548fcf037b3f8592e9fe41469110453a777416d6 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Thu, 15 Feb 2024 13:15:38 +0200 Subject: tty: Don't include tty_buffer.h in tty.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no need to include linux/tty_buffer.h in linux/tty.h. Move the include into tty_buffer.c that is actually using it. Signed-off-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240215111538.1920-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 1 + include/linux/tty.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index f8883afbeeba..79f0ff94ce00 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/tty.h b/include/linux/tty.h index 8c76fd97d4ad..2b2e6f0a54d6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From d87c295f599cab2ab3b3df53a9098adba4a6002b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 30 Jan 2024 10:46:27 -0800 Subject: sysfs: Introduce a mechanism to hide static attribute_groups Add a mechanism for named attribute_groups to hide their directory at sysfs_update_group() time, or otherwise skip emitting the group directory when the group is first registered. It piggybacks on is_visible() in a similar manner as SYSFS_PREALLOC, i.e. special flags in the upper bits of the returned mode. To use it, specify a symbol prefix to DEFINE_SYSFS_GROUP_VISIBLE(), and then pass that same prefix to SYSFS_GROUP_VISIBLE() when assigning the @is_visible() callback: DEFINE_SYSFS_GROUP_VISIBLE($prefix) struct attribute_group $prefix_group = { .name = $name, .is_visible = SYSFS_GROUP_VISIBLE($prefix), }; SYSFS_GROUP_VISIBLE() expects a definition of $prefix_group_visible() and $prefix_attr_visible(), where $prefix_group_visible() just returns true / false and $prefix_attr_visible() behaves as normal. The motivation for this capability is to centralize PCI device authentication in the PCI core with a named sysfs group while keeping that group hidden for devices and platforms that do not meet the requirements. In a PCI topology, most devices will not support authentication, a small subset will support just PCI CMA (Component Measurement and Authentication), a smaller subset will support PCI CMA + PCIe IDE (Link Integrity and Encryption), and only next generation server hosts will start to include a platform TSM (TEE Security Manager). Without this capability the alternatives are: * Check if all attributes are invisible and if so, hide the directory. Beyond trouble getting this to work [1], this is an ABI change for scenarios if userspace happens to depend on group visibility absent any attributes. I.e. this new capability avoids regression since it does not retroactively apply to existing cases. * Publish an empty /sys/bus/pci/devices/$pdev/tsm/ directory for all PCI devices (i.e. for the case when TSM platform support is present, but device support is absent). Unfortunate that this will be a vestigial empty directory in the vast majority of cases. * Reintroduce usage of runtime calls to sysfs_{create,remove}_group() in the PCI core. Bjorn has already indicated that he does not want to see any growth of pci_sysfs_init() [2]. * Drop the named group and simulate a directory by prefixing all TSM-related attributes with "tsm_". Unfortunate to not use the naming capability of a sysfs group as intended. In comparison, there is a small potential for regression if for some reason an @is_visible() callback had dependencies on how many times it was called. Additionally, it is no longer an error to update a group that does not have its directory already present, and it is no longer a WARN() to remove a group that was never visible. Link: https://lore.kernel.org/all/2024012321-envious-procedure-4a58@gregkh/ [1] Link: https://lore.kernel.org/linux-pci/20231019200110.GA1410324@bhelgaas/ [2] Signed-off-by: Dan Williams Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/2024013028-deflator-flaring-ec62@gregkh Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/group.c | 45 ++++++++++++++++++++++++++++-------- include/linux/sysfs.h | 63 +++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 87 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 138676463336..ccb275cdabcb 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -31,6 +31,17 @@ static void remove_files(struct kernfs_node *parent, kernfs_remove_by_name(parent, (*bin_attr)->attr.name); } +static umode_t __first_visible(const struct attribute_group *grp, struct kobject *kobj) +{ + if (grp->attrs && grp->is_visible) + return grp->is_visible(kobj, grp->attrs[0], 0); + + if (grp->bin_attrs && grp->is_bin_visible) + return grp->is_bin_visible(kobj, grp->bin_attrs[0], 0); + + return 0; +} + static int create_files(struct kernfs_node *parent, struct kobject *kobj, kuid_t uid, kgid_t gid, const struct attribute_group *grp, int update) @@ -52,6 +63,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, kernfs_remove_by_name(parent, (*attr)->name); if (grp->is_visible) { mode = grp->is_visible(kobj, *attr, i); + mode &= ~SYSFS_GROUP_INVISIBLE; if (!mode) continue; } @@ -81,6 +93,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, (*bin_attr)->attr.name); if (grp->is_bin_visible) { mode = grp->is_bin_visible(kobj, *bin_attr, i); + mode &= ~SYSFS_GROUP_INVISIBLE; if (!mode) continue; } @@ -127,16 +140,31 @@ static int internal_create_group(struct kobject *kobj, int update, kobject_get_ownership(kobj, &uid, &gid); if (grp->name) { + umode_t mode = __first_visible(grp, kobj); + + if (mode & SYSFS_GROUP_INVISIBLE) + mode = 0; + else + mode = S_IRWXU | S_IRUGO | S_IXUGO; + if (update) { kn = kernfs_find_and_get(kobj->sd, grp->name); if (!kn) { - pr_warn("Can't update unknown attr grp name: %s/%s\n", - kobj->name, grp->name); - return -EINVAL; + pr_debug("attr grp %s/%s not created yet\n", + kobj->name, grp->name); + /* may have been invisible prior to this update */ + update = 0; + } else if (!mode) { + sysfs_remove_group(kobj, grp); + kernfs_put(kn); + return 0; } - } else { - kn = kernfs_create_dir_ns(kobj->sd, grp->name, - S_IRWXU | S_IRUGO | S_IXUGO, + } + + if (!update) { + if (!mode) + return 0; + kn = kernfs_create_dir_ns(kobj->sd, grp->name, mode, uid, gid, kobj, NULL); if (IS_ERR(kn)) { if (PTR_ERR(kn) == -EEXIST) @@ -279,9 +307,8 @@ void sysfs_remove_group(struct kobject *kobj, if (grp->name) { kn = kernfs_find_and_get(parent, grp->name); if (!kn) { - WARN(!kn, KERN_WARNING - "sysfs group '%s' not found for kobject '%s'\n", - grp->name, kobject_name(kobj)); + pr_debug("sysfs group '%s' not found for kobject '%s'\n", + grp->name, kobject_name(kobj)); return; } } else { diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index b717a70219f6..a42642b277dd 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -61,22 +61,32 @@ do { \ /** * struct attribute_group - data structure used to declare an attribute group. * @name: Optional: Attribute group name - * If specified, the attribute group will be created in - * a new subdirectory with this name. + * If specified, the attribute group will be created in a + * new subdirectory with this name. Additionally when a + * group is named, @is_visible and @is_bin_visible may + * return SYSFS_GROUP_INVISIBLE to control visibility of + * the directory itself. * @is_visible: Optional: Function to return permissions associated with an - * attribute of the group. Will be called repeatedly for each - * non-binary attribute in the group. Only read/write + * attribute of the group. Will be called repeatedly for + * each non-binary attribute in the group. Only read/write * permissions as well as SYSFS_PREALLOC are accepted. Must - * return 0 if an attribute is not visible. The returned value - * will replace static permissions defined in struct attribute. + * return 0 if an attribute is not visible. The returned + * value will replace static permissions defined in struct + * attribute. Use SYSFS_GROUP_VISIBLE() when assigning this + * callback to specify separate _group_visible() and + * _attr_visible() handlers. * @is_bin_visible: * Optional: Function to return permissions associated with a * binary attribute of the group. Will be called repeatedly * for each binary attribute in the group. Only read/write - * permissions as well as SYSFS_PREALLOC are accepted. Must - * return 0 if a binary attribute is not visible. The returned - * value will replace static permissions defined in - * struct bin_attribute. + * permissions as well as SYSFS_PREALLOC (and the + * visibility flags for named groups) are accepted. Must + * return 0 if a binary attribute is not visible. The + * returned value will replace static permissions defined + * in struct bin_attribute. If @is_visible is not set, Use + * SYSFS_GROUP_VISIBLE() when assigning this callback to + * specify separate _group_visible() and _attr_visible() + * handlers. * @attrs: Pointer to NULL terminated list of attributes. * @bin_attrs: Pointer to NULL terminated list of binary attributes. * Either attrs or bin_attrs or both must be provided. @@ -91,13 +101,42 @@ struct attribute_group { struct bin_attribute **bin_attrs; }; +#define SYSFS_PREALLOC 010000 +#define SYSFS_GROUP_INVISIBLE 020000 + +/* + * The first call to is_visible() in the create / update path may + * indicate visibility for the entire group + */ +#define DEFINE_SYSFS_GROUP_VISIBLE(name) \ + static inline umode_t sysfs_group_visible_##name( \ + struct kobject *kobj, struct attribute *attr, int n) \ + { \ + if (n == 0 && !name##_group_visible(kobj)) \ + return SYSFS_GROUP_INVISIBLE; \ + return name##_attr_visible(kobj, attr, n); \ + } + +/* + * Same as DEFINE_SYSFS_GROUP_VISIBLE, but for groups with only binary + * attributes + */ +#define DEFINE_SYSFS_BIN_GROUP_VISIBLE(name) \ + static inline umode_t sysfs_group_visible_##name( \ + struct kobject *kobj, struct bin_attribute *attr, int n) \ + { \ + if (n == 0 && !name##_group_visible(kobj)) \ + return SYSFS_GROUP_INVISIBLE; \ + return name##_attr_visible(kobj, attr, n); \ + } + +#define SYSFS_GROUP_VISIBLE(fn) sysfs_group_visible_##fn + /* * Use these macros to make defining attributes easier. * See include/linux/device.h for examples.. */ -#define SYSFS_PREALLOC 010000 - #define __ATTR(_name, _mode, _show, _store) { \ .attr = {.name = __stringify(_name), \ .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ -- cgit v1.2.3 From 9c446288d7b31402adb454535cb2c3cbdb55bb88 Mon Sep 17 00:00:00 2001 From: Nuno Sa Date: Sat, 10 Feb 2024 21:57:16 +0100 Subject: iio: buffer-dmaengine: export buffer alloc and free functions Export iio_dmaengine_buffer_free() and iio_dmaengine_buffer_alloc(). This is in preparation of introducing IIO backends support. This will allow us to allocate a buffer and control it's lifetime from a device different from the one holding the DMA firmware properties. Effectively, in this case the struct device holding the firmware information about the DMA channels is not the same as iio_dev->dev.parent (typical case). While at it, namespace the buffer-dmaengine exports and update the current user of these buffers. Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20240210-iio-backend-v11-4-f5242a5fb42a@analog.com Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/adi-axi-adc.c | 1 + drivers/iio/buffer/industrialio-buffer-dmaengine.c | 8 +++++--- include/linux/iio/buffer-dmaengine.h | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index c247ff1541d2..0f21d1d98b9f 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -447,3 +447,4 @@ module_platform_driver(adi_axi_adc_driver); MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices Generic AXI ADC IP core driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER); diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 45fe7d0d42ee..a18c1da292af 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -159,7 +159,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = { * Once done using the buffer iio_dmaengine_buffer_free() should be used to * release it. */ -static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, +struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, const char *channel) { struct dmaengine_buffer *dmaengine_buffer; @@ -210,6 +210,7 @@ err_free: kfree(dmaengine_buffer); return ERR_PTR(ret); } +EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_alloc, IIO_DMAENGINE_BUFFER); /** * iio_dmaengine_buffer_free() - Free dmaengine buffer @@ -217,7 +218,7 @@ err_free: * * Frees a buffer previously allocated with iio_dmaengine_buffer_alloc(). */ -static void iio_dmaengine_buffer_free(struct iio_buffer *buffer) +void iio_dmaengine_buffer_free(struct iio_buffer *buffer) { struct dmaengine_buffer *dmaengine_buffer = iio_buffer_to_dmaengine_buffer(buffer); @@ -227,6 +228,7 @@ static void iio_dmaengine_buffer_free(struct iio_buffer *buffer) iio_buffer_put(buffer); } +EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, IIO_DMAENGINE_BUFFER); static void __devm_iio_dmaengine_buffer_free(void *buffer) { @@ -287,7 +289,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev, return iio_device_attach_buffer(indio_dev, buffer); } -EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup); +EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("DMA buffer for the IIO framework"); diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h index 5c355be89814..cbb8ba957fad 100644 --- a/include/linux/iio/buffer-dmaengine.h +++ b/include/linux/iio/buffer-dmaengine.h @@ -10,6 +10,9 @@ struct iio_dev; struct device; +struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, + const char *channel); +void iio_dmaengine_buffer_free(struct iio_buffer *buffer); int devm_iio_dmaengine_buffer_setup(struct device *dev, struct iio_dev *indio_dev, const char *channel); -- cgit v1.2.3 From 1a97905d3e48ebe79a06d16143fbfa427c56ce5f Mon Sep 17 00:00:00 2001 From: Nuno Sa Date: Sat, 10 Feb 2024 21:57:17 +0100 Subject: iio: add the IIO backend framework This is a Framework to handle complex IIO aggregate devices. The typical architecture is to have one device as the frontend device which can be "linked" against one or multiple backend devices. All the IIO and userspace interface is expected to be registers/managed by the frontend device which will callback into the backends when needed (to get/set some configuration that it does not directly control). The basic framework interface is pretty simple: - Backends should register themselves with @devm_iio_backend_register() - Frontend devices should get backends with @devm_iio_backend_get() Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20240210-iio-backend-v11-5-f5242a5fb42a@analog.com Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 + drivers/iio/Kconfig | 9 + drivers/iio/Makefile | 1 + drivers/iio/industrialio-backend.c | 418 +++++++++++++++++++++++++++++++++++++ include/linux/iio/backend.h | 72 +++++++ 5 files changed, 508 insertions(+) create mode 100644 drivers/iio/industrialio-backend.c create mode 100644 include/linux/iio/backend.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index bc1e236f2184..6add3fde252d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10391,6 +10391,14 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/media/rc/iguanair.c +IIO BACKEND FRAMEWORK +M: Nuno Sa +R: Olivier Moysan +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/industrialio-backend.c +F: include/linux/iio/backend.h + IIO DIGITAL POTENTIOMETER DAC M: Peter Rosin L: linux-iio@vger.kernel.org diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 52eb46ef84c1..9c351ffc7bed 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -71,6 +71,15 @@ config IIO_TRIGGERED_EVENT help Provides helper functions for setting up triggered events. +config IIO_BACKEND + tristate + help + Framework to handle complex IIO aggregate devices. The typical + architecture that can make use of this framework is to have one + device as the frontend device which can be "linked" against one or + multiple backend devices. The framework then makes it easy to get + and control such backend devices. + source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" source "drivers/iio/addac/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 9622347a1c1b..0ba0e1521ba4 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o +obj-$(CONFIG_IIO_BACKEND) += industrialio-backend.o obj-y += accel/ obj-y += adc/ diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c new file mode 100644 index 000000000000..2fea2bbbe47f --- /dev/null +++ b/drivers/iio/industrialio-backend.c @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Framework to handle complex IIO aggregate devices. + * + * The typical architecture is to have one device as the frontend device which + * can be "linked" against one or multiple backend devices. All the IIO and + * userspace interface is expected to be registers/managed by the frontend + * device which will callback into the backends when needed (to get/set some + * configuration that it does not directly control). + * + * ------------------------------------------------------- + * ------------------ | ------------ ------------ ------- FPGA| + * | ADC |------------------------| | ADC CORE |---------| DMA CORE |------| RAM | | + * | (Frontend/IIO) | Serial Data (eg: LVDS) | |(backend) |---------| |------| | | + * | |------------------------| ------------ ------------ ------- | + * ------------------ ------------------------------------------------------- + * + * The framework interface is pretty simple: + * - Backends should register themselves with devm_iio_backend_register() + * - Frontend devices should get backends with devm_iio_backend_get() + * + * Also to note that the primary target for this framework are converters like + * ADC/DACs so iio_backend_ops will have some operations typical of converter + * devices. On top of that, this is "generic" for all IIO which means any kind + * of device can make use of the framework. That said, If the iio_backend_ops + * struct begins to grow out of control, we can always refactor things so that + * the industrialio-backend.c is only left with the really generic stuff. Then, + * we can build on top of it depending on the needs. + * + * Copyright (C) 2023-2024 Analog Devices Inc. + */ +#define dev_fmt(fmt) "iio-backend: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct iio_backend { + struct list_head entry; + const struct iio_backend_ops *ops; + struct device *dev; + struct module *owner; + void *priv; +}; + +/* + * Helper struct for requesting buffers. This ensures that we have all data + * that we need to free the buffer in a device managed action. + */ +struct iio_backend_buffer_pair { + struct iio_backend *back; + struct iio_buffer *buffer; +}; + +static LIST_HEAD(iio_back_list); +static DEFINE_MUTEX(iio_back_lock); + +/* + * Helper macros to call backend ops. Makes sure the option is supported. + */ +#define iio_backend_check_op(back, op) ({ \ + struct iio_backend *____back = back; \ + int ____ret = 0; \ + \ + if (!____back->ops->op) \ + ____ret = -EOPNOTSUPP; \ + \ + ____ret; \ +}) + +#define iio_backend_op_call(back, op, args...) ({ \ + struct iio_backend *__back = back; \ + int __ret; \ + \ + __ret = iio_backend_check_op(__back, op); \ + if (!__ret) \ + __ret = __back->ops->op(__back, ##args); \ + \ + __ret; \ +}) + +#define iio_backend_ptr_op_call(back, op, args...) ({ \ + struct iio_backend *__back = back; \ + void *ptr_err; \ + int __ret; \ + \ + __ret = iio_backend_check_op(__back, op); \ + if (__ret) \ + ptr_err = ERR_PTR(__ret); \ + else \ + ptr_err = __back->ops->op(__back, ##args); \ + \ + ptr_err; \ +}) + +#define iio_backend_void_op_call(back, op, args...) { \ + struct iio_backend *__back = back; \ + int __ret; \ + \ + __ret = iio_backend_check_op(__back, op); \ + if (!__ret) \ + __back->ops->op(__back, ##args); \ +} + +/** + * iio_backend_chan_enable - Enable a backend channel + * @back: Backend device + * @chan: Channel number + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan) +{ + return iio_backend_op_call(back, chan_enable, chan); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_chan_enable, IIO_BACKEND); + +/** + * iio_backend_chan_disable - Disable a backend channel + * @back: Backend device + * @chan: Channel number + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan) +{ + return iio_backend_op_call(back, chan_disable, chan); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_chan_disable, IIO_BACKEND); + +static void __iio_backend_disable(void *back) +{ + iio_backend_void_op_call(back, disable); +} + +/** + * devm_iio_backend_enable - Device managed backend enable + * @dev: Consumer device for the backend + * @back: Backend device + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int devm_iio_backend_enable(struct device *dev, struct iio_backend *back) +{ + int ret; + + ret = iio_backend_op_call(back, enable); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, __iio_backend_disable, back); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_enable, IIO_BACKEND); + +/** + * iio_backend_data_format_set - Configure the channel data format + * @back: Backend device + * @chan: Channel number + * @data: Data format + * + * Properly configure a channel with respect to the expected data format. A + * @struct iio_backend_data_fmt must be passed with the settings. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan, + const struct iio_backend_data_fmt *data) +{ + if (!data || data->type >= IIO_BACKEND_DATA_TYPE_MAX) + return -EINVAL; + + return iio_backend_op_call(back, data_format_set, chan, data); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND); + +static void iio_backend_free_buffer(void *arg) +{ + struct iio_backend_buffer_pair *pair = arg; + + iio_backend_void_op_call(pair->back, free_buffer, pair->buffer); +} + +/** + * devm_iio_backend_request_buffer - Device managed buffer request + * @dev: Consumer device for the backend + * @back: Backend device + * @indio_dev: IIO device + * + * Request an IIO buffer from the backend. The type of the buffer (typically + * INDIO_BUFFER_HARDWARE) is up to the backend to decide. This is because, + * normally, the backend dictates what kind of buffering we can get. + * + * The backend .free_buffer() hooks is automatically called on @dev detach. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int devm_iio_backend_request_buffer(struct device *dev, + struct iio_backend *back, + struct iio_dev *indio_dev) +{ + struct iio_backend_buffer_pair *pair; + struct iio_buffer *buffer; + + pair = devm_kzalloc(dev, sizeof(*pair), GFP_KERNEL); + if (!pair) + return -ENOMEM; + + buffer = iio_backend_ptr_op_call(back, request_buffer, indio_dev); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + + /* weak reference should be all what we need */ + pair->back = back; + pair->buffer = buffer; + + return devm_add_action_or_reset(dev, iio_backend_free_buffer, pair); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND); + +static void iio_backend_release(void *arg) +{ + struct iio_backend *back = arg; + + module_put(back->owner); +} + +static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back) +{ + struct device_link *link; + int ret; + + /* + * Make sure the provider cannot be unloaded before the consumer module. + * Note that device_links would still guarantee that nothing is + * accessible (and breaks) but this makes it explicit that the consumer + * module must be also unloaded. + */ + if (!try_module_get(back->owner)) + return dev_err_probe(dev, -ENODEV, + "Cannot get module reference\n"); + + ret = devm_add_action_or_reset(dev, iio_backend_release, back); + if (ret) + return ret; + + link = device_link_add(dev, back->dev, DL_FLAG_AUTOREMOVE_CONSUMER); + if (!link) + return dev_err_probe(dev, -EINVAL, + "Could not link to supplier(%s)\n", + dev_name(back->dev)); + + dev_dbg(dev, "Found backend(%s) device\n", dev_name(back->dev)); + + return 0; +} + +/** + * devm_iio_backend_get - Device managed backend device get + * @dev: Consumer device for the backend + * @name: Backend name + * + * Get's the backend associated with @dev. + * + * RETURNS: + * A backend pointer, negative error pointer otherwise. + */ +struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name) +{ + struct fwnode_handle *fwnode; + struct iio_backend *back; + unsigned int index; + int ret; + + if (name) { + ret = device_property_match_string(dev, "io-backend-names", + name); + if (ret < 0) + return ERR_PTR(ret); + index = ret; + } else { + index = 0; + } + + fwnode = fwnode_find_reference(dev_fwnode(dev), "io-backends", index); + if (IS_ERR(fwnode)) { + dev_err_probe(dev, PTR_ERR(fwnode), + "Cannot get Firmware reference\n"); + return ERR_CAST(fwnode); + } + + guard(mutex)(&iio_back_lock); + list_for_each_entry(back, &iio_back_list, entry) { + if (!device_match_fwnode(back->dev, fwnode)) + continue; + + fwnode_handle_put(fwnode); + ret = __devm_iio_backend_get(dev, back); + if (ret) + return ERR_PTR(ret); + + return back; + } + + fwnode_handle_put(fwnode); + return ERR_PTR(-EPROBE_DEFER); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get, IIO_BACKEND); + +/** + * __devm_iio_backend_get_from_fwnode_lookup - Device managed fwnode backend device get + * @dev: Consumer device for the backend + * @fwnode: Firmware node of the backend device + * + * Search the backend list for a device matching @fwnode. + * This API should not be used and it's only present for preventing the first + * user of this framework to break it's DT ABI. + * + * RETURNS: + * A backend pointer, negative error pointer otherwise. + */ +struct iio_backend * +__devm_iio_backend_get_from_fwnode_lookup(struct device *dev, + struct fwnode_handle *fwnode) +{ + struct iio_backend *back; + int ret; + + guard(mutex)(&iio_back_lock); + list_for_each_entry(back, &iio_back_list, entry) { + if (!device_match_fwnode(back->dev, fwnode)) + continue; + + ret = __devm_iio_backend_get(dev, back); + if (ret) + return ERR_PTR(ret); + + return back; + } + + return ERR_PTR(-EPROBE_DEFER); +} +EXPORT_SYMBOL_NS_GPL(__devm_iio_backend_get_from_fwnode_lookup, IIO_BACKEND); + +/** + * iio_backend_get_priv - Get driver private data + * @back: Backend device + */ +void *iio_backend_get_priv(const struct iio_backend *back) +{ + return back->priv; +} +EXPORT_SYMBOL_NS_GPL(iio_backend_get_priv, IIO_BACKEND); + +static void iio_backend_unregister(void *arg) +{ + struct iio_backend *back = arg; + + guard(mutex)(&iio_back_lock); + list_del(&back->entry); +} + +/** + * devm_iio_backend_register - Device managed backend device register + * @dev: Backend device being registered + * @ops: Backend ops + * @priv: Device private data + * + * @ops is mandatory. Not providing it results in -EINVAL. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int devm_iio_backend_register(struct device *dev, + const struct iio_backend_ops *ops, void *priv) +{ + struct iio_backend *back; + + if (!ops) + return dev_err_probe(dev, -EINVAL, "No backend ops given\n"); + + /* + * Through device_links, we guarantee that a frontend device cannot be + * bound/exist if the backend driver is not around. Hence, we can bind + * the backend object lifetime with the device being passed since + * removing it will tear the frontend/consumer down. + */ + back = devm_kzalloc(dev, sizeof(*back), GFP_KERNEL); + if (!back) + return -ENOMEM; + + back->ops = ops; + back->owner = dev->driver->owner; + back->dev = dev; + back->priv = priv; + scoped_guard(mutex, &iio_back_lock) + list_add(&back->entry, &iio_back_list); + + return devm_add_action_or_reset(dev, iio_backend_unregister, back); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_register, IIO_BACKEND); + +MODULE_AUTHOR("Nuno Sa "); +MODULE_DESCRIPTION("Framework to handle complex IIO aggregate devices"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h new file mode 100644 index 000000000000..a6d79381866e --- /dev/null +++ b/include/linux/iio/backend.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _IIO_BACKEND_H_ +#define _IIO_BACKEND_H_ + +#include + +struct fwnode_handle; +struct iio_backend; +struct device; +struct iio_dev; + +enum iio_backend_data_type { + IIO_BACKEND_TWOS_COMPLEMENT, + IIO_BACKEND_OFFSET_BINARY, + IIO_BACKEND_DATA_TYPE_MAX +}; + +/** + * struct iio_backend_data_fmt - Backend data format + * @type: Data type. + * @sign_extend: Bool to tell if the data is sign extended. + * @enable: Enable/Disable the data format module. If disabled, + * not formatting will happen. + */ +struct iio_backend_data_fmt { + enum iio_backend_data_type type; + bool sign_extend; + bool enable; +}; + +/** + * struct iio_backend_ops - operations structure for an iio_backend + * @enable: Enable backend. + * @disable: Disable backend. + * @chan_enable: Enable one channel. + * @chan_disable: Disable one channel. + * @data_format_set: Configure the data format for a specific channel. + * @request_buffer: Request an IIO buffer. + * @free_buffer: Free an IIO buffer. + **/ +struct iio_backend_ops { + int (*enable)(struct iio_backend *back); + void (*disable)(struct iio_backend *back); + int (*chan_enable)(struct iio_backend *back, unsigned int chan); + int (*chan_disable)(struct iio_backend *back, unsigned int chan); + int (*data_format_set)(struct iio_backend *back, unsigned int chan, + const struct iio_backend_data_fmt *data); + struct iio_buffer *(*request_buffer)(struct iio_backend *back, + struct iio_dev *indio_dev); + void (*free_buffer)(struct iio_backend *back, + struct iio_buffer *buffer); +}; + +int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan); +int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan); +int devm_iio_backend_enable(struct device *dev, struct iio_backend *back); +int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan, + const struct iio_backend_data_fmt *data); +int devm_iio_backend_request_buffer(struct device *dev, + struct iio_backend *back, + struct iio_dev *indio_dev); + +void *iio_backend_get_priv(const struct iio_backend *conv); +struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name); +struct iio_backend * +__devm_iio_backend_get_from_fwnode_lookup(struct device *dev, + struct fwnode_handle *fwnode); + +int devm_iio_backend_register(struct device *dev, + const struct iio_backend_ops *ops, void *priv); + +#endif -- cgit v1.2.3 From 794ef0e57854d794173c8ab6bcce3285032dcd95 Mon Sep 17 00:00:00 2001 From: Nuno Sa Date: Sat, 10 Feb 2024 21:57:19 +0100 Subject: iio: adc: adi-axi-adc: move to backend framework Move to the IIO backend framework. Devices supported by adi-axi-adc now register themselves as backend devices. Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20240210-iio-backend-v11-7-f5242a5fb42a@analog.com Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 +- drivers/iio/adc/adi-axi-adc.c | 384 +++++++++--------------------------- include/linux/iio/adc/adi-axi-adc.h | 68 ------- 3 files changed, 96 insertions(+), 358 deletions(-) delete mode 100644 include/linux/iio/adc/adi-axi-adc.h (limited to 'include') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 6793faacb4dc..02a55809cb6a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -309,7 +309,7 @@ config ADI_AXI_ADC select IIO_BUFFER_HW_CONSUMER select IIO_BUFFER_DMAENGINE select REGMAP_MMIO - depends on OF + select IIO_BACKEND help Say yes here to build support for Analog Devices Generic AXI ADC IP core. The IP core is used for interfacing with diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 0f21d1d98b9f..4156639b3c8b 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -17,13 +18,12 @@ #include #include -#include -#include -#include -#include - #include -#include + +#include +#include +#include +#include /* * Register definitions: @@ -44,6 +44,7 @@ #define ADI_AXI_REG_CHAN_CTRL_PN_SEL_OWR BIT(10) #define ADI_AXI_REG_CHAN_CTRL_IQCOR_EN BIT(9) #define ADI_AXI_REG_CHAN_CTRL_DCFILT_EN BIT(8) +#define ADI_AXI_REG_CHAN_CTRL_FMT_MASK GENMASK(6, 4) #define ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT BIT(6) #define ADI_AXI_REG_CHAN_CTRL_FMT_TYPE BIT(5) #define ADI_AXI_REG_CHAN_CTRL_FMT_EN BIT(4) @@ -55,286 +56,100 @@ ADI_AXI_REG_CHAN_CTRL_FMT_EN | \ ADI_AXI_REG_CHAN_CTRL_ENABLE) -struct adi_axi_adc_core_info { - unsigned int version; -}; - struct adi_axi_adc_state { - struct mutex lock; - - struct adi_axi_adc_client *client; struct regmap *regmap; -}; - -struct adi_axi_adc_client { - struct list_head entry; - struct adi_axi_adc_conv conv; - struct adi_axi_adc_state *state; struct device *dev; - const struct adi_axi_adc_core_info *info; }; -static LIST_HEAD(registered_clients); -static DEFINE_MUTEX(registered_clients_lock); - -static struct adi_axi_adc_client *conv_to_client(struct adi_axi_adc_conv *conv) -{ - return container_of(conv, struct adi_axi_adc_client, conv); -} - -void *adi_axi_adc_conv_priv(struct adi_axi_adc_conv *conv) -{ - struct adi_axi_adc_client *cl = conv_to_client(conv); - - return (char *)cl + ALIGN(sizeof(struct adi_axi_adc_client), - IIO_DMA_MINALIGN); -} -EXPORT_SYMBOL_NS_GPL(adi_axi_adc_conv_priv, IIO_ADI_AXI); - -static int adi_axi_adc_config_dma_buffer(struct device *dev, - struct iio_dev *indio_dev) -{ - const char *dma_name; - - if (!device_property_present(dev, "dmas")) - return 0; - - if (device_property_read_string(dev, "dma-names", &dma_name)) - dma_name = "rx"; - - return devm_iio_dmaengine_buffer_setup(indio_dev->dev.parent, - indio_dev, dma_name); -} - -static int adi_axi_adc_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) -{ - struct adi_axi_adc_state *st = iio_priv(indio_dev); - struct adi_axi_adc_conv *conv = &st->client->conv; - - if (!conv->read_raw) - return -EOPNOTSUPP; - - return conv->read_raw(conv, chan, val, val2, mask); -} - -static int adi_axi_adc_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - struct adi_axi_adc_state *st = iio_priv(indio_dev); - struct adi_axi_adc_conv *conv = &st->client->conv; - - if (!conv->write_raw) - return -EOPNOTSUPP; - - return conv->write_raw(conv, chan, val, val2, mask); -} - -static int adi_axi_adc_read_avail(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - const int **vals, int *type, int *length, - long mask) -{ - struct adi_axi_adc_state *st = iio_priv(indio_dev); - struct adi_axi_adc_conv *conv = &st->client->conv; - - if (!conv->read_avail) - return -EOPNOTSUPP; - - return conv->read_avail(conv, chan, vals, type, length, mask); -} - -static int adi_axi_adc_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *scan_mask) +static int axi_adc_enable(struct iio_backend *back) { - struct adi_axi_adc_state *st = iio_priv(indio_dev); - struct adi_axi_adc_conv *conv = &st->client->conv; - unsigned int i; + struct adi_axi_adc_state *st = iio_backend_get_priv(back); int ret; - for (i = 0; i < conv->chip_info->num_channels; i++) { - if (test_bit(i, scan_mask)) - ret = regmap_set_bits(st->regmap, - ADI_AXI_REG_CHAN_CTRL(i), - ADI_AXI_REG_CHAN_CTRL_ENABLE); - else - ret = regmap_clear_bits(st->regmap, - ADI_AXI_REG_CHAN_CTRL(i), - ADI_AXI_REG_CHAN_CTRL_ENABLE); - if (ret) - return ret; - } - - return 0; -} - -static struct adi_axi_adc_conv *adi_axi_adc_conv_register(struct device *dev, - size_t sizeof_priv) -{ - struct adi_axi_adc_client *cl; - size_t alloc_size; - - alloc_size = ALIGN(sizeof(struct adi_axi_adc_client), IIO_DMA_MINALIGN); - if (sizeof_priv) - alloc_size += ALIGN(sizeof_priv, IIO_DMA_MINALIGN); - - cl = kzalloc(alloc_size, GFP_KERNEL); - if (!cl) - return ERR_PTR(-ENOMEM); - - mutex_lock(®istered_clients_lock); - - cl->dev = get_device(dev); - - list_add_tail(&cl->entry, ®istered_clients); - - mutex_unlock(®istered_clients_lock); + ret = regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN, + ADI_AXI_REG_RSTN_MMCM_RSTN); + if (ret) + return ret; - return &cl->conv; + fsleep(10000); + return regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN, + ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN); } -static void adi_axi_adc_conv_unregister(struct adi_axi_adc_conv *conv) +static void axi_adc_disable(struct iio_backend *back) { - struct adi_axi_adc_client *cl = conv_to_client(conv); - - mutex_lock(®istered_clients_lock); - - list_del(&cl->entry); - put_device(cl->dev); + struct adi_axi_adc_state *st = iio_backend_get_priv(back); - mutex_unlock(®istered_clients_lock); - - kfree(cl); + regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); } -static void devm_adi_axi_adc_conv_release(void *conv) +static int axi_adc_data_format_set(struct iio_backend *back, unsigned int chan, + const struct iio_backend_data_fmt *data) { - adi_axi_adc_conv_unregister(conv); + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 val; + + if (!data->enable) + return regmap_clear_bits(st->regmap, + ADI_AXI_REG_CHAN_CTRL(chan), + ADI_AXI_REG_CHAN_CTRL_FMT_EN); + + val = FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_EN, true); + if (data->sign_extend) + val |= FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT, true); + if (data->type == IIO_BACKEND_OFFSET_BINARY) + val |= FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_TYPE, true); + + return regmap_update_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan), + ADI_AXI_REG_CHAN_CTRL_FMT_MASK, val); } -struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev, - size_t sizeof_priv) +static int axi_adc_chan_enable(struct iio_backend *back, unsigned int chan) { - struct adi_axi_adc_conv *conv; - int ret; - - conv = adi_axi_adc_conv_register(dev, sizeof_priv); - if (IS_ERR(conv)) - return conv; + struct adi_axi_adc_state *st = iio_backend_get_priv(back); - ret = devm_add_action_or_reset(dev, devm_adi_axi_adc_conv_release, - conv); - if (ret) - return ERR_PTR(ret); - - return conv; + return regmap_set_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan), + ADI_AXI_REG_CHAN_CTRL_ENABLE); } -EXPORT_SYMBOL_NS_GPL(devm_adi_axi_adc_conv_register, IIO_ADI_AXI); - -static const struct iio_info adi_axi_adc_info = { - .read_raw = &adi_axi_adc_read_raw, - .write_raw = &adi_axi_adc_write_raw, - .update_scan_mode = &adi_axi_adc_update_scan_mode, - .read_avail = &adi_axi_adc_read_avail, -}; - -static const struct adi_axi_adc_core_info adi_axi_adc_10_0_a_info = { - .version = ADI_AXI_PCORE_VER(10, 0, 'a'), -}; -static struct adi_axi_adc_client *adi_axi_adc_attach_client(struct device *dev) +static int axi_adc_chan_disable(struct iio_backend *back, unsigned int chan) { - const struct adi_axi_adc_core_info *info; - struct adi_axi_adc_client *cl; - struct device_node *cln; - - info = of_device_get_match_data(dev); - if (!info) - return ERR_PTR(-ENODEV); - - cln = of_parse_phandle(dev->of_node, "adi,adc-dev", 0); - if (!cln) { - dev_err(dev, "No 'adi,adc-dev' node defined\n"); - return ERR_PTR(-ENODEV); - } - - mutex_lock(®istered_clients_lock); - - list_for_each_entry(cl, ®istered_clients, entry) { - if (!cl->dev) - continue; - - if (cl->dev->of_node != cln) - continue; - - if (!try_module_get(cl->dev->driver->owner)) { - mutex_unlock(®istered_clients_lock); - of_node_put(cln); - return ERR_PTR(-ENODEV); - } - - get_device(cl->dev); - cl->info = info; - mutex_unlock(®istered_clients_lock); - of_node_put(cln); - return cl; - } + struct adi_axi_adc_state *st = iio_backend_get_priv(back); - mutex_unlock(®istered_clients_lock); - of_node_put(cln); - - return ERR_PTR(-EPROBE_DEFER); + return regmap_clear_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan), + ADI_AXI_REG_CHAN_CTRL_ENABLE); } -static int adi_axi_adc_setup_channels(struct device *dev, - struct adi_axi_adc_state *st) +static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back, + struct iio_dev *indio_dev) { - struct adi_axi_adc_conv *conv = &st->client->conv; - int i, ret; + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + struct iio_buffer *buffer; + const char *dma_name; + int ret; - if (conv->preenable_setup) { - ret = conv->preenable_setup(conv); - if (ret) - return ret; - } + if (device_property_read_string(st->dev, "dma-names", &dma_name)) + dma_name = "rx"; - for (i = 0; i < conv->chip_info->num_channels; i++) { - ret = regmap_write(st->regmap, ADI_AXI_REG_CHAN_CTRL(i), - ADI_AXI_REG_CHAN_CTRL_DEFAULTS); - if (ret) - return ret; + buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name); + if (IS_ERR(buffer)) { + dev_err(st->dev, "Could not get DMA buffer, %ld\n", + PTR_ERR(buffer)); + return ERR_CAST(buffer); } - return 0; -} - -static int axi_adc_reset(struct adi_axi_adc_state *st) -{ - int ret; - - ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); - if (ret) - return ret; - - mdelay(10); - ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, - ADI_AXI_REG_RSTN_MMCM_RSTN); + indio_dev->modes |= INDIO_BUFFER_HARDWARE; + ret = iio_device_attach_buffer(indio_dev, buffer); if (ret) - return ret; + return ERR_PTR(ret); - mdelay(10); - return regmap_write(st->regmap, ADI_AXI_REG_RSTN, - ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN); + return buffer; } -static void adi_axi_adc_cleanup(void *data) +static void axi_adc_free_buffer(struct iio_backend *back, + struct iio_buffer *buffer) { - struct adi_axi_adc_client *cl = data; - - put_device(cl->dev); - module_put(cl->dev->driver->owner); + iio_dmaengine_buffer_free(buffer); } static const struct regmap_config axi_adc_regmap_config = { @@ -344,45 +159,47 @@ static const struct regmap_config axi_adc_regmap_config = { .max_register = 0x0800, }; +static const struct iio_backend_ops adi_axi_adc_generic = { + .enable = axi_adc_enable, + .disable = axi_adc_disable, + .data_format_set = axi_adc_data_format_set, + .chan_enable = axi_adc_chan_enable, + .chan_disable = axi_adc_chan_disable, + .request_buffer = axi_adc_request_buffer, + .free_buffer = axi_adc_free_buffer, +}; + static int adi_axi_adc_probe(struct platform_device *pdev) { - struct adi_axi_adc_conv *conv; - struct iio_dev *indio_dev; - struct adi_axi_adc_client *cl; + const unsigned int *expected_ver; struct adi_axi_adc_state *st; void __iomem *base; unsigned int ver; int ret; - cl = adi_axi_adc_attach_client(&pdev->dev); - if (IS_ERR(cl)) - return PTR_ERR(cl); - - ret = devm_add_action_or_reset(&pdev->dev, adi_axi_adc_cleanup, cl); - if (ret) - return ret; - - indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); - if (indio_dev == NULL) + st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL); + if (!st) return -ENOMEM; - st = iio_priv(indio_dev); - st->client = cl; - cl->state = st; - mutex_init(&st->lock); - base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); + st->dev = &pdev->dev; st->regmap = devm_regmap_init_mmio(&pdev->dev, base, &axi_adc_regmap_config); if (IS_ERR(st->regmap)) return PTR_ERR(st->regmap); - conv = &st->client->conv; + expected_ver = device_get_match_data(&pdev->dev); + if (!expected_ver) + return -ENODEV; - ret = axi_adc_reset(st); + /* + * Force disable the core. Up to the frontend to enable us. And we can + * still read/write registers... + */ + ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); if (ret) return ret; @@ -390,33 +207,19 @@ static int adi_axi_adc_probe(struct platform_device *pdev) if (ret) return ret; - if (cl->info->version > ver) { + if (*expected_ver > ver) { dev_err(&pdev->dev, "IP core version is too old. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", - ADI_AXI_PCORE_VER_MAJOR(cl->info->version), - ADI_AXI_PCORE_VER_MINOR(cl->info->version), - ADI_AXI_PCORE_VER_PATCH(cl->info->version), + ADI_AXI_PCORE_VER_MAJOR(*expected_ver), + ADI_AXI_PCORE_VER_MINOR(*expected_ver), + ADI_AXI_PCORE_VER_PATCH(*expected_ver), ADI_AXI_PCORE_VER_MAJOR(ver), ADI_AXI_PCORE_VER_MINOR(ver), ADI_AXI_PCORE_VER_PATCH(ver)); return -ENODEV; } - indio_dev->info = &adi_axi_adc_info; - indio_dev->name = "adi-axi-adc"; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->num_channels = conv->chip_info->num_channels; - indio_dev->channels = conv->chip_info->channels; - - ret = adi_axi_adc_config_dma_buffer(&pdev->dev, indio_dev); - if (ret) - return ret; - - ret = adi_axi_adc_setup_channels(&pdev->dev, st); - if (ret) - return ret; - - ret = devm_iio_device_register(&pdev->dev, indio_dev); + ret = devm_iio_backend_register(&pdev->dev, &adi_axi_adc_generic, st); if (ret) return ret; @@ -428,6 +231,8 @@ static int adi_axi_adc_probe(struct platform_device *pdev) return 0; } +static unsigned int adi_axi_adc_10_0_a_info = ADI_AXI_PCORE_VER(10, 0, 'a'); + /* Match table for of_platform binding */ static const struct of_device_id adi_axi_adc_of_match[] = { { .compatible = "adi,axi-adc-10.0.a", .data = &adi_axi_adc_10_0_a_info }, @@ -448,3 +253,4 @@ MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices Generic AXI ADC IP core driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER); +MODULE_IMPORT_NS(IIO_BACKEND); diff --git a/include/linux/iio/adc/adi-axi-adc.h b/include/linux/iio/adc/adi-axi-adc.h deleted file mode 100644 index b7904992d561..000000000000 --- a/include/linux/iio/adc/adi-axi-adc.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Analog Devices Generic AXI ADC IP core driver/library - * Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip - * - * Copyright 2012-2020 Analog Devices Inc. - */ -#ifndef __ADI_AXI_ADC_H__ -#define __ADI_AXI_ADC_H__ - -struct device; -struct iio_chan_spec; - -/** - * struct adi_axi_adc_chip_info - Chip specific information - * @name Chip name - * @id Chip ID (usually product ID) - * @channels Channel specifications of type @struct iio_chan_spec - * @num_channels Number of @channels - * @scale_table Supported scales by the chip; tuples of 2 ints - * @num_scales Number of scales in the table - * @max_rate Maximum sampling rate supported by the device - */ -struct adi_axi_adc_chip_info { - const char *name; - unsigned int id; - - const struct iio_chan_spec *channels; - unsigned int num_channels; - - const unsigned int (*scale_table)[2]; - int num_scales; - - unsigned long max_rate; -}; - -/** - * struct adi_axi_adc_conv - data of the ADC attached to the AXI ADC - * @chip_info chip info details for the client ADC - * @preenable_setup op to run in the client before enabling the AXI ADC - * @reg_access IIO debugfs_reg_access hook for the client ADC - * @read_raw IIO read_raw hook for the client ADC - * @write_raw IIO write_raw hook for the client ADC - * @read_avail IIO read_avail hook for the client ADC - */ -struct adi_axi_adc_conv { - const struct adi_axi_adc_chip_info *chip_info; - - int (*preenable_setup)(struct adi_axi_adc_conv *conv); - int (*reg_access)(struct adi_axi_adc_conv *conv, unsigned int reg, - unsigned int writeval, unsigned int *readval); - int (*read_raw)(struct adi_axi_adc_conv *conv, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask); - int (*write_raw)(struct adi_axi_adc_conv *conv, - struct iio_chan_spec const *chan, - int val, int val2, long mask); - int (*read_avail)(struct adi_axi_adc_conv *conv, - struct iio_chan_spec const *chan, - const int **val, int *type, int *length, long mask); -}; - -struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev, - size_t sizeof_priv); - -void *adi_axi_adc_conv_priv(struct adi_axi_adc_conv *conv); - -#endif -- cgit v1.2.3 From 87161a2b0aed9e9b614bbf6fe8697ad560ceb0cb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 6 Feb 2024 11:21:00 -0800 Subject: f2fs: deprecate io_bits Let's deprecate an unused io_bits feature to save CPU cycles and memory. Reviewed-by: Daeho Jeong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.rst | 2 - fs/f2fs/data.c | 73 +------------------------------ fs/f2fs/f2fs.h | 25 +++-------- fs/f2fs/file.c | 2 - fs/f2fs/gc.c | 10 +---- fs/f2fs/segment.c | 9 +--- fs/f2fs/super.c | 88 +------------------------------------- include/linux/f2fs_fs.h | 6 --- 8 files changed, 10 insertions(+), 205 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst index 32cbfa864f38..9ac5083dae8e 100644 --- a/Documentation/filesystems/f2fs.rst +++ b/Documentation/filesystems/f2fs.rst @@ -229,8 +229,6 @@ mode=%s Control block allocation mode which supports "adaptive" option for more randomness. Please, use these options for your experiments and we strongly recommend to re-format the filesystem after using these options. -io_bits=%u Set the bit size of write IO requests. It should be set - with "mode=lfs". usrquota Enable plain user disk quota accounting. grpquota Enable plain group disk quota accounting. prjquota Enable plain project quota accounting. diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 05158f89ef32..828c797cd47c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -338,17 +338,6 @@ static void f2fs_write_end_io(struct bio *bio) struct page *page = bvec->bv_page; enum count_type type = WB_DATA_TYPE(page, false); - if (page_private_dummy(page)) { - clear_page_private_dummy(page); - unlock_page(page); - mempool_free(page, sbi->write_io_dummy); - - if (unlikely(bio->bi_status)) - f2fs_stop_checkpoint(sbi, true, - STOP_CP_REASON_WRITE_FAIL); - continue; - } - fscrypt_finalize_bounce_page(&page); #ifdef CONFIG_F2FS_FS_COMPRESSION @@ -522,50 +511,13 @@ void f2fs_submit_read_bio(struct f2fs_sb_info *sbi, struct bio *bio, submit_bio(bio); } -static void f2fs_align_write_bio(struct f2fs_sb_info *sbi, struct bio *bio) -{ - unsigned int start = - (bio->bi_iter.bi_size >> F2FS_BLKSIZE_BITS) % F2FS_IO_SIZE(sbi); - - if (start == 0) - return; - - /* fill dummy pages */ - for (; start < F2FS_IO_SIZE(sbi); start++) { - struct page *page = - mempool_alloc(sbi->write_io_dummy, - GFP_NOIO | __GFP_NOFAIL); - f2fs_bug_on(sbi, !page); - - lock_page(page); - - zero_user_segment(page, 0, PAGE_SIZE); - set_page_private_dummy(page); - - if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) - f2fs_bug_on(sbi, 1); - } -} - static void f2fs_submit_write_bio(struct f2fs_sb_info *sbi, struct bio *bio, enum page_type type) { WARN_ON_ONCE(is_read_io(bio_op(bio))); - if (type == DATA || type == NODE) { - if (f2fs_lfs_mode(sbi) && current->plug) - blk_finish_plug(current->plug); - - if (F2FS_IO_ALIGNED(sbi)) { - f2fs_align_write_bio(sbi, bio); - /* - * In the NODE case, we lose next block address chain. - * So, we need to do checkpoint in f2fs_sync_file. - */ - if (type == NODE) - set_sbi_flag(sbi, SBI_NEED_CP); - } - } + if (f2fs_lfs_mode(sbi) && current->plug && PAGE_TYPE_ON_MAIN(type)) + blk_finish_plug(current->plug); trace_f2fs_submit_write_bio(sbi->sb, type, bio); iostat_update_submit_ctx(bio, type); @@ -794,16 +746,6 @@ static bool io_is_mergeable(struct f2fs_sb_info *sbi, struct bio *bio, block_t last_blkaddr, block_t cur_blkaddr) { - if (F2FS_IO_ALIGNED(sbi) && (fio->type == DATA || fio->type == NODE)) { - unsigned int filled_blocks = - F2FS_BYTES_TO_BLK(bio->bi_iter.bi_size); - unsigned int io_size = F2FS_IO_SIZE(sbi); - unsigned int left_vecs = bio->bi_max_vecs - bio->bi_vcnt; - - /* IOs in bio is aligned and left space of vectors is not enough */ - if (!(filled_blocks % io_size) && left_vecs < io_size) - return false; - } if (!page_is_mergeable(sbi, bio, last_blkaddr, cur_blkaddr)) return false; return io_type_is_mergeable(io, fio); @@ -1055,14 +997,6 @@ next: __submit_merged_bio(io); alloc_new: if (io->bio == NULL) { - if (F2FS_IO_ALIGNED(sbi) && - (fio->type == DATA || fio->type == NODE) && - fio->new_blkaddr & F2FS_IO_SIZE_MASK(sbi)) { - dec_page_count(sbi, WB_DATA_TYPE(bio_page, - fio->compressed_page)); - fio->retry = 1; - goto skip; - } io->bio = __bio_alloc(fio, BIO_MAX_VECS); f2fs_set_bio_crypt_ctx(io->bio, fio->page->mapping->host, bio_page->index, fio, GFP_NOIO); @@ -1092,7 +1026,6 @@ alloc_new: __submit_merged_bio(io); } #endif -skip: if (fio->in_list) goto next; out: @@ -2669,8 +2602,6 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio) if (fio) { if (page_private_gcing(fio->page)) return true; - if (page_private_dummy(fio->page)) - return true; if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) && f2fs_is_checkpointed_data(sbi, fio->old_blkaddr))) return true; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 543898482f8b..4c52136cbc10 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -148,7 +148,6 @@ struct f2fs_rwsem { struct f2fs_mount_info { unsigned int opt; - int write_io_size_bits; /* Write IO size bits */ block_t root_reserved_blocks; /* root reserved blocks */ kuid_t s_resuid; /* reserved blocks for uid */ kgid_t s_resgid; /* reserved blocks for gid */ @@ -1117,6 +1116,7 @@ enum count_type { * ... Only can be used with META. */ #define PAGE_TYPE_OF_BIO(type) ((type) > META ? META : (type)) +#define PAGE_TYPE_ON_MAIN(type) ((type) == DATA || (type) == NODE) enum page_type { DATA = 0, NODE = 1, /* should not change this */ @@ -1211,7 +1211,6 @@ struct f2fs_io_info { unsigned int submitted:1; /* indicate IO submission */ unsigned int in_list:1; /* indicate fio is in io_list */ unsigned int is_por:1; /* indicate IO is from recovery or not */ - unsigned int retry:1; /* need to reallocate block address */ unsigned int encrypted:1; /* indicate file is encrypted */ unsigned int post_read:1; /* require post read */ enum iostat_type io_type; /* io type */ @@ -1413,18 +1412,16 @@ static inline void f2fs_clear_bit(unsigned int nr, char *addr); * Layout A: lowest bit should be 1 * | bit0 = 1 | bit1 | bit2 | ... | bit MAX | private data .... | * bit 0 PAGE_PRIVATE_NOT_POINTER - * bit 1 PAGE_PRIVATE_DUMMY_WRITE - * bit 2 PAGE_PRIVATE_ONGOING_MIGRATION - * bit 3 PAGE_PRIVATE_INLINE_INODE - * bit 4 PAGE_PRIVATE_REF_RESOURCE - * bit 5- f2fs private data + * bit 1 PAGE_PRIVATE_ONGOING_MIGRATION + * bit 2 PAGE_PRIVATE_INLINE_INODE + * bit 3 PAGE_PRIVATE_REF_RESOURCE + * bit 4- f2fs private data * * Layout B: lowest bit should be 0 * page.private is a wrapped pointer. */ enum { PAGE_PRIVATE_NOT_POINTER, /* private contains non-pointer data */ - PAGE_PRIVATE_DUMMY_WRITE, /* data page for padding aligned IO */ PAGE_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */ PAGE_PRIVATE_INLINE_INODE, /* inode page contains inline data */ PAGE_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */ @@ -1571,7 +1568,6 @@ struct f2fs_sb_info { struct f2fs_bio_info *write_io[NR_PAGE_TYPE]; /* for write bios */ /* keep migration IO order for LFS mode */ struct f2fs_rwsem io_order_lock; - mempool_t *write_io_dummy; /* Dummy pages */ pgoff_t page_eio_ofs[NR_PAGE_TYPE]; /* EIO page offset */ int page_eio_cnt[NR_PAGE_TYPE]; /* EIO count */ @@ -2307,10 +2303,6 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, if (!__allow_reserved_blocks(sbi, inode, true)) avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks; - if (F2FS_IO_ALIGNED(sbi)) - avail_user_block_count -= sbi->blocks_per_seg * - SM_I(sbi)->additional_reserved_segments; - if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { if (avail_user_block_count > sbi->unusable_block_count) avail_user_block_count -= sbi->unusable_block_count; @@ -2378,17 +2370,14 @@ static inline void clear_page_private_##name(struct page *page) \ PAGE_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER); PAGE_PRIVATE_GET_FUNC(inline, INLINE_INODE); PAGE_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION); -PAGE_PRIVATE_GET_FUNC(dummy, DUMMY_WRITE); PAGE_PRIVATE_SET_FUNC(reference, REF_RESOURCE); PAGE_PRIVATE_SET_FUNC(inline, INLINE_INODE); PAGE_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION); -PAGE_PRIVATE_SET_FUNC(dummy, DUMMY_WRITE); PAGE_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE); PAGE_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE); PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION); -PAGE_PRIVATE_CLEAR_FUNC(dummy, DUMMY_WRITE); static inline unsigned long get_page_private_data(struct page *page) { @@ -2644,10 +2633,6 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, if (!__allow_reserved_blocks(sbi, inode, false)) valid_block_count += F2FS_OPTION(sbi).root_reserved_blocks; - if (F2FS_IO_ALIGNED(sbi)) - valid_block_count += sbi->blocks_per_seg * - SM_I(sbi)->additional_reserved_segments; - user_block_count = sbi->user_block_count; if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) user_block_count -= sbi->unusable_block_count; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 25b119cf3499..c6cd9474ba2d 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -822,8 +822,6 @@ static bool f2fs_force_buffered_io(struct inode *inode, int rw) */ if (f2fs_sb_has_blkzoned(sbi) && (rw == WRITE)) return true; - if (f2fs_lfs_mode(sbi) && rw == WRITE && F2FS_IO_ALIGNED(sbi)) - return true; if (is_sbi_flag_set(sbi, SBI_CP_DISABLED)) return true; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index a079eebfb080..6899f434ad68 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1184,7 +1184,6 @@ static int ra_data_block(struct inode *inode, pgoff_t index) .op_flags = 0, .encrypted_page = NULL, .in_list = 0, - .retry = 0, }; int err; @@ -1273,7 +1272,6 @@ static int move_data_block(struct inode *inode, block_t bidx, .op_flags = 0, .encrypted_page = NULL, .in_list = 0, - .retry = 0, }; struct dnode_of_data dn; struct f2fs_summary sum; @@ -1393,18 +1391,12 @@ static int move_data_block(struct inode *inode, block_t bidx, fio.op_flags = REQ_SYNC; fio.new_blkaddr = newaddr; f2fs_submit_page_write(&fio); - if (fio.retry) { - err = -EAGAIN; - if (PageWriteback(fio.encrypted_page)) - end_page_writeback(fio.encrypted_page); - goto put_page_out; - } f2fs_update_iostat(fio.sbi, NULL, FS_GC_DATA_IO, F2FS_BLKSIZE); f2fs_update_data_blkaddr(&dn, newaddr); set_inode_flag(inode, FI_APPEND_WRITE); -put_page_out: + f2fs_put_page(fio.encrypted_page, 1); recover_block: if (err) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7901ede58113..e5759813276a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3507,9 +3507,6 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, if (fio) { struct f2fs_bio_info *io; - if (F2FS_IO_ALIGNED(sbi)) - fio->retry = 0; - INIT_LIST_HEAD(&fio->list); fio->in_list = 1; io = sbi->write_io[fio->type] + fio->temp; @@ -3557,7 +3554,7 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) if (keep_order) f2fs_down_read(&fio->sbi->io_order_lock); -reallocate: + f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, &fio->new_blkaddr, sum, type, fio); if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO) @@ -3565,10 +3562,6 @@ reallocate: /* writeout dirty page into bdev */ f2fs_submit_page_write(fio); - if (fio->retry) { - fio->old_blkaddr = fio->new_blkaddr; - goto reallocate; - } f2fs_update_device_state(fio->sbi, fio->ino, fio->new_blkaddr, 1); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1b718bebfaa1..f1516fd5088a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -138,7 +138,6 @@ enum { Opt_resgid, Opt_resuid, Opt_mode, - Opt_io_size_bits, Opt_fault_injection, Opt_fault_type, Opt_lazytime, @@ -217,7 +216,6 @@ static match_table_t f2fs_tokens = { {Opt_resgid, "resgid=%u"}, {Opt_resuid, "resuid=%u"}, {Opt_mode, "mode=%s"}, - {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, {Opt_fault_type, "fault_type=%u"}, {Opt_lazytime, "lazytime"}, @@ -349,46 +347,6 @@ static inline void limit_reserve_root(struct f2fs_sb_info *sbi) F2FS_OPTION(sbi).s_resgid)); } -static inline int adjust_reserved_segment(struct f2fs_sb_info *sbi) -{ - unsigned int sec_blks = sbi->blocks_per_seg * sbi->segs_per_sec; - unsigned int avg_vblocks; - unsigned int wanted_reserved_segments; - block_t avail_user_block_count; - - if (!F2FS_IO_ALIGNED(sbi)) - return 0; - - /* average valid block count in section in worst case */ - avg_vblocks = sec_blks / F2FS_IO_SIZE(sbi); - - /* - * we need enough free space when migrating one section in worst case - */ - wanted_reserved_segments = (F2FS_IO_SIZE(sbi) / avg_vblocks) * - reserved_segments(sbi); - wanted_reserved_segments -= reserved_segments(sbi); - - avail_user_block_count = sbi->user_block_count - - sbi->current_reserved_blocks - - F2FS_OPTION(sbi).root_reserved_blocks; - - if (wanted_reserved_segments * sbi->blocks_per_seg > - avail_user_block_count) { - f2fs_err(sbi, "IO align feature can't grab additional reserved segment: %u, available segments: %u", - wanted_reserved_segments, - avail_user_block_count >> sbi->log_blocks_per_seg); - return -ENOSPC; - } - - SM_I(sbi)->additional_reserved_segments = wanted_reserved_segments; - - f2fs_info(sbi, "IO align feature needs additional reserved segment: %u", - wanted_reserved_segments); - - return 0; -} - static inline void adjust_unusable_cap_perc(struct f2fs_sb_info *sbi) { if (!F2FS_OPTION(sbi).unusable_cap_perc) @@ -919,16 +877,6 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) } kfree(name); break; - case Opt_io_size_bits: - if (args->from && match_int(args, &arg)) - return -EINVAL; - if (arg <= 0 || arg > __ilog2_u32(BIO_MAX_VECS)) { - f2fs_warn(sbi, "Not support %ld, larger than %d", - BIT(arg), BIO_MAX_VECS); - return -EINVAL; - } - F2FS_OPTION(sbi).write_io_size_bits = arg; - break; #ifdef CONFIG_F2FS_FAULT_INJECTION case Opt_fault_injection: if (args->from && match_int(args, &arg)) @@ -1398,12 +1346,6 @@ default_check: } #endif - if (F2FS_IO_SIZE_BITS(sbi) && !f2fs_lfs_mode(sbi)) { - f2fs_err(sbi, "Should set mode=lfs with %luKB-sized IO", - F2FS_IO_SIZE_KB(sbi)); - return -EINVAL; - } - if (test_opt(sbi, INLINE_XATTR_SIZE)) { int min_size, max_size; @@ -1724,7 +1666,6 @@ static void f2fs_put_super(struct super_block *sb) f2fs_destroy_page_array_cache(sbi); f2fs_destroy_xattr_caches(sbi); - mempool_destroy(sbi->write_io_dummy); #ifdef CONFIG_QUOTA for (i = 0; i < MAXQUOTAS; i++) kfree(F2FS_OPTION(sbi).s_qf_names[i]); @@ -2084,9 +2025,6 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) F2FS_OPTION(sbi).s_resuid), from_kgid_munged(&init_user_ns, F2FS_OPTION(sbi).s_resgid)); - if (F2FS_IO_SIZE_BITS(sbi)) - seq_printf(seq, ",io_bits=%u", - F2FS_OPTION(sbi).write_io_size_bits); #ifdef CONFIG_F2FS_FAULT_INJECTION if (test_opt(sbi, FAULT_INJECTION)) { seq_printf(seq, ",fault_injection=%u", @@ -2338,7 +2276,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) bool no_read_extent_cache = !test_opt(sbi, READ_EXTENT_CACHE); bool no_age_extent_cache = !test_opt(sbi, AGE_EXTENT_CACHE); bool enable_checkpoint = !test_opt(sbi, DISABLE_CHECKPOINT); - bool no_io_align = !F2FS_IO_ALIGNED(sbi); bool no_atgc = !test_opt(sbi, ATGC); bool no_discard = !test_opt(sbi, DISCARD); bool no_compress_cache = !test_opt(sbi, COMPRESS_CACHE); @@ -2446,12 +2383,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) goto restore_opts; } - if (no_io_align == !!F2FS_IO_ALIGNED(sbi)) { - err = -EINVAL; - f2fs_warn(sbi, "switch io_bits option is not allowed"); - goto restore_opts; - } - if (no_compress_cache == !!test_opt(sbi, COMPRESS_CACHE)) { err = -EINVAL; f2fs_warn(sbi, "switch compress_cache option is not allowed"); @@ -4314,8 +4245,6 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) FDEV(i).total_segments, FDEV(i).start_blk, FDEV(i).end_blk); } - f2fs_info(sbi, - "IO Block Size: %8ld KB", F2FS_IO_SIZE_KB(sbi)); return 0; } @@ -4528,19 +4457,10 @@ try_onemore: if (err) goto free_iostat; - if (F2FS_IO_ALIGNED(sbi)) { - sbi->write_io_dummy = - mempool_create_page_pool(2 * (F2FS_IO_SIZE(sbi) - 1), 0); - if (!sbi->write_io_dummy) { - err = -ENOMEM; - goto free_percpu; - } - } - /* init per sbi slab cache */ err = f2fs_init_xattr_caches(sbi); if (err) - goto free_io_dummy; + goto free_percpu; err = f2fs_init_page_array_cache(sbi); if (err) goto free_xattr_cache; @@ -4628,10 +4548,6 @@ try_onemore: goto free_nm; } - err = adjust_reserved_segment(sbi); - if (err) - goto free_nm; - /* For write statistics */ sbi->sectors_written_start = f2fs_get_sectors_written(sbi); @@ -4862,8 +4778,6 @@ free_page_array_cache: f2fs_destroy_page_array_cache(sbi); free_xattr_cache: f2fs_destroy_xattr_caches(sbi); -free_io_dummy: - mempool_destroy(sbi->write_io_dummy); free_percpu: destroy_percpu_info(sbi); free_iostat: diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 053137a0fe45..9b69c50255b2 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -40,12 +40,6 @@ #define F2FS_ENC_UTF8_12_1 1 -#define F2FS_IO_SIZE(sbi) BIT(F2FS_OPTION(sbi).write_io_size_bits) /* Blocks */ -#define F2FS_IO_SIZE_KB(sbi) BIT(F2FS_OPTION(sbi).write_io_size_bits + 2) /* KB */ -#define F2FS_IO_SIZE_BITS(sbi) (F2FS_OPTION(sbi).write_io_size_bits) /* power of 2 */ -#define F2FS_IO_SIZE_MASK(sbi) (F2FS_IO_SIZE(sbi) - 1) -#define F2FS_IO_ALIGNED(sbi) (F2FS_IO_SIZE(sbi) > 1) - /* This flag is used by node and meta inodes, and by recovery */ #define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO) -- cgit v1.2.3 From f0397e27d1204a7e6581d140c2de7fd11383c6ba Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Mon, 19 Feb 2024 11:07:48 -0700 Subject: Revert "bus: mhi: core: Add support for reading MHI info from device" This reverts commit 3316ab2b45f6bf4797d8d65b22fda3cc13318890. The MHI spec owner pointed out that the SOC_HW_VERSION register is part of the BHIe segment, and only valid on devices which implement BHIe. Only a small subset of MHI devices implement BHIe so blindly accessing the register for all devices is not correct. Also, since the BHIe segment offset is not used when accessing the register, any implementation which moves the BHIe segment will result in accessing some other register. We've seen that accessing this register on AIC100 which does not support BHIe can result in initialization failures. We could try to put checks into the code to address these issues, but in the roughly 4 years this functionality has existed, no one has used it. Easier to drop this dead code and address the issues if anyone comes up with a real world use for it. Signed-off-by: Jeffrey Hugo Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20240219180748.1591527-1-quic_jhugo@quicinc.com Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/init.c | 12 ------------ drivers/bus/mhi/host/internal.h | 6 ------ include/linux/mhi.h | 17 ----------------- 3 files changed, 35 deletions(-) (limited to 'include') diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 944da46e6f11..44f934981de8 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -914,7 +914,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan; struct mhi_cmd *mhi_cmd; struct mhi_device *mhi_dev; - u32 soc_info; int ret, i; if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->regs || @@ -989,17 +988,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, mhi_cntrl->unmap_single = mhi_unmap_single_no_bb; } - /* Read the MHI device info */ - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, - SOC_HW_VERSION_OFFS, &soc_info); - if (ret) - goto err_destroy_wq; - - mhi_cntrl->family_number = FIELD_GET(SOC_HW_VERSION_FAM_NUM_BMSK, soc_info); - mhi_cntrl->device_number = FIELD_GET(SOC_HW_VERSION_DEV_NUM_BMSK, soc_info); - mhi_cntrl->major_version = FIELD_GET(SOC_HW_VERSION_MAJOR_VER_BMSK, soc_info); - mhi_cntrl->minor_version = FIELD_GET(SOC_HW_VERSION_MINOR_VER_BMSK, soc_info); - mhi_cntrl->index = ida_alloc(&mhi_controller_ida, GFP_KERNEL); if (mhi_cntrl->index < 0) { ret = mhi_cntrl->index; diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index 091244cf17c6..5fe49311b8eb 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -15,12 +15,6 @@ extern struct bus_type mhi_bus_type; #define MHI_SOC_RESET_REQ_OFFSET 0xb0 #define MHI_SOC_RESET_REQ BIT(0) -#define SOC_HW_VERSION_OFFS 0x224 -#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28) -#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16) -#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8) -#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0) - struct mhi_ctxt { struct mhi_event_ctxt *er_ctxt; struct mhi_chan_ctxt *chan_ctxt; diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 474d32cb0520..77b8c0a26674 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -320,10 +320,6 @@ struct mhi_controller_config { * @hw_ev_rings: Number of hardware event rings * @sw_ev_rings: Number of software event rings * @nr_irqs: Number of IRQ allocated by bus master (required) - * @family_number: MHI controller family number - * @device_number: MHI controller device number - * @major_version: MHI controller major revision number - * @minor_version: MHI controller minor revision number * @serial_number: MHI controller serial number obtained from BHI * @mhi_event: MHI event ring configurations table * @mhi_cmd: MHI command ring configurations table @@ -368,15 +364,6 @@ struct mhi_controller_config { * Fields marked as (required) need to be populated by the controller driver * before calling mhi_register_controller(). For the fields marked as (optional) * they can be populated depending on the usecase. - * - * The following fields are present for the purpose of implementing any device - * specific quirks or customizations for specific MHI revisions used in device - * by the controller drivers. The MHI stack will just populate these fields - * during mhi_register_controller(): - * family_number - * device_number - * major_version - * minor_version */ struct mhi_controller { struct device *cntrl_dev; @@ -407,10 +394,6 @@ struct mhi_controller { u32 hw_ev_rings; u32 sw_ev_rings; u32 nr_irqs; - u32 family_number; - u32 device_number; - u32 major_version; - u32 minor_version; u32 serial_number; struct mhi_event *mhi_event; -- cgit v1.2.3 From 41197eb5f993d5572ea533c9378c49ca81e24c8e Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Tue, 30 Jan 2024 09:49:45 +0800 Subject: dt-bindings: reset: sophgo: support SG2042 Add bindings for the reset generator on the SOPHGO SG2042 RISC-V SoC. Signed-off-by: Chen Wang Reviewed-by: Krzysztof Kozlowski Reviewed-by: Inochi Amaoto Link: https://lore.kernel.org/r/35c348437b6e18972ccaf90d9c38040caccd1f11.1706577450.git.unicorn_wang@outlook.com Signed-off-by: Philipp Zabel --- .../bindings/reset/sophgo,sg2042-reset.yaml | 35 +++++++++ include/dt-bindings/reset/sophgo,sg2042-reset.h | 87 ++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml create mode 100644 include/dt-bindings/reset/sophgo,sg2042-reset.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml b/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml new file mode 100644 index 000000000000..76e1931f0908 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/sophgo,sg2042-reset.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/sophgo,sg2042-reset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sophgo SG2042 SoC Reset Controller + +maintainers: + - Chen Wang + +properties: + compatible: + const: sophgo,sg2042-reset + + reg: + maxItems: 1 + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - "#reset-cells" + +additionalProperties: false + +examples: + - | + rstgen: reset-controller@c00 { + compatible = "sophgo,sg2042-reset"; + reg = <0xc00 0xc>; + #reset-cells = <1>; + }; diff --git a/include/dt-bindings/reset/sophgo,sg2042-reset.h b/include/dt-bindings/reset/sophgo,sg2042-reset.h new file mode 100644 index 000000000000..9ab0980625c1 --- /dev/null +++ b/include/dt-bindings/reset/sophgo,sg2042-reset.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* + * Copyright (C) 2023 Sophgo Technology Inc. All rights reserved. + */ + +#ifndef __DT_BINDINGS_RESET_SOPHGO_SG2042_H_ +#define __DT_BINDINGS_RESET_SOPHGO_SG2042_H_ + +#define RST_MAIN_AP 0 +#define RST_RISCV_CPU 1 +#define RST_RISCV_LOW_SPEED_LOGIC 2 +#define RST_RISCV_CMN 3 +#define RST_HSDMA 4 +#define RST_SYSDMA 5 +#define RST_EFUSE0 6 +#define RST_EFUSE1 7 +#define RST_RTC 8 +#define RST_TIMER 9 +#define RST_WDT 10 +#define RST_AHB_ROM0 11 +#define RST_AHB_ROM1 12 +#define RST_I2C0 13 +#define RST_I2C1 14 +#define RST_I2C2 15 +#define RST_I2C3 16 +#define RST_GPIO0 17 +#define RST_GPIO1 18 +#define RST_GPIO2 19 +#define RST_PWM 20 +#define RST_AXI_SRAM0 21 +#define RST_AXI_SRAM1 22 +#define RST_SF0 23 +#define RST_SF1 24 +#define RST_LPC 25 +#define RST_ETH0 26 +#define RST_EMMC 27 +#define RST_SD 28 +#define RST_UART0 29 +#define RST_UART1 30 +#define RST_UART2 31 +#define RST_UART3 32 +#define RST_SPI0 33 +#define RST_SPI1 34 +#define RST_DBG_I2C 35 +#define RST_PCIE0 36 +#define RST_PCIE1 37 +#define RST_DDR0 38 +#define RST_DDR1 39 +#define RST_DDR2 40 +#define RST_DDR3 41 +#define RST_FAU0 42 +#define RST_FAU1 43 +#define RST_FAU2 44 +#define RST_RXU0 45 +#define RST_RXU1 46 +#define RST_RXU2 47 +#define RST_RXU3 48 +#define RST_RXU4 49 +#define RST_RXU5 50 +#define RST_RXU6 51 +#define RST_RXU7 52 +#define RST_RXU8 53 +#define RST_RXU9 54 +#define RST_RXU10 55 +#define RST_RXU11 56 +#define RST_RXU12 57 +#define RST_RXU13 58 +#define RST_RXU14 59 +#define RST_RXU15 60 +#define RST_RXU16 61 +#define RST_RXU17 62 +#define RST_RXU18 63 +#define RST_RXU19 64 +#define RST_RXU20 65 +#define RST_RXU21 66 +#define RST_RXU22 67 +#define RST_RXU23 68 +#define RST_RXU24 69 +#define RST_RXU25 70 +#define RST_RXU26 71 +#define RST_RXU27 72 +#define RST_RXU28 73 +#define RST_RXU29 74 +#define RST_RXU30 75 +#define RST_RXU31 76 + +#endif /* __DT_BINDINGS_RESET_SOPHGO_SG2042_H_ */ -- cgit v1.2.3 From 26ea8511c849f9fea325bcdbd8b41ea031a53afe Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 29 Jan 2024 12:52:11 +0100 Subject: of: Add of_phandle_args_equal() helper Add a helper comparing two "struct of_phandle_args" to avoid reinventing the wheel. Reviewed-by: Philipp Zabel Acked-by: Rob Herring Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240129115216.96479-2-krzysztof.kozlowski@linaro.org Signed-off-by: Philipp Zabel --- include/linux/of.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index 6a9ddf20e79a..85bcc05b278d 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -1065,6 +1065,22 @@ static inline int of_parse_phandle_with_optional_args(const struct device_node * 0, index, out_args); } +/** + * of_phandle_args_equal() - Compare two of_phandle_args + * @a1: First of_phandle_args to compare + * @a2: Second of_phandle_args to compare + * + * Return: True if a1 and a2 are the same (same node pointer, same phandle + * args), false otherwise. + */ +static inline bool of_phandle_args_equal(const struct of_phandle_args *a1, + const struct of_phandle_args *a2) +{ + return a1->np == a2->np && + a1->args_count == a2->args_count && + !memcmp(a1->args, a2->args, sizeof(a1->args[0]) * a1->args_count); +} + /** * of_property_count_u8_elems - Count the number of u8 elements in a property * -- cgit v1.2.3 From 0f28982835c29752cdb657f1f8316b2ea42c407a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 29 Jan 2024 12:52:12 +0100 Subject: cpufreq: do not open-code of_phandle_args_equal() Use newly added of_phandle_args_equal() helper to compare two of_phandle_args. Acked-by: Viresh Kumar Reviewed-by: Philipp Zabel Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240129115216.96479-3-krzysztof.kozlowski@linaro.org Signed-off-by: Philipp Zabel --- include/linux/cpufreq.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index afda5f24d3dd..3cd06dafb04b 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -1149,8 +1149,7 @@ static inline int of_perf_domain_get_sharing_cpumask(int pcpu, const char *list_ if (ret < 0) continue; - if (pargs->np == args.np && pargs->args_count == args.args_count && - !memcmp(pargs->args, args.args, sizeof(args.args[0]) * args.args_count)) + if (of_phandle_args_equal(pargs, &args)) cpumask_set_cpu(cpu, cpumask); of_node_put(args.np); -- cgit v1.2.3 From c721f189e89c0d4db119d7bb2b46768d0fb5f6b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 29 Jan 2024 12:52:14 +0100 Subject: reset: Instantiate reset GPIO controller for shared reset-gpios Devices sharing a reset GPIO could use the reset framework for coordinated handling of that shared GPIO line. We have several cases of such needs, at least for Devicetree-based platforms. If Devicetree-based device requests a reset line, while "resets" Devicetree property is missing but there is a "reset-gpios" one, instantiate a new "reset-gpio" platform device which will handle such reset line. This allows seamless handling of such shared reset-gpios without need of changing Devicetree binding [1]. To avoid creating multiple "reset-gpio" platform devices, store the Devicetree "reset-gpios" GPIO specifiers used for new devices on a linked list. Later such Devicetree GPIO specifier (phandle to GPIO controller, GPIO number and GPIO flags) is used to check if reset controller for given GPIO was already registered. If two devices have conflicting "reset-gpios" property, e.g. with different ACTIVE_xxx flags, this would allow to spawn two separate "reset-gpio" devices, where the second would fail probing on busy GPIO request. Link: https://lore.kernel.org/all/YXi5CUCEi7YmNxXM@robh.at.kernel.org/ [1] Cc: Bartosz Golaszewski Cc: Chris Packham Cc: Sean Anderson Reviewed-by: Philipp Zabel Signed-off-by: Krzysztof Kozlowski Acked-by: Bartosz Golaszewski Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20240129115216.96479-5-krzysztof.kozlowski@linaro.org Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 224 ++++++++++++++++++++++++++++++++++++--- include/linux/reset-controller.h | 4 + 2 files changed, 215 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 4d5a78d3c085..dba74e857be6 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -5,14 +5,19 @@ * Copyright 2013 Philipp Zabel, Pengutronix */ #include +#include #include #include #include #include #include +#include +#include +#include #include #include #include +#include #include #include #include @@ -23,6 +28,11 @@ static LIST_HEAD(reset_controller_list); static DEFINE_MUTEX(reset_lookup_mutex); static LIST_HEAD(reset_lookup_list); +/* Protects reset_gpio_lookup_list */ +static DEFINE_MUTEX(reset_gpio_lookup_mutex); +static LIST_HEAD(reset_gpio_lookup_list); +static DEFINE_IDA(reset_gpio_ida); + /** * struct reset_control - a reset control * @rcdev: a pointer to the reset controller device @@ -63,6 +73,16 @@ struct reset_control_array { struct reset_control *rstc[] __counted_by(num_rstcs); }; +/** + * struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices + * @of_args: phandle to the reset controller with all the args like GPIO number + * @list: list entry for the reset_gpio_lookup_list + */ +struct reset_gpio_lookup { + struct of_phandle_args of_args; + struct list_head list; +}; + static const char *rcdev_name(struct reset_controller_dev *rcdev) { if (rcdev->dev) @@ -71,6 +91,9 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev) if (rcdev->of_node) return rcdev->of_node->full_name; + if (rcdev->of_args) + return rcdev->of_args->np->full_name; + return NULL; } @@ -99,6 +122,9 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, */ int reset_controller_register(struct reset_controller_dev *rcdev) { + if (rcdev->of_node && rcdev->of_args) + return -EINVAL; + if (!rcdev->of_xlate) { rcdev->of_reset_n_cells = 1; rcdev->of_xlate = of_reset_simple_xlate; @@ -813,12 +839,171 @@ static void __reset_control_put_internal(struct reset_control *rstc) kref_put(&rstc->refcnt, __reset_control_release); } +static int __reset_add_reset_gpio_lookup(int id, struct device_node *np, + unsigned int gpio, + unsigned int of_flags) +{ + const struct fwnode_handle *fwnode = of_fwnode_handle(np); + unsigned int lookup_flags; + const char *label_tmp; + + /* + * Later we map GPIO flags between OF and Linux, however not all + * constants from include/dt-bindings/gpio/gpio.h and + * include/linux/gpio/machine.h match each other. + */ + if (of_flags > GPIO_ACTIVE_LOW) { + pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n", + of_flags, gpio); + return -EINVAL; + } + + struct gpio_device *gdev __free(gpio_device_put) = gpio_device_find_by_fwnode(fwnode); + if (!gdev) + return -EPROBE_DEFER; + + label_tmp = gpio_device_get_label(gdev); + if (!label_tmp) + return -EINVAL; + + char *label __free(kfree) = kstrdup(label_tmp, GFP_KERNEL); + if (!label) + return -ENOMEM; + + /* Size: one lookup entry plus sentinel */ + struct gpiod_lookup_table *lookup __free(kfree) = kzalloc(struct_size(lookup, table, 2), + GFP_KERNEL); + if (!lookup) + return -ENOMEM; + + lookup->dev_id = kasprintf(GFP_KERNEL, "reset-gpio.%d", id); + if (!lookup->dev_id) + return -ENOMEM; + + lookup_flags = GPIO_PERSISTENT; + lookup_flags |= of_flags & GPIO_ACTIVE_LOW; + lookup->table[0] = GPIO_LOOKUP(no_free_ptr(label), gpio, "reset", + lookup_flags); + + /* Not freed on success, because it is persisent subsystem data. */ + gpiod_add_lookup_table(no_free_ptr(lookup)); + + return 0; +} + +/* + * @args: phandle to the GPIO provider with all the args like GPIO number + */ +static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) +{ + struct reset_gpio_lookup *rgpio_dev; + struct platform_device *pdev; + int id, ret; + + /* + * Currently only #gpio-cells=2 is supported with the meaning of: + * args[0]: GPIO number + * args[1]: GPIO flags + * TODO: Handle other cases. + */ + if (args->args_count != 2) + return -ENOENT; + + /* + * Registering reset-gpio device might cause immediate + * bind, resulting in its probe() registering new reset controller thus + * taking reset_list_mutex lock via reset_controller_register(). + */ + lockdep_assert_not_held(&reset_list_mutex); + + mutex_lock(&reset_gpio_lookup_mutex); + + list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) { + if (args->np == rgpio_dev->of_args.np) { + if (of_phandle_args_equal(args, &rgpio_dev->of_args)) + goto out; /* Already on the list, done */ + } + } + + id = ida_alloc(&reset_gpio_ida, GFP_KERNEL); + if (id < 0) { + ret = id; + goto err_unlock; + } + + /* Not freed on success, because it is persisent subsystem data. */ + rgpio_dev = kzalloc(sizeof(*rgpio_dev), GFP_KERNEL); + if (!rgpio_dev) { + ret = -ENOMEM; + goto err_ida_free; + } + + ret = __reset_add_reset_gpio_lookup(id, args->np, args->args[0], + args->args[1]); + if (ret < 0) + goto err_kfree; + + rgpio_dev->of_args = *args; + /* + * We keep the device_node reference, but of_args.np is put at the end + * of __of_reset_control_get(), so get it one more time. + * Hold reference as long as rgpio_dev memory is valid. + */ + of_node_get(rgpio_dev->of_args.np); + pdev = platform_device_register_data(NULL, "reset-gpio", id, + &rgpio_dev->of_args, + sizeof(rgpio_dev->of_args)); + ret = PTR_ERR_OR_ZERO(pdev); + if (ret) + goto err_put; + + list_add(&rgpio_dev->list, &reset_gpio_lookup_list); + +out: + mutex_unlock(&reset_gpio_lookup_mutex); + + return 0; + +err_put: + of_node_put(rgpio_dev->of_args.np); +err_kfree: + kfree(rgpio_dev); +err_ida_free: + ida_free(&reset_gpio_ida, id); +err_unlock: + mutex_unlock(&reset_gpio_lookup_mutex); + + return ret; +} + +static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_args *args, + bool gpio_fallback) +{ + struct reset_controller_dev *rcdev; + + lockdep_assert_held(&reset_list_mutex); + + list_for_each_entry(rcdev, &reset_controller_list, list) { + if (gpio_fallback) { + if (rcdev->of_args && of_phandle_args_equal(args, + rcdev->of_args)) + return rcdev; + } else { + if (args->np == rcdev->of_node) + return rcdev; + } + } + + return NULL; +} + struct reset_control * __of_reset_control_get(struct device_node *node, const char *id, int index, bool shared, bool optional, bool acquired) { + bool gpio_fallback = false; struct reset_control *rstc; - struct reset_controller_dev *r, *rcdev; + struct reset_controller_dev *rcdev; struct of_phandle_args args; int rstc_id; int ret; @@ -839,39 +1024,52 @@ __of_reset_control_get(struct device_node *node, const char *id, int index, index, &args); if (ret == -EINVAL) return ERR_PTR(ret); - if (ret) - return optional ? NULL : ERR_PTR(ret); + if (ret) { + if (!IS_ENABLED(CONFIG_RESET_GPIO)) + return optional ? NULL : ERR_PTR(ret); - mutex_lock(&reset_list_mutex); - rcdev = NULL; - list_for_each_entry(r, &reset_controller_list, list) { - if (args.np == r->of_node) { - rcdev = r; - break; + /* + * There can be only one reset-gpio for regular devices, so + * don't bother with the "reset-gpios" phandle index. + */ + ret = of_parse_phandle_with_args(node, "reset-gpios", "#gpio-cells", + 0, &args); + if (ret) + return optional ? NULL : ERR_PTR(ret); + + gpio_fallback = true; + + ret = __reset_add_reset_gpio_device(&args); + if (ret) { + rstc = ERR_PTR(ret); + goto out_put; } } + mutex_lock(&reset_list_mutex); + rcdev = __reset_find_rcdev(&args, gpio_fallback); if (!rcdev) { rstc = ERR_PTR(-EPROBE_DEFER); - goto out; + goto out_unlock; } if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { rstc = ERR_PTR(-EINVAL); - goto out; + goto out_unlock; } rstc_id = rcdev->of_xlate(rcdev, &args); if (rstc_id < 0) { rstc = ERR_PTR(rstc_id); - goto out; + goto out_unlock; } /* reset_list_mutex also protects the rcdev's reset_control list */ rstc = __reset_control_get_internal(rcdev, rstc_id, shared, acquired); -out: +out_unlock: mutex_unlock(&reset_list_mutex); +out_put: of_node_put(args.np); return rstc; diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index 0fa4f60e1186..357df16ede32 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -60,6 +60,9 @@ struct reset_control_lookup { * @reset_control_head: head of internal list of requested reset controls * @dev: corresponding driver model device struct * @of_node: corresponding device tree node as phandle target + * @of_args: for reset-gpios controllers: corresponding phandle args with + * of_node and GPIO number complementing of_node; either this or + * of_node should be present * @of_reset_n_cells: number of cells in reset line specifiers * @of_xlate: translation function to translate from specifier as found in the * device tree to id as given to the reset control ops, defaults @@ -73,6 +76,7 @@ struct reset_controller_dev { struct list_head reset_control_head; struct device *dev; struct device_node *of_node; + const struct of_phandle_args *of_args; int of_reset_n_cells; int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec); -- cgit v1.2.3 From 14b526f55ba5916856126f9793309fd6de5c5e7e Mon Sep 17 00:00:00 2001 From: Erick Archer Date: Sat, 17 Feb 2024 15:29:13 +0100 Subject: RDMA/uverbs: Remove flexible arrays from struct *_filter When a struct containing a flexible array is included in another struct, and there is a member after the struct-with-flex-array, there is a possibility of memory overlap. These cases must be audited [1]. See: struct inner { ... int flex[]; }; struct outer { ... struct inner header; int overlap; ... }; This is the scenario for all the "struct *_filter" structures that are included in the following "struct ib_flow_spec_*" structures: struct ib_flow_spec_eth struct ib_flow_spec_ib struct ib_flow_spec_ipv4 struct ib_flow_spec_ipv6 struct ib_flow_spec_tcp_udp struct ib_flow_spec_tunnel struct ib_flow_spec_esp struct ib_flow_spec_gre struct ib_flow_spec_mpls The pattern is like the one shown below: struct *_filter { ... u8 real_sz[]; }; struct ib_flow_spec_* { ... struct *_filter val; struct *_filter mask; }; In this case, the trailing flexible array "real_sz" is never allocated and is only used to calculate the size of the structures. Here the use of the "offsetof" helper can be changed by the "sizeof" operator because the goal is to get the size of these structures. Therefore, the trailing flexible arrays can also be removed. However, due to the trailing padding that can be induced in structs it is possible that the: offsetof(struct *_filter, real_sz) != sizeof(struct *_filter) This situation happens with the "struct ib_flow_ipv6_filter" and to avoid it the "__packed" macro is used in this structure. But now, the "sizeof(struct ib_flow_ipv6_filter)" has changed. This is not a problem since this size is not used in the code. The situation now is that "sizeof(struct ib_flow_spec_ipv6)" has also changed (this struct contains the struct ib_flow_ipv6_filter). This is also not a problem since it is only used to set the size of the "union ib_flow_spec", which can store all the "ib_flow_spec_*" structures. Link: https://lore.kernel.org/r/20240217142913.4285-1-erick.archer@gmx.com Signed-off-by: Erick Archer Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/uverbs_cmd.c | 16 ++++++++-------- include/rdma/ib_verbs.h | 19 +------------------ 2 files changed, 9 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 6de05ade2ba9..3d3ee3eca983 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -2737,7 +2737,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type, switch (ib_spec->type & ~IB_FLOW_SPEC_INNER) { case IB_FLOW_SPEC_ETH: - ib_filter_sz = offsetof(struct ib_flow_eth_filter, real_sz); + ib_filter_sz = sizeof(struct ib_flow_eth_filter); actual_filter_sz = spec_filter_size(kern_spec_mask, kern_filter_sz, ib_filter_sz); @@ -2748,7 +2748,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type, memcpy(&ib_spec->eth.mask, kern_spec_mask, actual_filter_sz); break; case IB_FLOW_SPEC_IPV4: - ib_filter_sz = offsetof(struct ib_flow_ipv4_filter, real_sz); + ib_filter_sz = sizeof(struct ib_flow_ipv4_filter); actual_filter_sz = spec_filter_size(kern_spec_mask, kern_filter_sz, ib_filter_sz); @@ -2759,7 +2759,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type, memcpy(&ib_spec->ipv4.mask, kern_spec_mask, actual_filter_sz); break; case IB_FLOW_SPEC_IPV6: - ib_filter_sz = offsetof(struct ib_flow_ipv6_filter, real_sz); + ib_filter_sz = sizeof(struct ib_flow_ipv6_filter); actual_filter_sz = spec_filter_size(kern_spec_mask, kern_filter_sz, ib_filter_sz); @@ -2775,7 +2775,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type, break; case IB_FLOW_SPEC_TCP: case IB_FLOW_SPEC_UDP: - ib_filter_sz = offsetof(struct ib_flow_tcp_udp_filter, real_sz); + ib_filter_sz = sizeof(struct ib_flow_tcp_udp_filter); actual_filter_sz = spec_filter_size(kern_spec_mask, kern_filter_sz, ib_filter_sz); @@ -2786,7 +2786,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type, memcpy(&ib_spec->tcp_udp.mask, kern_spec_mask, actual_filter_sz); break; case IB_FLOW_SPEC_VXLAN_TUNNEL: - ib_filter_sz = offsetof(struct ib_flow_tunnel_filter, real_sz); + ib_filter_sz = sizeof(struct ib_flow_tunnel_filter); actual_filter_sz = spec_filter_size(kern_spec_mask, kern_filter_sz, ib_filter_sz); @@ -2801,7 +2801,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type, return -EINVAL; break; case IB_FLOW_SPEC_ESP: - ib_filter_sz = offsetof(struct ib_flow_esp_filter, real_sz); + ib_filter_sz = sizeof(struct ib_flow_esp_filter); actual_filter_sz = spec_filter_size(kern_spec_mask, kern_filter_sz, ib_filter_sz); @@ -2812,7 +2812,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type, memcpy(&ib_spec->esp.mask, kern_spec_mask, actual_filter_sz); break; case IB_FLOW_SPEC_GRE: - ib_filter_sz = offsetof(struct ib_flow_gre_filter, real_sz); + ib_filter_sz = sizeof(struct ib_flow_gre_filter); actual_filter_sz = spec_filter_size(kern_spec_mask, kern_filter_sz, ib_filter_sz); @@ -2823,7 +2823,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type, memcpy(&ib_spec->gre.mask, kern_spec_mask, actual_filter_sz); break; case IB_FLOW_SPEC_MPLS: - ib_filter_sz = offsetof(struct ib_flow_mpls_filter, real_sz); + ib_filter_sz = sizeof(struct ib_flow_mpls_filter); actual_filter_sz = spec_filter_size(kern_spec_mask, kern_filter_sz, ib_filter_sz); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index b7b6b58dd348..477bf9dd5e71 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1910,8 +1910,6 @@ struct ib_flow_eth_filter { u8 src_mac[6]; __be16 ether_type; __be16 vlan_tag; - /* Must be last */ - u8 real_sz[]; }; struct ib_flow_spec_eth { @@ -1924,8 +1922,6 @@ struct ib_flow_spec_eth { struct ib_flow_ib_filter { __be16 dlid; __u8 sl; - /* Must be last */ - u8 real_sz[]; }; struct ib_flow_spec_ib { @@ -1949,8 +1945,6 @@ struct ib_flow_ipv4_filter { u8 tos; u8 ttl; u8 flags; - /* Must be last */ - u8 real_sz[]; }; struct ib_flow_spec_ipv4 { @@ -1967,9 +1961,7 @@ struct ib_flow_ipv6_filter { u8 next_hdr; u8 traffic_class; u8 hop_limit; - /* Must be last */ - u8 real_sz[]; -}; +} __packed; struct ib_flow_spec_ipv6 { u32 type; @@ -1981,8 +1973,6 @@ struct ib_flow_spec_ipv6 { struct ib_flow_tcp_udp_filter { __be16 dst_port; __be16 src_port; - /* Must be last */ - u8 real_sz[]; }; struct ib_flow_spec_tcp_udp { @@ -1994,7 +1984,6 @@ struct ib_flow_spec_tcp_udp { struct ib_flow_tunnel_filter { __be32 tunnel_id; - u8 real_sz[]; }; /* ib_flow_spec_tunnel describes the Vxlan tunnel @@ -2010,8 +1999,6 @@ struct ib_flow_spec_tunnel { struct ib_flow_esp_filter { __be32 spi; __be32 seq; - /* Must be last */ - u8 real_sz[]; }; struct ib_flow_spec_esp { @@ -2025,8 +2012,6 @@ struct ib_flow_gre_filter { __be16 c_ks_res0_ver; __be16 protocol; __be32 key; - /* Must be last */ - u8 real_sz[]; }; struct ib_flow_spec_gre { @@ -2038,8 +2023,6 @@ struct ib_flow_spec_gre { struct ib_flow_mpls_filter { __be32 tag; - /* Must be last */ - u8 real_sz[]; }; struct ib_flow_spec_mpls { -- cgit v1.2.3 From aa3c88990f77bb9acb3d445337bc088031ac63f9 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 22 Feb 2024 12:41:00 -0800 Subject: sysfs: Document new "group visible" helpers Add documentation and examples for how to use DEFINE_SYSFS_GROUP_VISIBLE() and SYSFS_GROUP_VISIBLE(). Recall that the motivation for this work is that it is easier to reason about the lifetime of statically defined sysfs attributes that become visible at device_add() time rather than dynamically adding them later. DEFINE_SYSFS_GROUP_VISIBLE() tackles one of the reasons to opt for dynamically created attributes which did not have a facility for hiding empty directories. Signed-off-by: Dan Williams Link: https://lore.kernel.org/r/170863446065.1479840.10697164014098377292.stgit@dwillia2-xfh.jf.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/sysfs.h | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index a42642b277dd..dabf7f4f3581 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -105,8 +105,42 @@ struct attribute_group { #define SYSFS_GROUP_INVISIBLE 020000 /* - * The first call to is_visible() in the create / update path may - * indicate visibility for the entire group + * DEFINE_SYSFS_GROUP_VISIBLE(name): + * A helper macro to pair with the assignment of ".is_visible = + * SYSFS_GROUP_VISIBLE(name)", that arranges for the directory + * associated with a named attribute_group to optionally be hidden. + * This allows for static declaration of attribute_groups, and the + * simplification of attribute visibility lifetime that implies, + * without polluting sysfs with empty attribute directories. + * Ex. + * + * static umode_t example_attr_visible(struct kobject *kobj, + * struct attribute *attr, int n) + * { + * if (example_attr_condition) + * return 0; + * else if (ro_attr_condition) + * return 0444; + * return a->mode; + * } + * + * static bool example_group_visible(struct kobject *kobj) + * { + * if (example_group_condition) + * return false; + * return true; + * } + * + * DEFINE_SYSFS_GROUP_VISIBLE(example); + * + * static struct attribute_group example_group = { + * .name = "example", + * .is_visible = SYSFS_GROUP_VISIBLE(example), + * .attrs = &example_attrs, + * }; + * + * Note that it expects _attr_visible and _group_visible to + * be defined. */ #define DEFINE_SYSFS_GROUP_VISIBLE(name) \ static inline umode_t sysfs_group_visible_##name( \ @@ -119,7 +153,9 @@ struct attribute_group { /* * Same as DEFINE_SYSFS_GROUP_VISIBLE, but for groups with only binary - * attributes + * attributes. If an attribute_group defines both text and binary + * attributes, the group visibility is determined by the function + * specified to is_visible() not is_bin_visible() */ #define DEFINE_SYSFS_BIN_GROUP_VISIBLE(name) \ static inline umode_t sysfs_group_visible_##name( \ -- cgit v1.2.3 From 04edfa7fa059ba50d3236b55ba0ae23b1721e868 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 22 Feb 2024 12:41:06 -0800 Subject: sysfs: Introduce DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE() One of the first users of DEFINE_SYSFS_GROUP_VISIBLE() did this: static umode_t dp0_attr_visible(struct kobject *kobj, struct attribute *attr, int n) { struct sdw_slave *slave = dev_to_sdw_dev(kobj_to_dev(kobj)); if (slave->prop.dp0_prop) return attr->mode; return 0; } static bool dp0_group_visible(struct kobject *kobj) { struct sdw_slave *slave = dev_to_sdw_dev(kobj_to_dev(kobj)); if (slave->prop.dp0_prop) return true; return false; } DEFINE_SYSFS_GROUP_VISIBLE(dp0); ...i.e. the _group_visible() helper is identical to the _attr_visible() helper. Use the "simple" helper to reduce that to: static bool dp0_group_visible(struct kobject *kobj) { struct sdw_slave *slave = dev_to_sdw_dev(kobj_to_dev(kobj)); if (slave->prop.dp0_prop) return true; return false; } DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(dp0); Remove the need to specify per attribute visibility if the goal is to hide the entire group. Signed-off-by: Dan Williams Link: https://lore.kernel.org/r/170863446625.1479840.10593839479268727913.stgit@dwillia2-xfh.jf.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/sysfs.h | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index dabf7f4f3581..326341c62385 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -140,7 +140,9 @@ struct attribute_group { * }; * * Note that it expects _attr_visible and _group_visible to - * be defined. + * be defined. For cases where individual attributes do not need + * separate visibility consideration, only entire group visibility at + * once, see DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(). */ #define DEFINE_SYSFS_GROUP_VISIBLE(name) \ static inline umode_t sysfs_group_visible_##name( \ @@ -151,6 +153,38 @@ struct attribute_group { return name##_attr_visible(kobj, attr, n); \ } +/* + * DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(name): + * A helper macro to pair with SYSFS_GROUP_VISIBLE() that like + * DEFINE_SYSFS_GROUP_VISIBLE() controls group visibility, but does + * not require the implementation of a per-attribute visibility + * callback. + * Ex. + * + * static bool example_group_visible(struct kobject *kobj) + * { + * if (example_group_condition) + * return false; + * return true; + * } + * + * DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(example); + * + * static struct attribute_group example_group = { + * .name = "example", + * .is_visible = SYSFS_GROUP_VISIBLE(example), + * .attrs = &example_attrs, + * }; + */ +#define DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(name) \ + static inline umode_t sysfs_group_visible_##name( \ + struct kobject *kobj, struct attribute *a, int n) \ + { \ + if (n == 0 && !name##_group_visible(kobj)) \ + return SYSFS_GROUP_INVISIBLE; \ + return a->mode; \ + } + /* * Same as DEFINE_SYSFS_GROUP_VISIBLE, but for groups with only binary * attributes. If an attribute_group defines both text and binary @@ -166,6 +200,15 @@ struct attribute_group { return name##_attr_visible(kobj, attr, n); \ } +#define DEFINE_SIMPLE_SYSFS_BIN_GROUP_VISIBLE(name) \ + static inline umode_t sysfs_group_visible_##name( \ + struct kobject *kobj, struct bin_attribute *a, int n) \ + { \ + if (n == 0 && !name##_group_visible(kobj)) \ + return SYSFS_GROUP_INVISIBLE; \ + return a->mode; \ + } + #define SYSFS_GROUP_VISIBLE(fn) sysfs_group_visible_##fn /* -- cgit v1.2.3 From 5270316c9fec8cc99aa0e0a258509c5c7f789d12 Mon Sep 17 00:00:00 2001 From: Petr Pavlu Date: Thu, 22 Feb 2024 14:35:00 +0100 Subject: kbuild: Use -fmin-function-alignment when available GCC recently added option -fmin-function-alignment, which should appear in GCC 14. Unlike -falign-functions, this option causes all functions to be aligned at the specified value, including the cold ones. In particular, when an arm64 kernel is built with DYNAMIC_FTRACE_WITH_CALL_OPS=y, the 8-byte function alignment is required for correct functionality. This was done by -falign-functions=8 and having workarounds in the kernel to force the compiler to follow this alignment. The new -fmin-function-alignment option directly guarantees it. Detect availability of -fmin-function-alignment and use it instead of -falign-functions when present. Introduce CC_HAS_SANE_FUNCTION_ALIGNMENT and enable __cold to work as expected when it is set. Signed-off-by: Petr Pavlu Reviewed-by: Nathan Chancellor Acked-by: Mark Rutland Signed-off-by: Masahiro Yamada --- Makefile | 7 +++++++ arch/Kconfig | 12 ++++++++++++ include/linux/compiler_types.h | 10 +++++----- 3 files changed, 24 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Makefile b/Makefile index bc54a6cc8dc5..d84c0fb215fd 100644 --- a/Makefile +++ b/Makefile @@ -974,8 +974,15 @@ export CC_FLAGS_CFI endif ifneq ($(CONFIG_FUNCTION_ALIGNMENT),0) +# Set the minimal function alignment. Use the newer GCC option +# -fmin-function-alignment if it is available, or fall back to -falign-funtions. +# See also CONFIG_CC_HAS_SANE_FUNCTION_ALIGNMENT. +ifdef CONFIG_CC_HAS_MIN_FUNCTION_ALIGNMENT +KBUILD_CFLAGS += -fmin-function-alignment=$(CONFIG_FUNCTION_ALIGNMENT) +else KBUILD_CFLAGS += -falign-functions=$(CONFIG_FUNCTION_ALIGNMENT) endif +endif # arch Makefile may override CC so keep this after arch Makefile is included NOSTDINC_FLAGS += -nostdinc diff --git a/arch/Kconfig b/arch/Kconfig index a5af0edd3eb8..bd6c6335efac 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1507,4 +1507,16 @@ config FUNCTION_ALIGNMENT default 4 if FUNCTION_ALIGNMENT_4B default 0 +config CC_HAS_MIN_FUNCTION_ALIGNMENT + # Detect availability of the GCC option -fmin-function-alignment which + # guarantees minimal alignment for all functions, unlike + # -falign-functions which the compiler ignores for cold functions. + def_bool $(cc-option, -fmin-function-alignment=8) + +config CC_HAS_SANE_FUNCTION_ALIGNMENT + # Set if the guaranteed alignment with -fmin-function-alignment is + # available or extra care is required in the kernel. Clang provides + # strict alignment always, even with -falign-functions. + def_bool CC_HAS_MIN_FUNCTION_ALIGNMENT || CC_IS_CLANG + endmenu diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 0caf354cb94b..fb8888678687 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -99,17 +99,17 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { } * gcc: https://gcc.gnu.org/onlinedocs/gcc/Label-Attributes.html#index-cold-label-attribute * * When -falign-functions=N is in use, we must avoid the cold attribute as - * contemporary versions of GCC drop the alignment for cold functions. Worse, - * GCC can implicitly mark callees of cold functions as cold themselves, so - * it's not sufficient to add __function_aligned here as that will not ensure - * that callees are correctly aligned. + * GCC drops the alignment for cold functions. Worse, GCC can implicitly mark + * callees of cold functions as cold themselves, so it's not sufficient to add + * __function_aligned here as that will not ensure that callees are correctly + * aligned. * * See: * * https://lore.kernel.org/lkml/Y77%2FqVgvaJidFpYt@FVFF77S0Q05N * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88345#c9 */ -#if !defined(CONFIG_CC_IS_GCC) || (CONFIG_FUNCTION_ALIGNMENT == 0) +#if defined(CONFIG_CC_HAS_SANE_FUNCTION_ALIGNMENT) || (CONFIG_FUNCTION_ALIGNMENT == 0) #define __cold __attribute__((__cold__)) #else #define __cold -- cgit v1.2.3 From 7e84c961b2eb062d2f47037dcca52dcd1d3615b5 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 19 Dec 2023 02:33:24 +0000 Subject: mtd: ubi: introduce pre-removal notification for UBI volumes Introduce a new notification type UBI_VOLUME_SHUTDOWN to inform users that a volume is just about to be removed. This is needed because users (such as the NVMEM subsystem) expect that at the time their removal function is called, the parenting device is still available (for removal of sysfs nodes, for example, in case of NVMEM which otherwise WARNs on volume removal). Signed-off-by: Daniel Golle Signed-off-by: Richard Weinberger --- drivers/mtd/ubi/build.c | 19 ++++++++++++++----- drivers/mtd/ubi/kapi.c | 2 +- drivers/mtd/ubi/ubi.h | 2 ++ drivers/mtd/ubi/vmt.c | 17 +++++++++++++++-- include/linux/mtd/ubi.h | 2 ++ 5 files changed, 34 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 8c3f763e4ddb..a7e3a6246c0e 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -93,7 +93,7 @@ static struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; /* Serializes UBI devices creations and removals */ DEFINE_MUTEX(ubi_devices_mutex); -/* Protects @ubi_devices and @ubi->ref_count */ +/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */ static DEFINE_SPINLOCK(ubi_devices_lock); /* "Show" method for files in '//class/ubi/' */ @@ -261,6 +261,9 @@ struct ubi_device *ubi_get_device(int ubi_num) spin_lock(&ubi_devices_lock); ubi = ubi_devices[ubi_num]; + if (ubi && ubi->is_dead) + ubi = NULL; + if (ubi) { ubi_assert(ubi->ref_count >= 0); ubi->ref_count += 1; @@ -298,7 +301,7 @@ struct ubi_device *ubi_get_by_major(int major) spin_lock(&ubi_devices_lock); for (i = 0; i < UBI_MAX_DEVICES; i++) { ubi = ubi_devices[i]; - if (ubi && MAJOR(ubi->cdev.dev) == major) { + if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { ubi_assert(ubi->ref_count >= 0); ubi->ref_count += 1; get_device(&ubi->dev); @@ -327,7 +330,7 @@ int ubi_major2num(int major) for (i = 0; i < UBI_MAX_DEVICES; i++) { struct ubi_device *ubi = ubi_devices[i]; - if (ubi && MAJOR(ubi->cdev.dev) == major) { + if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { ubi_num = ubi->ubi_num; break; } @@ -514,7 +517,7 @@ static void ubi_free_volumes_from(struct ubi_device *ubi, int from) int i; for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { - if (!ubi->volumes[i]) + if (!ubi->volumes[i] || ubi->volumes[i]->is_dead) continue; ubi_eba_replace_table(ubi->volumes[i], NULL); ubi_fastmap_destroy_checkmap(ubi->volumes[i]); @@ -1099,7 +1102,6 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) return -EINVAL; spin_lock(&ubi_devices_lock); - put_device(&ubi->dev); ubi->ref_count -= 1; if (ubi->ref_count) { if (!anyway) { @@ -1110,6 +1112,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) ubi_err(ubi, "%s reference count %d, destroy anyway", ubi->ubi_name, ubi->ref_count); } + ubi->is_dead = true; + spin_unlock(&ubi_devices_lock); + + ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL); + + spin_lock(&ubi_devices_lock); + put_device(&ubi->dev); ubi_devices[ubi_num] = NULL; spin_unlock(&ubi_devices_lock); diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index fbf3a7fe2af7..f1ea8677467f 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) spin_lock(&ubi->volumes_lock); vol = ubi->volumes[vol_id]; - if (!vol) + if (!vol || vol->is_dead) goto out_unlock; err = -EBUSY; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index a588381c50ad..32009a24869e 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -337,6 +337,7 @@ struct ubi_volume { int writers; int exclusive; int metaonly; + bool is_dead; int reserved_pebs; int vol_type; @@ -561,6 +562,7 @@ struct ubi_device { spinlock_t volumes_lock; int ref_count; int image_seq; + bool is_dead; int rsvd_pebs; int avail_pebs; diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 990571287e84..eaf8328f6fc3 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct device *dev, struct ubi_device *ubi = vol->ubi; spin_lock(&ubi->volumes_lock); - if (!ubi->volumes[vol->vol_id]) { + if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) { spin_unlock(&ubi->volumes_lock); return -ENODEV; } @@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) /* Ensure that the name is unique */ for (i = 0; i < ubi->vtbl_slots; i++) - if (ubi->volumes[i] && + if (ubi->volumes[i] && !ubi->volumes[i]->is_dead && ubi->volumes[i]->name_len == req->name_len && !strcmp(ubi->volumes[i]->name, req->name)) { ubi_err(ubi, "volume \"%s\" exists (ID %d)", @@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) err = -EBUSY; goto out_unlock; } + + /* + * Mark volume as dead at this point to prevent that anyone + * can take a reference to the volume from now on. + * This is necessary as we have to release the spinlock before + * calling ubi_volume_notify. + */ + vol->is_dead = true; + spin_unlock(&ubi->volumes_lock); + + ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN); + + spin_lock(&ubi->volumes_lock); ubi->volumes[vol_id] = NULL; spin_unlock(&ubi->volumes_lock); diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index a529347fd75b..562f92504f2b 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -192,6 +192,7 @@ struct ubi_device_info { * or a volume was removed) * @UBI_VOLUME_RESIZED: a volume has been re-sized * @UBI_VOLUME_RENAMED: a volume has been re-named + * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users * @UBI_VOLUME_UPDATED: data has been written to a volume * * These constants define which type of event has happened when a volume @@ -202,6 +203,7 @@ enum { UBI_VOLUME_REMOVED, UBI_VOLUME_RESIZED, UBI_VOLUME_RENAMED, + UBI_VOLUME_SHUTDOWN, UBI_VOLUME_UPDATED, }; -- cgit v1.2.3 From 0dc5b8abfa03e8720cb341699e3ece194058bb03 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 20 Feb 2024 08:22:13 +0100 Subject: interconnect: constify of_phandle_args in xlate The xlate callbacks are supposed to translate of_phandle_args to proper provider without modifying the of_phandle_args. Make the argument pointer to const for code safety and readability. Acked-by: Konrad Dybcio Acked-by: Thierry Reding # Tegra Signed-off-by: Krzysztof Kozlowski Acked-by: Alim Akhtar # Samsung Link: https://lore.kernel.org/r/20240220072213.35779-1-krzysztof.kozlowski@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 4 ++-- drivers/interconnect/qcom/icc-common.c | 3 ++- drivers/interconnect/qcom/icc-common.h | 3 ++- drivers/interconnect/samsung/exynos.c | 2 +- drivers/memory/tegra/mc.c | 2 +- drivers/memory/tegra/tegra124-emc.c | 2 +- drivers/memory/tegra/tegra124.c | 2 +- drivers/memory/tegra/tegra186-emc.c | 2 +- drivers/memory/tegra/tegra20-emc.c | 2 +- drivers/memory/tegra/tegra20.c | 2 +- drivers/memory/tegra/tegra30-emc.c | 2 +- drivers/memory/tegra/tegra30.c | 2 +- include/linux/interconnect-provider.h | 11 ++++++----- include/soc/tegra/mc.h | 7 ++++--- 14 files changed, 25 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 50bac2d79d9b..5d1010cafed8 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -343,7 +343,7 @@ EXPORT_SYMBOL_GPL(icc_std_aggregate); * an array of icc nodes specified in the icc_onecell_data struct when * registering the provider. */ -struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec, +struct icc_node *of_icc_xlate_onecell(const struct of_phandle_args *spec, void *data) { struct icc_onecell_data *icc_data = data; @@ -368,7 +368,7 @@ EXPORT_SYMBOL_GPL(of_icc_xlate_onecell); * Returns a valid pointer to struct icc_node_data on success or ERR_PTR() * on failure. */ -struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec) +struct icc_node_data *of_icc_get_from_provider(const struct of_phandle_args *spec) { struct icc_node *node = ERR_PTR(-EPROBE_DEFER); struct icc_node_data *data = NULL; diff --git a/drivers/interconnect/qcom/icc-common.c b/drivers/interconnect/qcom/icc-common.c index f27f4fdc4531..9b9ee113f172 100644 --- a/drivers/interconnect/qcom/icc-common.c +++ b/drivers/interconnect/qcom/icc-common.c @@ -9,7 +9,8 @@ #include "icc-common.h" -struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data) +struct icc_node_data *qcom_icc_xlate_extended(const struct of_phandle_args *spec, + void *data) { struct icc_node_data *ndata; struct icc_node *node; diff --git a/drivers/interconnect/qcom/icc-common.h b/drivers/interconnect/qcom/icc-common.h index 33bb2c38dff3..21c39b163948 100644 --- a/drivers/interconnect/qcom/icc-common.h +++ b/drivers/interconnect/qcom/icc-common.h @@ -8,6 +8,7 @@ #include -struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data); +struct icc_node_data *qcom_icc_xlate_extended(const struct of_phandle_args *spec, + void *data); #endif diff --git a/drivers/interconnect/samsung/exynos.c b/drivers/interconnect/samsung/exynos.c index 1ba14cb45d5a..c9e5361e17c5 100644 --- a/drivers/interconnect/samsung/exynos.c +++ b/drivers/interconnect/samsung/exynos.c @@ -82,7 +82,7 @@ static int exynos_generic_icc_set(struct icc_node *src, struct icc_node *dst) return 0; } -static struct icc_node *exynos_generic_icc_xlate(struct of_phandle_args *spec, +static struct icc_node *exynos_generic_icc_xlate(const struct of_phandle_args *spec, void *data) { struct exynos_icc_priv *priv = data; diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index a083921a8968..224b488794e5 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -755,7 +755,7 @@ const char *const tegra_mc_error_names[8] = { [6] = "SMMU translation error", }; -struct icc_node *tegra_mc_icc_xlate(struct of_phandle_args *spec, void *data) +struct icc_node *tegra_mc_icc_xlate(const struct of_phandle_args *spec, void *data) { struct tegra_mc *mc = icc_provider_to_tegra_mc(data); struct icc_node *node; diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index 00ed2b6a0d1b..47c0c19e13fd 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c @@ -1285,7 +1285,7 @@ to_tegra_emc_provider(struct icc_provider *provider) } static struct icc_node_data * -emc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data) +emc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data) { struct icc_provider *provider = data; struct icc_node_data *ndata; diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c index 470b7dbab2c2..9d7393e19f12 100644 --- a/drivers/memory/tegra/tegra124.c +++ b/drivers/memory/tegra/tegra124.c @@ -1170,7 +1170,7 @@ static int tegra124_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw, } static struct icc_node_data * -tegra124_mc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data) +tegra124_mc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data) { struct tegra_mc *mc = icc_provider_to_tegra_mc(data); const struct tegra_mc_client *client; diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c index fcd4aea48bda..57d9ae12fcfe 100644 --- a/drivers/memory/tegra/tegra186-emc.c +++ b/drivers/memory/tegra/tegra186-emc.c @@ -236,7 +236,7 @@ static int tegra_emc_icc_set_bw(struct icc_node *src, struct icc_node *dst) } static struct icc_node * -tegra_emc_of_icc_xlate(struct of_phandle_args *spec, void *data) +tegra_emc_of_icc_xlate(const struct of_phandle_args *spec, void *data) { struct icc_provider *provider = data; struct icc_node *node; diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c index fd595c851a27..97cf59523b0b 100644 --- a/drivers/memory/tegra/tegra20-emc.c +++ b/drivers/memory/tegra/tegra20-emc.c @@ -950,7 +950,7 @@ to_tegra_emc_provider(struct icc_provider *provider) } static struct icc_node_data * -emc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data) +emc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data) { struct icc_provider *provider = data; struct icc_node_data *ndata; diff --git a/drivers/memory/tegra/tegra20.c b/drivers/memory/tegra/tegra20.c index aa4b97d5e732..a3022e715dee 100644 --- a/drivers/memory/tegra/tegra20.c +++ b/drivers/memory/tegra/tegra20.c @@ -390,7 +390,7 @@ static int tegra20_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw, } static struct icc_node_data * -tegra20_mc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data) +tegra20_mc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data) { struct tegra_mc *mc = icc_provider_to_tegra_mc(data); unsigned int i, idx = spec->args[0]; diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c index 9eae25c57ec6..d7b0a23c2d7d 100644 --- a/drivers/memory/tegra/tegra30-emc.c +++ b/drivers/memory/tegra/tegra30-emc.c @@ -1468,7 +1468,7 @@ to_tegra_emc_provider(struct icc_provider *provider) } static struct icc_node_data * -emc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data) +emc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data) { struct icc_provider *provider = data; struct icc_node_data *ndata; diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c index 06f8b35e0a14..d3e685c8431f 100644 --- a/drivers/memory/tegra/tegra30.c +++ b/drivers/memory/tegra/tegra30.c @@ -1332,7 +1332,7 @@ static int tegra30_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw, } static struct icc_node_data * -tegra30_mc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data) +tegra30_mc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data) { struct tegra_mc *mc = icc_provider_to_tegra_mc(data); const struct tegra_mc_client *client; diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index 7ba183f221f1..f5aef8784692 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -36,7 +36,7 @@ struct icc_onecell_data { struct icc_node *nodes[] __counted_by(num_nodes); }; -struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec, +struct icc_node *of_icc_xlate_onecell(const struct of_phandle_args *spec, void *data); /** @@ -65,8 +65,9 @@ struct icc_provider { u32 peak_bw, u32 *agg_avg, u32 *agg_peak); void (*pre_aggregate)(struct icc_node *node); int (*get_bw)(struct icc_node *node, u32 *avg, u32 *peak); - struct icc_node* (*xlate)(struct of_phandle_args *spec, void *data); - struct icc_node_data* (*xlate_extended)(struct of_phandle_args *spec, void *data); + struct icc_node* (*xlate)(const struct of_phandle_args *spec, void *data); + struct icc_node_data* (*xlate_extended)(const struct of_phandle_args *spec, + void *data); struct device *dev; int users; bool inter_set; @@ -124,7 +125,7 @@ int icc_nodes_remove(struct icc_provider *provider); void icc_provider_init(struct icc_provider *provider); int icc_provider_register(struct icc_provider *provider); void icc_provider_deregister(struct icc_provider *provider); -struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec); +struct icc_node_data *of_icc_get_from_provider(const struct of_phandle_args *spec); void icc_sync_state(struct device *dev); #else @@ -171,7 +172,7 @@ static inline int icc_provider_register(struct icc_provider *provider) static inline void icc_provider_deregister(struct icc_provider *provider) { } -static inline struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec) +static inline struct icc_node_data *of_icc_get_from_provider(const struct of_phandle_args *spec) { return ERR_PTR(-ENOTSUPP); } diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index af1d73a7f0cd..6ee4c59db620 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -146,13 +146,14 @@ struct tegra_mc_icc_ops { int (*set)(struct icc_node *src, struct icc_node *dst); int (*aggregate)(struct icc_node *node, u32 tag, u32 avg_bw, u32 peak_bw, u32 *agg_avg, u32 *agg_peak); - struct icc_node* (*xlate)(struct of_phandle_args *spec, void *data); - struct icc_node_data *(*xlate_extended)(struct of_phandle_args *spec, + struct icc_node* (*xlate)(const struct of_phandle_args *spec, void *data); + struct icc_node_data *(*xlate_extended)(const struct of_phandle_args *spec, void *data); int (*get_bw)(struct icc_node *node, u32 *avg, u32 *peak); }; -struct icc_node *tegra_mc_icc_xlate(struct of_phandle_args *spec, void *data); +struct icc_node *tegra_mc_icc_xlate(const struct of_phandle_args *spec, + void *data); extern const struct tegra_mc_icc_ops tegra_mc_icc_ops; struct tegra_mc_ops { -- cgit v1.2.3 From b8a730836c6b1788ca2fbd6bcc2ac99e97ef7de9 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Mon, 19 Feb 2024 09:45:50 -0300 Subject: thunderbolt: Constify the struct device_type usage Since commit aed65af1cc2f ("drivers: make device_type const"), the driver core can properly handle constant struct device_type. Move the tb_domain_type, tb_retimer_type, tb_switch_type, usb4_port_device_type, tb_service_type and tb_xdomain_type variables to be constant structures as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Signed-off-by: Mika Westerberg --- drivers/thunderbolt/domain.c | 2 +- drivers/thunderbolt/retimer.c | 2 +- drivers/thunderbolt/switch.c | 2 +- drivers/thunderbolt/tb.h | 8 ++++---- drivers/thunderbolt/usb4_port.c | 2 +- drivers/thunderbolt/xdomain.c | 4 ++-- include/linux/thunderbolt.h | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 1d915a6c61a4..0023017299f7 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -326,7 +326,7 @@ static void tb_domain_release(struct device *dev) kfree(tb); } -struct device_type tb_domain_type = { +const struct device_type tb_domain_type = { .name = "thunderbolt_domain", .release = tb_domain_release, }; diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index d49d6628dbf2..6bb49bdcd6c1 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -356,7 +356,7 @@ static void tb_retimer_release(struct device *dev) kfree(rt); } -struct device_type tb_retimer_type = { +const struct device_type tb_retimer_type = { .name = "thunderbolt_retimer", .groups = retimer_groups, .release = tb_retimer_release, diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index bca6f28c553b..5a617ea285a7 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2327,7 +2327,7 @@ static const struct dev_pm_ops tb_switch_pm_ops = { NULL) }; -struct device_type tb_switch_type = { +const struct device_type tb_switch_type = { .name = "thunderbolt_device", .release = tb_switch_release, .uevent = tb_switch_uevent, diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 8e87d1a0005c..feed8ecaf712 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -749,10 +749,10 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, struct tb *icm_probe(struct tb_nhi *nhi); struct tb *tb_probe(struct tb_nhi *nhi); -extern struct device_type tb_domain_type; -extern struct device_type tb_retimer_type; -extern struct device_type tb_switch_type; -extern struct device_type usb4_port_device_type; +extern const struct device_type tb_domain_type; +extern const struct device_type tb_retimer_type; +extern const struct device_type tb_switch_type; +extern const struct device_type usb4_port_device_type; int tb_domain_init(void); void tb_domain_exit(void); diff --git a/drivers/thunderbolt/usb4_port.c b/drivers/thunderbolt/usb4_port.c index e355bfd6343f..5150879888ca 100644 --- a/drivers/thunderbolt/usb4_port.c +++ b/drivers/thunderbolt/usb4_port.c @@ -243,7 +243,7 @@ static void usb4_port_device_release(struct device *dev) kfree(usb4); } -struct device_type usb4_port_device_type = { +const struct device_type usb4_port_device_type = { .name = "usb4_port", .groups = usb4_port_device_groups, .release = usb4_port_device_release, diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index b48df88981bd..940ae97987ff 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1002,7 +1002,7 @@ static void tb_service_release(struct device *dev) kfree(svc); } -struct device_type tb_service_type = { +const struct device_type tb_service_type = { .name = "thunderbolt_service", .groups = tb_service_attr_groups, .uevent = tb_service_uevent, @@ -1893,7 +1893,7 @@ static const struct dev_pm_ops tb_xdomain_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(tb_xdomain_suspend, tb_xdomain_resume) }; -struct device_type tb_xdomain_type = { +const struct device_type tb_xdomain_type = { .name = "thunderbolt_xdomain", .release = tb_xdomain_release, .pm = &tb_xdomain_pm_ops, diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 2c835e5c41f6..4338ea9ac4fd 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -87,8 +87,8 @@ struct tb { }; extern const struct bus_type tb_bus_type; -extern struct device_type tb_service_type; -extern struct device_type tb_xdomain_type; +extern const struct device_type tb_service_type; +extern const struct device_type tb_xdomain_type; #define TB_LINKS_PER_PHY_PORT 2 -- cgit v1.2.3 From 4a5917cd504c7afd5e9de7166eb710687a9b026f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 13 Feb 2024 12:48:52 +0200 Subject: clk: ti: Improve clksel clock bit parsing for reg property Because of legacy reasons, the TI clksel composite clocks can have overlapping reg properties, and use a custom ti,bit-shift property. For the clksel clocks we can start using of the standard reg property instead of the custom ti,bit-shift property. To do this, let's add a ti_clk_get_legacy_bit_shift() helper, and make ti_clk_get_reg_addr() populate the clock bit offset. This makes it possible to update the devicetree files to use the reg property one clock at a time. Acked-by: Stephen Boyd Signed-off-by: Tony Lindgren --- drivers/clk/ti/apll.c | 11 +++------ drivers/clk/ti/clk.c | 57 ++++++++++++++++++++++++++++++++++++++++------ drivers/clk/ti/clock.h | 1 + drivers/clk/ti/divider.c | 5 +--- drivers/clk/ti/gate.c | 9 ++------ drivers/clk/ti/interface.c | 4 +--- drivers/clk/ti/mux.c | 6 ++--- include/linux/clk/ti.h | 3 +++ 8 files changed, 63 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index 93183287c58d..43514e6f3b78 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c @@ -376,14 +376,9 @@ static void __init of_omap2_apll_setup(struct device_node *node) } clk_hw->fixed_rate = val; - if (of_property_read_u32(node, "ti,bit-shift", &val)) { - pr_err("%pOFn missing bit-shift\n", node); - goto cleanup; - } - - clk_hw->enable_bit = val; - ad->enable_mask = 0x3 << val; - ad->autoidle_mask = 0x3 << val; + clk_hw->enable_bit = ti_clk_get_legacy_bit_shift(node); + ad->enable_mask = 0x3 << clk_hw->enable_bit; + ad->autoidle_mask = 0x3 << clk_hw->enable_bit; if (of_property_read_u32(node, "ti,idlest-shift", &val)) { pr_err("%pOFn missing idlest-shift\n", node); diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 53173bb250da..f2117fef7c7d 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -307,8 +308,9 @@ int __init ti_clk_retry_init(struct device_node *node, void *user, int ti_clk_get_reg_addr(struct device_node *node, int index, struct clk_omap_reg *reg) { - u32 val; - int i; + u32 clksel_addr, val; + bool is_clksel = false; + int i, err; for (i = 0; i < CLK_MAX_MEMMAPS; i++) { if (clocks_node_ptr[i] == node->parent) @@ -324,21 +326,62 @@ int ti_clk_get_reg_addr(struct device_node *node, int index, reg->index = i; - if (of_property_read_u32_index(node, "reg", index, &val)) { - if (of_property_read_u32_index(node->parent, "reg", - index, &val)) { - pr_err("%pOFn or parent must have reg[%d]!\n", - node, index); + if (of_device_is_compatible(node->parent, "ti,clksel")) { + err = of_property_read_u32_index(node->parent, "reg", index, &clksel_addr); + if (err) { + pr_err("%pOFn parent clksel must have reg[%d]!\n", node, index); return -EINVAL; } + is_clksel = true; + } + + err = of_property_read_u32_index(node, "reg", index, &val); + if (err && is_clksel) { + /* Legacy clksel with no reg and a possible ti,bit-shift property */ + reg->offset = clksel_addr; + reg->bit = ti_clk_get_legacy_bit_shift(node); + reg->ptr = NULL; + + return 0; } + /* Updated clksel clock with a proper reg property */ + if (is_clksel) { + reg->offset = clksel_addr; + reg->bit = val; + reg->ptr = NULL; + return 0; + } + + /* Other clocks that may or may not have ti,bit-shift property */ reg->offset = val; + reg->bit = ti_clk_get_legacy_bit_shift(node); reg->ptr = NULL; return 0; } +/** + * ti_clk_get_legacy_bit_shift - get bit shift for a clock register + * @node: device node for the clock + * + * Gets the clock register bit shift using the legacy ti,bit-shift + * property. Only needed for legacy clock, and can be eventually + * dropped once all the composite clocks use a clksel node with a + * proper reg property. + */ +int ti_clk_get_legacy_bit_shift(struct device_node *node) +{ + int err; + u32 val; + + err = of_property_read_u32(node, "ti,bit-shift", &val); + if (!err && in_range(val, 0, 32)) + return val; + + return 0; +} + void ti_clk_latch(struct clk_omap_reg *reg, s8 shift) { u32 latch; diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 16a9f7c2280a..2de7acea1ea0 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -216,6 +216,7 @@ int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div, int ti_clk_get_reg_addr(struct device_node *node, int index, struct clk_omap_reg *reg); +int ti_clk_get_legacy_bit_shift(struct device_node *node); void ti_dt_clocks_register(struct ti_dt_clk *oclks); int ti_clk_retry_init(struct device_node *node, void *user, ti_of_clk_init_cb_t func); diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index 5d5bb123ba94..ade99ab6cfa9 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -477,10 +477,7 @@ static int __init ti_clk_divider_populate(struct device_node *node, if (ret) return ret; - if (!of_property_read_u32(node, "ti,bit-shift", &val)) - div->shift = val; - else - div->shift = 0; + div->shift = div->reg.bit; if (!of_property_read_u32(node, "ti,latch-bit", &val)) div->latch = val; diff --git a/drivers/clk/ti/gate.c b/drivers/clk/ti/gate.c index 8e477d50d0fd..a9febd6356b8 100644 --- a/drivers/clk/ti/gate.c +++ b/drivers/clk/ti/gate.c @@ -132,7 +132,6 @@ static void __init _of_ti_gate_clk_setup(struct device_node *node, struct clk_omap_reg reg; const char *name; u8 enable_bit = 0; - u32 val; u32 flags = 0; u8 clk_gate_flags = 0; @@ -140,8 +139,7 @@ static void __init _of_ti_gate_clk_setup(struct device_node *node, if (ti_clk_get_reg_addr(node, 0, ®)) return; - if (!of_property_read_u32(node, "ti,bit-shift", &val)) - enable_bit = val; + enable_bit = reg.bit; } if (of_clk_get_parent_count(node) != 1) { @@ -170,7 +168,6 @@ _of_ti_composite_gate_clk_setup(struct device_node *node, const struct clk_hw_omap_ops *hw_ops) { struct clk_hw_omap *gate; - u32 val = 0; gate = kzalloc(sizeof(*gate), GFP_KERNEL); if (!gate) @@ -179,9 +176,7 @@ _of_ti_composite_gate_clk_setup(struct device_node *node, if (ti_clk_get_reg_addr(node, 0, &gate->enable_reg)) goto cleanup; - of_property_read_u32(node, "ti,bit-shift", &val); - - gate->enable_bit = val; + gate->enable_bit = gate->enable_reg.bit; gate->ops = hw_ops; if (!ti_clk_add_component(node, &gate->hw, CLK_COMPONENT_TYPE_GATE)) diff --git a/drivers/clk/ti/interface.c b/drivers/clk/ti/interface.c index 172301c646f8..3eb35c87c0ed 100644 --- a/drivers/clk/ti/interface.c +++ b/drivers/clk/ti/interface.c @@ -66,13 +66,11 @@ static void __init _of_ti_interface_clk_setup(struct device_node *node, struct clk_omap_reg reg; u8 enable_bit = 0; const char *name; - u32 val; if (ti_clk_get_reg_addr(node, 0, ®)) return; - if (!of_property_read_u32(node, "ti,bit-shift", &val)) - enable_bit = val; + enable_bit = reg.bit; parent_name = of_clk_get_parent_name(node, 0); if (!parent_name) { diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index 1ebafa386be6..216d85d6aac6 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -189,7 +189,7 @@ static void of_mux_clk_setup(struct device_node *node) if (ti_clk_get_reg_addr(node, 0, ®)) goto cleanup; - of_property_read_u32(node, "ti,bit-shift", &shift); + shift = reg.bit; of_property_read_u32(node, "ti,latch-bit", &latch); @@ -252,7 +252,6 @@ static void __init of_ti_composite_mux_clk_setup(struct device_node *node) { struct clk_omap_mux *mux; unsigned int num_parents; - u32 val; mux = kzalloc(sizeof(*mux), GFP_KERNEL); if (!mux) @@ -261,8 +260,7 @@ static void __init of_ti_composite_mux_clk_setup(struct device_node *node) if (ti_clk_get_reg_addr(node, 0, &mux->reg)) goto cleanup; - if (!of_property_read_u32(node, "ti,bit-shift", &val)) - mux->shift = val; + mux->shift = mux->reg.bit; if (of_property_read_bool(node, "ti,index-starts-at-one")) mux->flags |= CLK_MUX_INDEX_ONE; diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index cbfcbf186ce3..e656f63efdce 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h @@ -13,11 +13,14 @@ /** * struct clk_omap_reg - OMAP register declaration * @offset: offset from the master IP module base address + * @bit: register bit offset * @index: index of the master IP module + * @flags: flags */ struct clk_omap_reg { void __iomem *ptr; u16 offset; + u8 bit; u8 index; u8 flags; }; -- cgit v1.2.3 From 96b171d6dba6a66c63312f35e3ac6465b2c2ca94 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 30 Jan 2024 13:48:35 -0800 Subject: scsi: core: Query the Block Limits Extension VPD page Parse the Reduced Stream Control Supported (RSCS) bit from the block limits extension VPD page. The RSCS bit is defined in SBC-5 r05 (https://www.t10.org/cgi-bin/ac.pl?t=f&f=sbc5r05.pdf). Reviewed-by: Avri Altman Reviewed-by: Daejun Park Cc: Martin K. Petersen Signed-off-by: Bart Van Assche Link: https://lore.kernel.org/r/20240130214911.1863909-10-bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi.c | 2 ++ drivers/scsi/scsi_sysfs.c | 10 ++++++++++ drivers/scsi/sd.c | 13 +++++++++++++ drivers/scsi/sd.h | 1 + include/scsi/scsi_device.h | 1 + 5 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 76d369343c7a..74cc3369dd8d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -499,6 +499,8 @@ void scsi_attach_vpd(struct scsi_device *sdev) scsi_update_vpd_page(sdev, 0xb1, &sdev->vpd_pgb1); if (vpd_buf->data[i] == 0xb2) scsi_update_vpd_page(sdev, 0xb2, &sdev->vpd_pgb2); + if (vpd_buf->data[i] == 0xb7) + scsi_update_vpd_page(sdev, 0xb7, &sdev->vpd_pgb7); } kfree(vpd_buf); } diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 24f6eefb6803..93652a786a46 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -449,6 +449,7 @@ static void scsi_device_dev_release(struct device *dev) struct scsi_vpd *vpd_pg80 = NULL, *vpd_pg83 = NULL; struct scsi_vpd *vpd_pg0 = NULL, *vpd_pg89 = NULL; struct scsi_vpd *vpd_pgb0 = NULL, *vpd_pgb1 = NULL, *vpd_pgb2 = NULL; + struct scsi_vpd *vpd_pgb7 = NULL; unsigned long flags; might_sleep(); @@ -494,6 +495,8 @@ static void scsi_device_dev_release(struct device *dev) lockdep_is_held(&sdev->inquiry_mutex)); vpd_pgb2 = rcu_replace_pointer(sdev->vpd_pgb2, vpd_pgb2, lockdep_is_held(&sdev->inquiry_mutex)); + vpd_pgb7 = rcu_replace_pointer(sdev->vpd_pgb7, vpd_pgb7, + lockdep_is_held(&sdev->inquiry_mutex)); mutex_unlock(&sdev->inquiry_mutex); if (vpd_pg0) @@ -510,6 +513,8 @@ static void scsi_device_dev_release(struct device *dev) kfree_rcu(vpd_pgb1, rcu); if (vpd_pgb2) kfree_rcu(vpd_pgb2, rcu); + if (vpd_pgb7) + kfree_rcu(vpd_pgb7, rcu); kfree(sdev->inquiry); kfree(sdev); @@ -921,6 +926,7 @@ sdev_vpd_pg_attr(pg89); sdev_vpd_pg_attr(pgb0); sdev_vpd_pg_attr(pgb1); sdev_vpd_pg_attr(pgb2); +sdev_vpd_pg_attr(pgb7); sdev_vpd_pg_attr(pg0); static ssize_t show_inquiry(struct file *filep, struct kobject *kobj, @@ -1295,6 +1301,9 @@ static umode_t scsi_sdev_bin_attr_is_visible(struct kobject *kobj, if (attr == &dev_attr_vpd_pgb2 && !sdev->vpd_pgb2) return 0; + if (attr == &dev_attr_vpd_pgb7 && !sdev->vpd_pgb7) + return 0; + return S_IRUGO; } @@ -1347,6 +1356,7 @@ static struct bin_attribute *scsi_sdev_bin_attrs[] = { &dev_attr_vpd_pgb0, &dev_attr_vpd_pgb1, &dev_attr_vpd_pgb2, + &dev_attr_vpd_pgb7, &dev_attr_inquiry, NULL }; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 0833b3e6aa6e..86b819fa04d9 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3108,6 +3108,18 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) rcu_read_unlock(); } +/* Parse the Block Limits Extension VPD page (0xb7) */ +static void sd_read_block_limits_ext(struct scsi_disk *sdkp) +{ + struct scsi_vpd *vpd; + + rcu_read_lock(); + vpd = rcu_dereference(sdkp->device->vpd_pgb7); + if (vpd && vpd->len >= 2) + sdkp->rscs = vpd->data[5] & 1; + rcu_read_unlock(); +} + /** * sd_read_block_characteristics - Query block dev. characteristics * @sdkp: disk to query @@ -3459,6 +3471,7 @@ static int sd_revalidate_disk(struct gendisk *disk) if (scsi_device_supports_vpd(sdp)) { sd_read_block_provisioning(sdkp); sd_read_block_limits(sdkp); + sd_read_block_limits_ext(sdkp); sd_read_block_characteristics(sdkp); sd_zbc_read_zones(sdkp, buffer); sd_read_cpr(sdkp); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 409dda5350d1..e4539122f2a2 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -151,6 +151,7 @@ struct scsi_disk { unsigned urswrz : 1; unsigned security : 1; unsigned ignore_medium_access_errors : 1; + bool rscs : 1; /* reduced stream control support */ }; #define to_scsi_disk(obj) container_of(obj, struct scsi_disk, disk_dev) diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 5ec1e71a09de..f670b55d803a 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -153,6 +153,7 @@ struct scsi_device { struct scsi_vpd __rcu *vpd_pgb0; struct scsi_vpd __rcu *vpd_pgb1; struct scsi_vpd __rcu *vpd_pgb2; + struct scsi_vpd __rcu *vpd_pgb7; struct scsi_target *sdev_target; -- cgit v1.2.3 From 4977c0f4523e1d6c87df4eedceb33d38f055c5fb Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 30 Jan 2024 13:48:36 -0800 Subject: scsi: scsi_proto: Add structures and constants related to I/O groups and streams Prepare for adding code that will query the I/O advice hints group descriptors and for adding code that will retrieve the stream status. Cc: Martin K. Petersen Signed-off-by: Bart Van Assche Link: https://lore.kernel.org/r/20240130214911.1863909-11-bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/scsi/Kconfig | 5 +++ drivers/scsi/Makefile | 2 ++ drivers/scsi/scsi_proto_test.c | 56 ++++++++++++++++++++++++++++++ include/scsi/scsi_proto.h | 78 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 drivers/scsi/scsi_proto_test.c (limited to 'include') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index addac7fbe37b..83b542abfc29 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -232,6 +232,11 @@ config SCSI_SCAN_ASYNC Note that this setting also affects whether resuming from system suspend will be performed asynchronously. +config SCSI_PROTO_TEST + tristate "scsi_proto.h unit tests" if !KUNIT_ALL_TESTS + depends on SCSI && KUNIT + default KUNIT_ALL_TESTS + menu "SCSI Transports" depends on SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index f055bfd54a68..1313ddf2fd1a 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -24,6 +24,8 @@ obj-$(CONFIG_SCSI_COMMON) += scsi_common.o obj-$(CONFIG_RAID_ATTRS) += raid_class.o +obj-$(CONFIG_SCSI_PROTO_TEST) += scsi_proto_test.o + # --- NOTE ORDERING HERE --- # For kernel non-modular link, transport attributes need to # be initialised before drivers diff --git a/drivers/scsi/scsi_proto_test.c b/drivers/scsi/scsi_proto_test.c new file mode 100644 index 000000000000..7fa0a78a2ad1 --- /dev/null +++ b/drivers/scsi/scsi_proto_test.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2023 Google LLC + */ +#include +#include +#include + +static void test_scsi_proto(struct kunit *test) +{ + static const union { + struct scsi_io_group_descriptor desc; + u8 arr[sizeof(struct scsi_io_group_descriptor)]; + } d = { .arr = { 0x45, 0, 0, 0, 0xb0, 0xe4, 0xe3 } }; + KUNIT_EXPECT_EQ(test, d.desc.io_advice_hints_mode + 0, 1); + KUNIT_EXPECT_EQ(test, d.desc.st_enble + 0, 1); + KUNIT_EXPECT_EQ(test, d.desc.cs_enble + 0, 0); + KUNIT_EXPECT_EQ(test, d.desc.ic_enable + 0, 1); + KUNIT_EXPECT_EQ(test, d.desc.acdlu + 0, 1); + KUNIT_EXPECT_EQ(test, d.desc.rlbsr + 0, 3); + KUNIT_EXPECT_EQ(test, d.desc.lbm_descriptor_type + 0, 0); + KUNIT_EXPECT_EQ(test, d.desc.params[0] + 0, 0xe4); + KUNIT_EXPECT_EQ(test, d.desc.params[1] + 0, 0xe3); + + static const union { + struct scsi_stream_status s; + u8 arr[sizeof(struct scsi_stream_status)]; + } ss = { .arr = { 0x80, 0, 0x12, 0x34, 0x3f } }; + KUNIT_EXPECT_EQ(test, ss.s.perm + 0, 1); + KUNIT_EXPECT_EQ(test, get_unaligned_be16(&ss.s.stream_identifier), + 0x1234); + KUNIT_EXPECT_EQ(test, ss.s.rel_lifetime + 0, 0x3f); + + static const union { + struct scsi_stream_status_header h; + u8 arr[sizeof(struct scsi_stream_status_header)]; + } sh = { .arr = { 1, 2, 3, 4, 0, 0, 5, 6 } }; + KUNIT_EXPECT_EQ(test, get_unaligned_be32(&sh.h.len), 0x1020304); + KUNIT_EXPECT_EQ(test, get_unaligned_be16(&sh.h.number_of_open_streams), + 0x506); +} + +static struct kunit_case scsi_proto_test_cases[] = { + KUNIT_CASE(test_scsi_proto), + {} +}; + +static struct kunit_suite scsi_proto_test_suite = { + .name = "scsi_proto", + .test_cases = scsi_proto_test_cases, +}; +kunit_test_suite(scsi_proto_test_suite); + +MODULE_DESCRIPTION(" unit tests"); +MODULE_AUTHOR("Bart Van Assche"); +MODULE_LICENSE("GPL"); diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index 07d65c1f59db..843106e1109f 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -10,6 +10,7 @@ #ifndef _SCSI_PROTO_H_ #define _SCSI_PROTO_H_ +#include #include /* @@ -126,6 +127,7 @@ #define SAI_READ_CAPACITY_16 0x10 #define SAI_GET_LBA_STATUS 0x12 #define SAI_REPORT_REFERRALS 0x13 +#define SAI_GET_STREAM_STATUS 0x16 /* values for maintenance in */ #define MI_REPORT_IDENTIFYING_INFORMATION 0x05 #define MI_REPORT_TARGET_PGS 0x0a @@ -275,6 +277,82 @@ struct scsi_lun { __u8 scsi_lun[8]; }; +/* SBC-5 IO advice hints group descriptor */ +struct scsi_io_group_descriptor { +#if defined(__BIG_ENDIAN) + u8 io_advice_hints_mode: 2; + u8 reserved1: 3; + u8 st_enble: 1; + u8 cs_enble: 1; + u8 ic_enable: 1; +#elif defined(__LITTLE_ENDIAN) + u8 ic_enable: 1; + u8 cs_enble: 1; + u8 st_enble: 1; + u8 reserved1: 3; + u8 io_advice_hints_mode: 2; +#else +#error +#endif + u8 reserved2[3]; + /* Logical block markup descriptor */ +#if defined(__BIG_ENDIAN) + u8 acdlu: 1; + u8 reserved3: 1; + u8 rlbsr: 2; + u8 lbm_descriptor_type: 4; +#elif defined(__LITTLE_ENDIAN) + u8 lbm_descriptor_type: 4; + u8 rlbsr: 2; + u8 reserved3: 1; + u8 acdlu: 1; +#else +#error +#endif + u8 params[2]; + u8 reserved4; + u8 reserved5[8]; +}; + +static_assert(sizeof(struct scsi_io_group_descriptor) == 16); + +/* SCSI stream status descriptor */ +struct scsi_stream_status { +#if defined(__BIG_ENDIAN) + u8 perm: 1; + u8 reserved1: 7; +#elif defined(__LITTLE_ENDIAN) + u8 reserved1: 7; + u8 perm: 1; +#else +#error +#endif + u8 reserved2; + __be16 stream_identifier; +#if defined(__BIG_ENDIAN) + u8 reserved3: 2; + u8 rel_lifetime: 6; +#elif defined(__LITTLE_ENDIAN) + u8 rel_lifetime: 6; + u8 reserved3: 2; +#else +#error +#endif + u8 reserved4[3]; +}; + +static_assert(sizeof(struct scsi_stream_status) == 8); + +/* GET STREAM STATUS parameter data */ +struct scsi_stream_status_header { + __be32 len; /* length in bytes of stream_status[] array. */ + u16 reserved; + __be16 number_of_open_streams; + DECLARE_FLEX_ARRAY(struct scsi_stream_status, stream_status); +}; + +static_assert(sizeof(struct scsi_stream_status_header) == 8); + /* SPC asymmetric access states */ #define SCSI_ACCESS_STATE_OPTIMAL 0x00 #define SCSI_ACCESS_STATE_ACTIVE 0x01 -- cgit v1.2.3 From f9e28904e6442019043a8e94ec6747a064d06003 Mon Sep 17 00:00:00 2001 From: Zhiguo Niu Date: Tue, 20 Feb 2024 14:11:24 +0800 Subject: f2fs: stop checkpoint when get a out-of-bounds segment There is low probability that an out-of-bounds segment will be got on a small-capacity device. In order to prevent subsequent write requests allocating block address from this invalid segment, which may cause unexpected issue, stop checkpoint should be performed. Also introduce a new stop cp reason: STOP_CP_REASON_NO_SEGMENT. Note, f2fs_stop_checkpoint(, false) is complex and it may sleep, so we should move it outside segmap_lock spinlock coverage in get_new_segment(). Signed-off-by: Zhiguo Niu Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 12 +++++++++++- include/linux/f2fs_fs.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 01067254ec40..dc511630eaa5 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2649,6 +2649,7 @@ static void get_new_segment(struct f2fs_sb_info *sbi, unsigned int old_zoneno = GET_ZONE_FROM_SEG(sbi, *newseg); bool init = true; int i; + int ret = 0; spin_lock(&free_i->segmap_lock); @@ -2673,7 +2674,10 @@ find_other_zone: if (secno >= MAIN_SECS(sbi)) { secno = find_first_zero_bit(free_i->free_secmap, MAIN_SECS(sbi)); - f2fs_bug_on(sbi, secno >= MAIN_SECS(sbi)); + if (secno >= MAIN_SECS(sbi)) { + ret = -ENOSPC; + goto out_unlock; + } } segno = GET_SEG_FROM_SEC(sbi, secno); zoneno = GET_ZONE_FROM_SEC(sbi, secno); @@ -2703,7 +2707,13 @@ got_it: f2fs_bug_on(sbi, test_bit(segno, free_i->free_segmap)); __set_inuse(sbi, segno); *newseg = segno; +out_unlock: spin_unlock(&free_i->segmap_lock); + + if (ret) { + f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_NO_SEGMENT); + f2fs_bug_on(sbi, 1); + } } static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 9b69c50255b2..755e9a41b196 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -75,6 +75,7 @@ enum stop_cp_reason { STOP_CP_REASON_CORRUPTED_SUMMARY, STOP_CP_REASON_UPDATE_INODE, STOP_CP_REASON_FLUSH_FAIL, + STOP_CP_REASON_NO_SEGMENT, STOP_CP_REASON_MAX, }; -- cgit v1.2.3 From 9c4058493b62f5ee7a0620e21d21592283c8f8f5 Mon Sep 17 00:00:00 2001 From: Danila Tikhonov Date: Thu, 22 Feb 2024 20:42:49 +0300 Subject: dt-bindings: interconnect: Add Qualcomm SM7150 DT bindings The Qualcomm SM7150 platform has several bus fabrics that could be controlled and tuned dynamically according to the bandwidth demand. Signed-off-by: Danila Tikhonov Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240222174250.80493-2-danila@jiaxyga.com Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,sm7150-rpmh.yaml | 84 ++++++++++++ .../dt-bindings/interconnect/qcom,sm7150-rpmh.h | 150 +++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml create mode 100644 include/dt-bindings/interconnect/qcom,sm7150-rpmh.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml new file mode 100644 index 000000000000..b565d1a382f6 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,sm7150-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on SM7150 + +maintainers: + - Danila Tikhonov + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). + + See also:: include/dt-bindings/interconnect/qcom,sm7150-rpmh.h + +allOf: + - $ref: qcom,rpmh-common.yaml# + +properties: + compatible: + enum: + - qcom,sm7150-aggre1-noc + - qcom,sm7150-aggre2-noc + - qcom,sm7150-compute-noc + - qcom,sm7150-config-noc + - qcom,sm7150-dc-noc + - qcom,sm7150-gem-noc + - qcom,sm7150-mc-virt + - qcom,sm7150-mmss-noc + - qcom,sm7150-system-noc + + reg: + maxItems: 1 + +# Child node's properties +patternProperties: + '^interconnect-[0-9]+$': + type: object + description: + The interconnect providers do not have a separate QoS register space, + but share parent's space. + + allOf: + - $ref: qcom,rpmh-common.yaml# + + properties: + compatible: + enum: + - qcom,sm7150-camnoc-virt + + required: + - compatible + + unevaluatedProperties: false + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + mc_virt: interconnect@1380000 { + compatible = "qcom,sm7150-mc-virt"; + reg = <0x01380000 0x40000>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + system_noc: interconnect@1620000 { + compatible = "qcom,sm7150-system-noc"; + reg = <0x01620000 0x40000>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + + camnoc_virt: interconnect-0 { + compatible = "qcom,sm7150-camnoc-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + }; diff --git a/include/dt-bindings/interconnect/qcom,sm7150-rpmh.h b/include/dt-bindings/interconnect/qcom,sm7150-rpmh.h new file mode 100644 index 000000000000..1f610eb832aa --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,sm7150-rpmh.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* + * Qualcomm SM7150 interconnect IDs + * + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2024, Danila Tikhonov + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_SM7150_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_SM7150_H + +#define MASTER_A1NOC_CFG 0 +#define MASTER_QUP_0 1 +#define MASTER_TSIF 2 +#define MASTER_EMMC 3 +#define MASTER_SDCC_2 4 +#define MASTER_SDCC_4 5 +#define MASTER_UFS_MEM 6 +#define A1NOC_SNOC_SLV 7 +#define SLAVE_SERVICE_A1NOC 8 + +#define MASTER_A2NOC_CFG 0 +#define MASTER_QDSS_BAM 1 +#define MASTER_QUP_1 2 +#define MASTER_CNOC_A2NOC 3 +#define MASTER_CRYPTO_CORE_0 4 +#define MASTER_IPA 5 +#define MASTER_PCIE 6 +#define MASTER_QDSS_ETR 7 +#define MASTER_USB3 8 +#define A2NOC_SNOC_SLV 9 +#define SLAVE_ANOC_PCIE_GEM_NOC 10 +#define SLAVE_SERVICE_A2NOC 11 + +#define MASTER_CAMNOC_HF0_UNCOMP 0 +#define MASTER_CAMNOC_RT_UNCOMP 1 +#define MASTER_CAMNOC_SF_UNCOMP 2 +#define MASTER_CAMNOC_NRT_UNCOMP 3 +#define SLAVE_CAMNOC_UNCOMP 4 + +#define MASTER_NPU 0 +#define SLAVE_CDSP_GEM_NOC 1 + +#define MASTER_SPDM 0 +#define SNOC_CNOC_MAS 1 +#define MASTER_QDSS_DAP 2 +#define SLAVE_A1NOC_CFG 3 +#define SLAVE_A2NOC_CFG 4 +#define SLAVE_AHB2PHY_NORTH 5 +#define SLAVE_AHB2PHY_SOUTH 6 +#define SLAVE_AHB2PHY_WEST 7 +#define SLAVE_AOP 8 +#define SLAVE_AOSS 9 +#define SLAVE_CAMERA_CFG 10 +#define SLAVE_CAMERA_NRT_THROTTLE_CFG 11 +#define SLAVE_CAMERA_RT_THROTTLE_CFG 12 +#define SLAVE_CLK_CTL 13 +#define SLAVE_CDSP_CFG 14 +#define SLAVE_RBCPR_CX_CFG 15 +#define SLAVE_RBCPR_MX_CFG 16 +#define SLAVE_CRYPTO_0_CFG 17 +#define SLAVE_CNOC_DDRSS 18 +#define SLAVE_DISPLAY_CFG 19 +#define SLAVE_DISPLAY_THROTTLE_CFG 20 +#define SLAVE_EMMC_CFG 21 +#define SLAVE_GLM 22 +#define SLAVE_GRAPHICS_3D_CFG 23 +#define SLAVE_IMEM_CFG 24 +#define SLAVE_IPA_CFG 25 +#define SLAVE_CNOC_MNOC_CFG 26 +#define SLAVE_PCIE_CFG 27 +#define SLAVE_PDM 28 +#define SLAVE_PIMEM_CFG 29 +#define SLAVE_PRNG 30 +#define SLAVE_QDSS_CFG 31 +#define SLAVE_QUP_0 32 +#define SLAVE_QUP_1 33 +#define SLAVE_SDCC_2 34 +#define SLAVE_SDCC_4 35 +#define SLAVE_SNOC_CFG 36 +#define SLAVE_SPDM_WRAPPER 37 +#define SLAVE_TCSR 38 +#define SLAVE_TLMM_NORTH 39 +#define SLAVE_TLMM_SOUTH 40 +#define SLAVE_TLMM_WEST 41 +#define SLAVE_TSIF 42 +#define SLAVE_UFS_MEM_CFG 43 +#define SLAVE_USB3 44 +#define SLAVE_VENUS_CFG 45 +#define SLAVE_VENUS_CVP_THROTTLE_CFG 46 +#define SLAVE_VENUS_THROTTLE_CFG 47 +#define SLAVE_VSENSE_CTRL_CFG 48 +#define SLAVE_CNOC_A2NOC 49 +#define SLAVE_SERVICE_CNOC 50 + +#define MASTER_CNOC_DC_NOC 0 +#define SLAVE_GEM_NOC_CFG 1 +#define SLAVE_LLCC_CFG 2 + +#define MASTER_AMPSS_M0 0 +#define MASTER_SYS_TCU 1 +#define MASTER_GEM_NOC_CFG 2 +#define MASTER_COMPUTE_NOC 3 +#define MASTER_MNOC_HF_MEM_NOC 4 +#define MASTER_MNOC_SF_MEM_NOC 5 +#define MASTER_GEM_NOC_PCIE_SNOC 6 +#define MASTER_SNOC_GC_MEM_NOC 7 +#define MASTER_SNOC_SF_MEM_NOC 8 +#define MASTER_GRAPHICS_3D 9 +#define SLAVE_MSS_PROC_MS_MPU_CFG 10 +#define SLAVE_GEM_NOC_SNOC 11 +#define SLAVE_LLCC 12 +#define SLAVE_SERVICE_GEM_NOC 13 + + +#define MASTER_LLCC 0 +#define SLAVE_EBI_CH0 1 + +#define MASTER_CNOC_MNOC_CFG 0 +#define MASTER_CAMNOC_HF0 1 +#define MASTER_CAMNOC_NRT 2 +#define MASTER_CAMNOC_RT 3 +#define MASTER_CAMNOC_SF 4 +#define MASTER_MDP_PORT0 5 +#define MASTER_MDP_PORT1 6 +#define MASTER_ROTATOR 7 +#define MASTER_VIDEO_P0 8 +#define MASTER_VIDEO_P1 9 +#define MASTER_VIDEO_PROC 10 +#define SLAVE_MNOC_SF_MEM_NOC 11 +#define SLAVE_MNOC_HF_MEM_NOC 12 +#define SLAVE_SERVICE_MNOC 13 + +#define MASTER_SNOC_CFG 0 +#define A1NOC_SNOC_MAS 1 +#define A2NOC_SNOC_MAS 2 +#define MASTER_GEM_NOC_SNOC 3 +#define MASTER_PIMEM 4 +#define MASTER_GIC 5 +#define SLAVE_APPSS 6 +#define SNOC_CNOC_SLV 7 +#define SLAVE_SNOC_GEM_NOC_GC 8 +#define SLAVE_SNOC_GEM_NOC_SF 9 +#define SLAVE_OCIMEM 10 +#define SLAVE_PIMEM 11 +#define SLAVE_SERVICE_SNOC 12 +#define SLAVE_QDSS_STM 13 +#define SLAVE_TCU 14 + +#endif -- cgit v1.2.3 From a74c0c9c3f7fa6fba34196d142bab93509f17dba Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 29 Feb 2024 14:23:57 +0100 Subject: USB: typec: no opencoding FIELD_GET We have a macro. It should be used. Signed-off-by: Oliver Neukum Reviewed-by: Mika Westerberg Link: https://lore.kernel.org/r/20240229132401.3270-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec_tbt.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h index c7a2153bd6f5..fa97d7e00f5c 100644 --- a/include/linux/usb/typec_tbt.h +++ b/include/linux/usb/typec_tbt.h @@ -3,6 +3,7 @@ #define __USB_TYPEC_TBT_H #include +#include #define USB_TYPEC_VENDOR_INTEL 0x8087 /* Alias for convenience */ @@ -25,7 +26,7 @@ struct typec_thunderbolt_data { /* TBT3 Device Discover Mode VDO bits */ #define TBT_MODE BIT(0) -#define TBT_ADAPTER(_vdo_) (((_vdo_) & BIT(16)) >> 16) +#define TBT_ADAPTER(_vdo_) FIELD_GET(BIT(16), _vdo_) #define TBT_ADAPTER_LEGACY 0 #define TBT_ADAPTER_TBT3 1 #define TBT_INTEL_SPECIFIC_B0 BIT(26) @@ -35,12 +36,12 @@ struct typec_thunderbolt_data { #define TBT_SET_ADAPTER(a) (((a) & 1) << 16) /* TBT3 Cable Discover Mode VDO bits */ -#define TBT_CABLE_SPEED(_vdo_) (((_vdo_) & GENMASK(18, 16)) >> 16) +#define TBT_CABLE_SPEED(_vdo_) FIELD_GET(GENMASK(18, 16), _vdo_) #define TBT_CABLE_USB3_GEN1 1 #define TBT_CABLE_USB3_PASSIVE 2 #define TBT_CABLE_10_AND_20GBPS 3 -#define TBT_CABLE_ROUNDED_SUPPORT(_vdo_) \ - (((_vdo_) & GENMASK(20, 19)) >> 19) +#define TBT_CABLE_ROUNDED_SUPPORT(_vdo_) FIELD_GET(GENMASK(20, 19), _vdo_) + #define TBT_GEN3_NON_ROUNDED 0 #define TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED 1 #define TBT_CABLE_OPTICAL BIT(21) -- cgit v1.2.3 From 6ec429d5887a41b2dc8d92e391552f5604085cc2 Mon Sep 17 00:00:00 2001 From: Junxian Huang Date: Fri, 1 Mar 2024 18:48:45 +0800 Subject: RDMA/hns: Support userspace configuring congestion control algorithm with QP granularity Currently, congestion control algorithm is statically configured in FW, and all QPs use the same algorithm(except UD which has a fixed configuration of DCQCN). This is not flexible enough. Support userspace configuring congestion control algorithm with QP granularity while creating QPs. If the algorithm is not specified in userspace, use the default one. Signed-off-by: Junxian Huang Link: https://lore.kernel.org/r/20240301104845.1141083-1-huangjunxian6@hisilicon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hns/hns_roce_device.h | 3 +- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 12 +----- drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 3 +- drivers/infiniband/hw/hns/hns_roce_main.c | 3 ++ drivers/infiniband/hw/hns/hns_roce_qp.c | 60 +++++++++++++++++++++++++++++ include/uapi/rdma/hns-abi.h | 16 ++++++++ 6 files changed, 85 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 1d062c522d69..bc015901a7d3 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -845,7 +845,8 @@ struct hns_roce_caps { u16 default_aeq_period; u16 default_aeq_arm_st; u16 default_ceq_arm_st; - enum hns_roce_cong_type cong_type; + u8 cong_cap; + enum hns_roce_cong_type default_cong_type; }; enum hns_roce_device_state { diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 42e28586cefa..38e426f4afb5 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -2209,11 +2209,12 @@ static int hns_roce_query_caps(struct hns_roce_dev *hr_dev) caps->max_wqes = 1 << le16_to_cpu(resp_c->sq_depth); caps->num_srqs = 1 << hr_reg_read(resp_d, PF_CAPS_D_NUM_SRQS); - caps->cong_type = hr_reg_read(resp_d, PF_CAPS_D_CONG_TYPE); + caps->cong_cap = hr_reg_read(resp_d, PF_CAPS_D_CONG_CAP); caps->max_srq_wrs = 1 << le16_to_cpu(resp_d->srq_depth); caps->ceqe_depth = 1 << hr_reg_read(resp_d, PF_CAPS_D_CEQ_DEPTH); caps->num_comp_vectors = hr_reg_read(resp_d, PF_CAPS_D_NUM_CEQS); caps->aeqe_depth = 1 << hr_reg_read(resp_d, PF_CAPS_D_AEQ_DEPTH); + caps->default_cong_type = hr_reg_read(resp_d, PF_CAPS_D_DEFAULT_ALG); caps->reserved_pds = hr_reg_read(resp_d, PF_CAPS_D_RSV_PDS); caps->num_uars = 1 << hr_reg_read(resp_d, PF_CAPS_D_NUM_UARS); caps->reserved_qps = hr_reg_read(resp_d, PF_CAPS_D_RSV_QPS); @@ -4737,14 +4738,8 @@ enum { static int check_cong_type(struct ib_qp *ibqp, struct hns_roce_congestion_algorithm *cong_alg) { - struct hns_roce_dev *hr_dev = to_hr_dev(ibqp->device); struct hns_roce_qp *hr_qp = to_hr_qp(ibqp); - if (ibqp->qp_type == IB_QPT_UD || ibqp->qp_type == IB_QPT_GSI) - hr_qp->cong_type = CONG_TYPE_DCQCN; - else - hr_qp->cong_type = hr_dev->caps.cong_type; - /* different congestion types match different configurations */ switch (hr_qp->cong_type) { case CONG_TYPE_DCQCN: @@ -4772,9 +4767,6 @@ static int check_cong_type(struct ib_qp *ibqp, cong_alg->wnd_mode_sel = WND_LIMIT; break; default: - ibdev_warn(&hr_dev->ib_dev, - "invalid type(%u) for congestion selection.\n", - hr_qp->cong_type); hr_qp->cong_type = CONG_TYPE_DCQCN; cong_alg->alg_sel = CONG_DCQCN; cong_alg->alg_sub_sel = UNSUPPORT_CONG_LEVEL; diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index cd97cbee682a..359a74672ba1 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -1214,12 +1214,13 @@ struct hns_roce_query_pf_caps_d { #define PF_CAPS_D_RQWQE_HOP_NUM PF_CAPS_D_FIELD_LOC(21, 20) #define PF_CAPS_D_EX_SGE_HOP_NUM PF_CAPS_D_FIELD_LOC(23, 22) #define PF_CAPS_D_SQWQE_HOP_NUM PF_CAPS_D_FIELD_LOC(25, 24) -#define PF_CAPS_D_CONG_TYPE PF_CAPS_D_FIELD_LOC(29, 26) +#define PF_CAPS_D_CONG_CAP PF_CAPS_D_FIELD_LOC(29, 26) #define PF_CAPS_D_CEQ_DEPTH PF_CAPS_D_FIELD_LOC(85, 64) #define PF_CAPS_D_NUM_CEQS PF_CAPS_D_FIELD_LOC(95, 86) #define PF_CAPS_D_AEQ_DEPTH PF_CAPS_D_FIELD_LOC(117, 96) #define PF_CAPS_D_AEQ_ARM_ST PF_CAPS_D_FIELD_LOC(119, 118) #define PF_CAPS_D_CEQ_ARM_ST PF_CAPS_D_FIELD_LOC(121, 120) +#define PF_CAPS_D_DEFAULT_ALG PF_CAPS_D_FIELD_LOC(127, 122) #define PF_CAPS_D_RSV_PDS PF_CAPS_D_FIELD_LOC(147, 128) #define PF_CAPS_D_NUM_UARS PF_CAPS_D_FIELD_LOC(155, 148) #define PF_CAPS_D_RSV_QPS PF_CAPS_D_FIELD_LOC(179, 160) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index b55fe6911f9f..1dc60c2b2b7a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -394,6 +394,9 @@ static int hns_roce_alloc_ucontext(struct ib_ucontext *uctx, resp.config |= HNS_ROCE_RSP_CQE_INLINE_FLAGS; } + if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09) + resp.congest_type = hr_dev->caps.cong_cap; + ret = hns_roce_uar_alloc(hr_dev, &context->uar); if (ret) goto error_out; diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index 31b147210688..f35a66325d9a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -1004,6 +1004,60 @@ static void free_kernel_wrid(struct hns_roce_qp *hr_qp) kfree(hr_qp->sq.wrid); } +static void default_congest_type(struct hns_roce_dev *hr_dev, + struct hns_roce_qp *hr_qp) +{ + if (hr_qp->ibqp.qp_type == IB_QPT_UD || + hr_qp->ibqp.qp_type == IB_QPT_GSI) + hr_qp->cong_type = CONG_TYPE_DCQCN; + else + hr_qp->cong_type = hr_dev->caps.default_cong_type; +} + +static int set_congest_type(struct hns_roce_qp *hr_qp, + struct hns_roce_ib_create_qp *ucmd) +{ + struct hns_roce_dev *hr_dev = to_hr_dev(hr_qp->ibqp.device); + + switch (ucmd->cong_type_flags) { + case HNS_ROCE_CREATE_QP_FLAGS_DCQCN: + hr_qp->cong_type = CONG_TYPE_DCQCN; + break; + case HNS_ROCE_CREATE_QP_FLAGS_LDCP: + hr_qp->cong_type = CONG_TYPE_LDCP; + break; + case HNS_ROCE_CREATE_QP_FLAGS_HC3: + hr_qp->cong_type = CONG_TYPE_HC3; + break; + case HNS_ROCE_CREATE_QP_FLAGS_DIP: + hr_qp->cong_type = CONG_TYPE_DIP; + break; + default: + return -EINVAL; + } + + if (!test_bit(hr_qp->cong_type, (unsigned long *)&hr_dev->caps.cong_cap)) + return -EOPNOTSUPP; + + if (hr_qp->ibqp.qp_type == IB_QPT_UD && + hr_qp->cong_type != CONG_TYPE_DCQCN) + return -EOPNOTSUPP; + + return 0; +} + +static int set_congest_param(struct hns_roce_dev *hr_dev, + struct hns_roce_qp *hr_qp, + struct hns_roce_ib_create_qp *ucmd) +{ + if (ucmd->comp_mask & HNS_ROCE_CREATE_QP_MASK_CONGEST_TYPE) + return set_congest_type(hr_qp, ucmd); + + default_congest_type(hr_dev, hr_qp); + + return 0; +} + static int set_qp_param(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, struct ib_qp_init_attr *init_attr, struct ib_udata *udata, @@ -1043,6 +1097,10 @@ static int set_qp_param(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, ibdev_err(ibdev, "failed to set user SQ size, ret = %d.\n", ret); + + ret = set_congest_param(hr_dev, hr_qp, ucmd); + if (ret) + return ret; } else { if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09) hr_qp->config = HNS_ROCE_EXSGE_FLAGS; @@ -1051,6 +1109,8 @@ static int set_qp_param(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, ibdev_err(ibdev, "failed to set kernel SQ size, ret = %d.\n", ret); + + default_congest_type(hr_dev, hr_qp); } return ret; diff --git a/include/uapi/rdma/hns-abi.h b/include/uapi/rdma/hns-abi.h index c996e151081e..158670da2b2a 100644 --- a/include/uapi/rdma/hns-abi.h +++ b/include/uapi/rdma/hns-abi.h @@ -73,6 +73,17 @@ struct hns_roce_ib_create_srq_resp { __u32 cap_flags; /* Use enum hns_roce_srq_cap_flags */ }; +enum hns_roce_congest_type_flags { + HNS_ROCE_CREATE_QP_FLAGS_DCQCN, + HNS_ROCE_CREATE_QP_FLAGS_LDCP, + HNS_ROCE_CREATE_QP_FLAGS_HC3, + HNS_ROCE_CREATE_QP_FLAGS_DIP, +}; + +enum hns_roce_create_qp_comp_mask { + HNS_ROCE_CREATE_QP_MASK_CONGEST_TYPE = 1 << 0, +}; + struct hns_roce_ib_create_qp { __aligned_u64 buf_addr; __aligned_u64 db_addr; @@ -81,6 +92,9 @@ struct hns_roce_ib_create_qp { __u8 sq_no_prefetch; __u8 reserved[5]; __aligned_u64 sdb_addr; + __aligned_u64 comp_mask; /* Use enum hns_roce_create_qp_comp_mask */ + __aligned_u64 create_flags; + __aligned_u64 cong_type_flags; }; enum hns_roce_qp_cap_flags { @@ -114,6 +128,8 @@ struct hns_roce_ib_alloc_ucontext_resp { __u32 reserved; __u32 config; __u32 max_inline_data; + __u8 congest_type; + __u8 reserved0[7]; }; struct hns_roce_ib_alloc_ucontext { -- cgit v1.2.3 From 155f04366e3cad7cc7471f8736aa05ec0300cddd Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 1 Mar 2024 12:37:45 -0600 Subject: RDMA/uverbs: Avoid -Wflex-array-member-not-at-end warnings -Wflex-array-member-not-at-end is coming in GCC-14, and we are getting ready to enable it globally. There are currently a couple of objects (`alloc_head` and `bundle`) in `struct bundle_priv` that contain a couple of flexible structures: struct bundle_priv { /* Must be first */ struct bundle_alloc_head alloc_head; ... /* * Must be last. bundle ends in a flex array which overlaps * internal_buffer. */ struct uverbs_attr_bundle bundle; u64 internal_buffer[32]; }; So, in order to avoid ending up with a couple of flexible-array members in the middle of a struct, we use the `struct_group_tagged()` helper to separate the flexible array from the rest of the members in the flexible structures: struct uverbs_attr_bundle { struct_group_tagged(uverbs_attr_bundle_hdr, hdr, ... the rest of the members ); struct uverbs_attr attrs[]; }; With the change described above, we now declare objects of the type of the tagged struct without embedding flexible arrays in the middle of another struct: struct bundle_priv { /* Must be first */ struct bundle_alloc_head_hdr alloc_head; ... struct uverbs_attr_bundle_hdr bundle; u64 internal_buffer[32]; }; We also use `container_of()` whenever we need to retrieve a pointer to the flexible structures. Notice that the `bundle_size` computed in `uapi_compute_bundle_size()` remains the same. So, with these changes, fix the following warnings: drivers/infiniband/core/uverbs_ioctl.c:45:34: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] 45 | struct bundle_alloc_head alloc_head; | ^~~~~~~~~~ drivers/infiniband/core/uverbs_ioctl.c:67:35: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] 67 | struct uverbs_attr_bundle bundle; | ^~~~~~ Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/ZeIgeZ5Sb0IZTOyt@neat Reviewed-by: Kees Cook Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/uverbs_ioctl.c | 78 ++++++++++++++++++++-------------- include/rdma/uverbs_ioctl.h | 14 +++--- 2 files changed, 53 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c index d9799706c58e..f80da6a67e24 100644 --- a/drivers/infiniband/core/uverbs_ioctl.c +++ b/drivers/infiniband/core/uverbs_ioctl.c @@ -36,13 +36,15 @@ #include "uverbs.h" struct bundle_alloc_head { - struct bundle_alloc_head *next; + struct_group_tagged(bundle_alloc_head_hdr, hdr, + struct bundle_alloc_head *next; + ); u8 data[]; }; struct bundle_priv { /* Must be first */ - struct bundle_alloc_head alloc_head; + struct bundle_alloc_head_hdr alloc_head; struct bundle_alloc_head *allocated_mem; size_t internal_avail; size_t internal_used; @@ -64,7 +66,7 @@ struct bundle_priv { * Must be last. bundle ends in a flex array which overlaps * internal_buffer. */ - struct uverbs_attr_bundle bundle; + struct uverbs_attr_bundle_hdr bundle; u64 internal_buffer[32]; }; @@ -77,9 +79,10 @@ void uapi_compute_bundle_size(struct uverbs_api_ioctl_method *method_elm, unsigned int num_attrs) { struct bundle_priv *pbundle; + struct uverbs_attr_bundle *bundle; size_t bundle_size = offsetof(struct bundle_priv, internal_buffer) + - sizeof(*pbundle->bundle.attrs) * method_elm->key_bitmap_len + + sizeof(*bundle->attrs) * method_elm->key_bitmap_len + sizeof(*pbundle->uattrs) * num_attrs; method_elm->use_stack = bundle_size <= sizeof(*pbundle); @@ -107,7 +110,7 @@ __malloc void *_uverbs_alloc(struct uverbs_attr_bundle *bundle, size_t size, gfp_t flags) { struct bundle_priv *pbundle = - container_of(bundle, struct bundle_priv, bundle); + container_of(&bundle->hdr, struct bundle_priv, bundle); size_t new_used; void *res; @@ -149,7 +152,7 @@ static int uverbs_set_output(const struct uverbs_attr_bundle *bundle, const struct uverbs_attr *attr) { struct bundle_priv *pbundle = - container_of(bundle, struct bundle_priv, bundle); + container_of(&bundle->hdr, struct bundle_priv, bundle); u16 flags; flags = pbundle->uattrs[attr->ptr_attr.uattr_idx].flags | @@ -166,6 +169,8 @@ static int uverbs_process_idrs_array(struct bundle_priv *pbundle, struct ib_uverbs_attr *uattr, u32 attr_bkey) { + struct uverbs_attr_bundle *bundle = + container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr); const struct uverbs_attr_spec *spec = &attr_uapi->spec; size_t array_len; u32 *idr_vals; @@ -184,7 +189,7 @@ static int uverbs_process_idrs_array(struct bundle_priv *pbundle, return -EINVAL; attr->uobjects = - uverbs_alloc(&pbundle->bundle, + uverbs_alloc(bundle, array_size(array_len, sizeof(*attr->uobjects))); if (IS_ERR(attr->uobjects)) return PTR_ERR(attr->uobjects); @@ -209,7 +214,7 @@ static int uverbs_process_idrs_array(struct bundle_priv *pbundle, for (i = 0; i != array_len; i++) { attr->uobjects[i] = uverbs_get_uobject_from_file( spec->u2.objs_arr.obj_type, spec->u2.objs_arr.access, - idr_vals[i], &pbundle->bundle); + idr_vals[i], bundle); if (IS_ERR(attr->uobjects[i])) { ret = PTR_ERR(attr->uobjects[i]); break; @@ -240,7 +245,9 @@ static int uverbs_process_attr(struct bundle_priv *pbundle, struct ib_uverbs_attr *uattr, u32 attr_bkey) { const struct uverbs_attr_spec *spec = &attr_uapi->spec; - struct uverbs_attr *e = &pbundle->bundle.attrs[attr_bkey]; + struct uverbs_attr_bundle *bundle = + container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr); + struct uverbs_attr *e = &bundle->attrs[attr_bkey]; const struct uverbs_attr_spec *val_spec = spec; struct uverbs_obj_attr *o_attr; @@ -288,7 +295,7 @@ static int uverbs_process_attr(struct bundle_priv *pbundle, if (val_spec->alloc_and_copy && !uverbs_attr_ptr_is_inline(e)) { void *p; - p = uverbs_alloc(&pbundle->bundle, uattr->len); + p = uverbs_alloc(bundle, uattr->len); if (IS_ERR(p)) return PTR_ERR(p); @@ -321,7 +328,7 @@ static int uverbs_process_attr(struct bundle_priv *pbundle, */ o_attr->uobject = uverbs_get_uobject_from_file( spec->u.obj.obj_type, spec->u.obj.access, - uattr->data_s64, &pbundle->bundle); + uattr->data_s64, bundle); if (IS_ERR(o_attr->uobject)) return PTR_ERR(o_attr->uobject); __set_bit(attr_bkey, pbundle->uobj_finalize); @@ -422,6 +429,8 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle, unsigned int num_attrs) { int (*handler)(struct uverbs_attr_bundle *attrs); + struct uverbs_attr_bundle *bundle = + container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr); size_t uattrs_size = array_size(sizeof(*pbundle->uattrs), num_attrs); unsigned int destroy_bkey = pbundle->method_elm->destroy_bkey; unsigned int i; @@ -434,7 +443,7 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle, if (!handler) return -EIO; - pbundle->uattrs = uverbs_alloc(&pbundle->bundle, uattrs_size); + pbundle->uattrs = uverbs_alloc(bundle, uattrs_size); if (IS_ERR(pbundle->uattrs)) return PTR_ERR(pbundle->uattrs); if (copy_from_user(pbundle->uattrs, pbundle->user_attrs, uattrs_size)) @@ -453,25 +462,23 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle, return -EINVAL; if (pbundle->method_elm->has_udata) - uverbs_fill_udata(&pbundle->bundle, - &pbundle->bundle.driver_udata, + uverbs_fill_udata(bundle, &pbundle->bundle.driver_udata, UVERBS_ATTR_UHW_IN, UVERBS_ATTR_UHW_OUT); else pbundle->bundle.driver_udata = (struct ib_udata){}; if (destroy_bkey != UVERBS_API_ATTR_BKEY_LEN) { - struct uverbs_obj_attr *destroy_attr = - &pbundle->bundle.attrs[destroy_bkey].obj_attr; + struct uverbs_obj_attr *destroy_attr = &bundle->attrs[destroy_bkey].obj_attr; - ret = uobj_destroy(destroy_attr->uobject, &pbundle->bundle); + ret = uobj_destroy(destroy_attr->uobject, bundle); if (ret) return ret; __clear_bit(destroy_bkey, pbundle->uobj_finalize); - ret = handler(&pbundle->bundle); + ret = handler(bundle); uobj_put_destroy(destroy_attr->uobject); } else { - ret = handler(&pbundle->bundle); + ret = handler(bundle); } /* @@ -481,10 +488,10 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle, */ if (!ret && pbundle->method_elm->has_udata) { const struct uverbs_attr *attr = - uverbs_attr_get(&pbundle->bundle, UVERBS_ATTR_UHW_OUT); + uverbs_attr_get(bundle, UVERBS_ATTR_UHW_OUT); if (!IS_ERR(attr)) - ret = uverbs_set_output(&pbundle->bundle, attr); + ret = uverbs_set_output(bundle, attr); } /* @@ -501,6 +508,8 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle, static void bundle_destroy(struct bundle_priv *pbundle, bool commit) { unsigned int key_bitmap_len = pbundle->method_elm->key_bitmap_len; + struct uverbs_attr_bundle *bundle = + container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr); struct bundle_alloc_head *memblock; unsigned int i; @@ -508,20 +517,19 @@ static void bundle_destroy(struct bundle_priv *pbundle, bool commit) i = -1; while ((i = find_next_bit(pbundle->uobj_finalize, key_bitmap_len, i + 1)) < key_bitmap_len) { - struct uverbs_attr *attr = &pbundle->bundle.attrs[i]; + struct uverbs_attr *attr = &bundle->attrs[i]; uverbs_finalize_object( attr->obj_attr.uobject, attr->obj_attr.attr_elm->spec.u.obj.access, test_bit(i, pbundle->uobj_hw_obj_valid), - commit, - &pbundle->bundle); + commit, bundle); } i = -1; while ((i = find_next_bit(pbundle->spec_finalize, key_bitmap_len, i + 1)) < key_bitmap_len) { - struct uverbs_attr *attr = &pbundle->bundle.attrs[i]; + struct uverbs_attr *attr = &bundle->attrs[i]; const struct uverbs_api_attr *attr_uapi; void __rcu **slot; @@ -535,7 +543,7 @@ static void bundle_destroy(struct bundle_priv *pbundle, bool commit) if (attr_uapi->spec.type == UVERBS_ATTR_TYPE_IDRS_ARRAY) { uverbs_free_idrs_array(attr_uapi, &attr->objs_arr_attr, - commit, &pbundle->bundle); + commit, bundle); } } @@ -578,7 +586,8 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile, method_elm->bundle_size - offsetof(struct bundle_priv, internal_buffer); pbundle->alloc_head.next = NULL; - pbundle->allocated_mem = &pbundle->alloc_head; + pbundle->allocated_mem = container_of(&pbundle->alloc_head, + struct bundle_alloc_head, hdr); } else { pbundle = &onstack; pbundle->internal_avail = sizeof(pbundle->internal_buffer); @@ -596,8 +605,9 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile, pbundle->user_attrs = user_attrs; pbundle->internal_used = ALIGN(pbundle->method_elm->key_bitmap_len * - sizeof(*pbundle->bundle.attrs), - sizeof(*pbundle->internal_buffer)); + sizeof(*container_of(&pbundle->bundle, + struct uverbs_attr_bundle, hdr)->attrs), + sizeof(*pbundle->internal_buffer)); memset(pbundle->bundle.attr_present, 0, sizeof(pbundle->bundle.attr_present)); memset(pbundle->uobj_finalize, 0, sizeof(pbundle->uobj_finalize)); @@ -700,11 +710,13 @@ void uverbs_fill_udata(struct uverbs_attr_bundle *bundle, unsigned int attr_out) { struct bundle_priv *pbundle = - container_of(bundle, struct bundle_priv, bundle); + container_of(&bundle->hdr, struct bundle_priv, bundle); + struct uverbs_attr_bundle *bundle_aux = + container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr); const struct uverbs_attr *in = - uverbs_attr_get(&pbundle->bundle, attr_in); + uverbs_attr_get(bundle_aux, attr_in); const struct uverbs_attr *out = - uverbs_attr_get(&pbundle->bundle, attr_out); + uverbs_attr_get(bundle_aux, attr_out); if (!IS_ERR(in)) { udata->inlen = in->ptr_attr.len; @@ -829,7 +841,7 @@ void uverbs_finalize_uobj_create(const struct uverbs_attr_bundle *bundle, u16 idx) { struct bundle_priv *pbundle = - container_of(bundle, struct bundle_priv, bundle); + container_of(&bundle->hdr, struct bundle_priv, bundle); __set_bit(uapi_bkey_attr(uapi_key_attr(idx)), pbundle->uobj_hw_obj_valid); diff --git a/include/rdma/uverbs_ioctl.h b/include/rdma/uverbs_ioctl.h index 06287de69cd2..e6c0de227fad 100644 --- a/include/rdma/uverbs_ioctl.h +++ b/include/rdma/uverbs_ioctl.h @@ -629,12 +629,14 @@ struct uverbs_attr { }; struct uverbs_attr_bundle { - struct ib_udata driver_udata; - struct ib_udata ucore; - struct ib_uverbs_file *ufile; - struct ib_ucontext *context; - struct ib_uobject *uobject; - DECLARE_BITMAP(attr_present, UVERBS_API_ATTR_BKEY_LEN); + struct_group_tagged(uverbs_attr_bundle_hdr, hdr, + struct ib_udata driver_udata; + struct ib_udata ucore; + struct ib_uverbs_file *ufile; + struct ib_ucontext *context; + struct ib_uobject *uobject; + DECLARE_BITMAP(attr_present, UVERBS_API_ATTR_BKEY_LEN); + ); struct uverbs_attr attrs[]; }; -- cgit v1.2.3 From 0e3f7d120086c8b9d6e1ae0dd4917fc529daa1ca Mon Sep 17 00:00:00 2001 From: Nuno Das Neves Date: Tue, 20 Feb 2024 06:55:33 -0800 Subject: hyperv-tlfs: Change prefix of generic HV_REGISTER_* MSRs to HV_MSR_* The HV_REGISTER_ are used as arguments to hv_set/get_register(), which delegate to arch-specific mechanisms for getting/setting synthetic Hyper-V MSRs. On arm64, HV_REGISTER_ defines are synthetic VP registers accessed via the get/set vp registers hypercalls. The naming matches the TLFS document, although these register names are not specific to arm64. However, on x86 the prefix HV_REGISTER_ indicates Hyper-V MSRs accessed via rdmsrl()/wrmsrl(). This is not consistent with the TLFS doc, where HV_REGISTER_ is *only* used for used for VP register names used by the get/set register hypercalls. To fix this inconsistency and prevent future confusion, change the arch-generic aliases used by callers of hv_set/get_register() to have the prefix HV_MSR_ instead of HV_REGISTER_. Use the prefix HV_X64_MSR_ for the x86-only Hyper-V MSRs. On x86, the generic HV_MSR_'s point to the corresponding HV_X64_MSR_. Move the arm64 HV_REGISTER_* defines to the asm-generic hyperv-tlfs.h, since these are not specific to arm64. On arm64, the generic HV_MSR_'s point to the corresponding HV_REGISTER_. While at it, rename hv_get/set_registers() and related functions to hv_get/set_msr(), hv_get/set_nested_msr(), etc. These are only used for Hyper-V MSRs and this naming makes that clear. Signed-off-by: Nuno Das Neves Reviewed-by: Wei Liu Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1708440933-27125-1-git-send-email-nunodasneves@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <1708440933-27125-1-git-send-email-nunodasneves@linux.microsoft.com> --- arch/arm64/include/asm/hyperv-tlfs.h | 45 +++++------ arch/arm64/include/asm/mshyperv.h | 4 +- arch/x86/hyperv/hv_init.c | 8 +- arch/x86/include/asm/hyperv-tlfs.h | 145 ++++++++++++++++++----------------- arch/x86/include/asm/mshyperv.h | 30 ++++---- arch/x86/kernel/cpu/mshyperv.c | 56 +++++++------- drivers/clocksource/hyperv_timer.c | 26 +++---- drivers/hv/hv.c | 36 ++++----- drivers/hv/hv_common.c | 22 +++--- include/asm-generic/hyperv-tlfs.h | 32 +++++++- include/asm-generic/mshyperv.h | 2 +- 11 files changed, 216 insertions(+), 190 deletions(-) (limited to 'include') diff --git a/arch/arm64/include/asm/hyperv-tlfs.h b/arch/arm64/include/asm/hyperv-tlfs.h index bc6c7ac934a1..54846d1d29c3 100644 --- a/arch/arm64/include/asm/hyperv-tlfs.h +++ b/arch/arm64/include/asm/hyperv-tlfs.h @@ -21,14 +21,6 @@ * byte ordering of Linux running on ARM64, so no special handling is required. */ -/* - * These Hyper-V registers provide information equivalent to the CPUID - * instruction on x86/x64. - */ -#define HV_REGISTER_HYPERVISOR_VERSION 0x00000100 /*CPUID 0x40000002 */ -#define HV_REGISTER_FEATURES 0x00000200 /*CPUID 0x40000003 */ -#define HV_REGISTER_ENLIGHTENMENTS 0x00000201 /*CPUID 0x40000004 */ - /* * Group C Features. See the asm-generic version of hyperv-tlfs.h * for a description of Feature Groups. @@ -41,28 +33,29 @@ #define HV_STIMER_DIRECT_MODE_AVAILABLE BIT(13) /* - * Synthetic register definitions equivalent to MSRs on x86/x64 + * To support arch-generic code calling hv_set/get_register: + * - On x86, HV_MSR_ indicates an MSR accessed via rdmsrl/wrmsrl + * - On ARM, HV_MSR_ indicates a VP register accessed via hypercall */ -#define HV_REGISTER_CRASH_P0 0x00000210 -#define HV_REGISTER_CRASH_P1 0x00000211 -#define HV_REGISTER_CRASH_P2 0x00000212 -#define HV_REGISTER_CRASH_P3 0x00000213 -#define HV_REGISTER_CRASH_P4 0x00000214 -#define HV_REGISTER_CRASH_CTL 0x00000215 +#define HV_MSR_CRASH_P0 (HV_REGISTER_CRASH_P0) +#define HV_MSR_CRASH_P1 (HV_REGISTER_CRASH_P1) +#define HV_MSR_CRASH_P2 (HV_REGISTER_CRASH_P2) +#define HV_MSR_CRASH_P3 (HV_REGISTER_CRASH_P3) +#define HV_MSR_CRASH_P4 (HV_REGISTER_CRASH_P4) +#define HV_MSR_CRASH_CTL (HV_REGISTER_CRASH_CTL) -#define HV_REGISTER_GUEST_OSID 0x00090002 -#define HV_REGISTER_VP_INDEX 0x00090003 -#define HV_REGISTER_TIME_REF_COUNT 0x00090004 -#define HV_REGISTER_REFERENCE_TSC 0x00090017 +#define HV_MSR_VP_INDEX (HV_REGISTER_VP_INDEX) +#define HV_MSR_TIME_REF_COUNT (HV_REGISTER_TIME_REF_COUNT) +#define HV_MSR_REFERENCE_TSC (HV_REGISTER_REFERENCE_TSC) -#define HV_REGISTER_SINT0 0x000A0000 -#define HV_REGISTER_SCONTROL 0x000A0010 -#define HV_REGISTER_SIEFP 0x000A0012 -#define HV_REGISTER_SIMP 0x000A0013 -#define HV_REGISTER_EOM 0x000A0014 +#define HV_MSR_SINT0 (HV_REGISTER_SINT0) +#define HV_MSR_SCONTROL (HV_REGISTER_SCONTROL) +#define HV_MSR_SIEFP (HV_REGISTER_SIEFP) +#define HV_MSR_SIMP (HV_REGISTER_SIMP) +#define HV_MSR_EOM (HV_REGISTER_EOM) -#define HV_REGISTER_STIMER0_CONFIG 0x000B0000 -#define HV_REGISTER_STIMER0_COUNT 0x000B0001 +#define HV_MSR_STIMER0_CONFIG (HV_REGISTER_STIMER0_CONFIG) +#define HV_MSR_STIMER0_COUNT (HV_REGISTER_STIMER0_COUNT) union hv_msi_entry { u64 as_uint64[2]; diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h index 20070a847304..a975e1a689dd 100644 --- a/arch/arm64/include/asm/mshyperv.h +++ b/arch/arm64/include/asm/mshyperv.h @@ -31,12 +31,12 @@ void hv_set_vpreg(u32 reg, u64 value); u64 hv_get_vpreg(u32 reg); void hv_get_vpreg_128(u32 reg, struct hv_get_vp_registers_output *result); -static inline void hv_set_register(unsigned int reg, u64 value) +static inline void hv_set_msr(unsigned int reg, u64 value) { hv_set_vpreg(reg, value); } -static inline u64 hv_get_register(unsigned int reg) +static inline u64 hv_get_msr(unsigned int reg) { return hv_get_vpreg(reg); } diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 8f3a4d16bb79..17a71e92a343 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -667,14 +667,14 @@ void hyperv_cleanup(void) hv_hypercall_pg = NULL; /* Reset the hypercall page */ - hypercall_msr.as_uint64 = hv_get_register(HV_X64_MSR_HYPERCALL); + hypercall_msr.as_uint64 = hv_get_msr(HV_X64_MSR_HYPERCALL); hypercall_msr.enable = 0; - hv_set_register(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + hv_set_msr(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); /* Reset the TSC page */ - tsc_msr.as_uint64 = hv_get_register(HV_X64_MSR_REFERENCE_TSC); + tsc_msr.as_uint64 = hv_get_msr(HV_X64_MSR_REFERENCE_TSC); tsc_msr.enable = 0; - hv_set_register(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); + hv_set_msr(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); } void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die) diff --git a/arch/x86/include/asm/hyperv-tlfs.h b/arch/x86/include/asm/hyperv-tlfs.h index 2ff26f53cd62..3787d26810c1 100644 --- a/arch/x86/include/asm/hyperv-tlfs.h +++ b/arch/x86/include/asm/hyperv-tlfs.h @@ -182,7 +182,7 @@ enum hv_isolation_type { #define HV_X64_MSR_HYPERCALL 0x40000001 /* MSR used to provide vcpu index */ -#define HV_REGISTER_VP_INDEX 0x40000002 +#define HV_X64_MSR_VP_INDEX 0x40000002 /* MSR used to reset the guest OS. */ #define HV_X64_MSR_RESET 0x40000003 @@ -191,10 +191,10 @@ enum hv_isolation_type { #define HV_X64_MSR_VP_RUNTIME 0x40000010 /* MSR used to read the per-partition time reference counter */ -#define HV_REGISTER_TIME_REF_COUNT 0x40000020 +#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 /* A partition's reference time stamp counter (TSC) page */ -#define HV_REGISTER_REFERENCE_TSC 0x40000021 +#define HV_X64_MSR_REFERENCE_TSC 0x40000021 /* MSR used to retrieve the TSC frequency */ #define HV_X64_MSR_TSC_FREQUENCY 0x40000022 @@ -209,61 +209,61 @@ enum hv_isolation_type { #define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073 /* Define synthetic interrupt controller model specific registers. */ -#define HV_REGISTER_SCONTROL 0x40000080 -#define HV_REGISTER_SVERSION 0x40000081 -#define HV_REGISTER_SIEFP 0x40000082 -#define HV_REGISTER_SIMP 0x40000083 -#define HV_REGISTER_EOM 0x40000084 -#define HV_REGISTER_SINT0 0x40000090 -#define HV_REGISTER_SINT1 0x40000091 -#define HV_REGISTER_SINT2 0x40000092 -#define HV_REGISTER_SINT3 0x40000093 -#define HV_REGISTER_SINT4 0x40000094 -#define HV_REGISTER_SINT5 0x40000095 -#define HV_REGISTER_SINT6 0x40000096 -#define HV_REGISTER_SINT7 0x40000097 -#define HV_REGISTER_SINT8 0x40000098 -#define HV_REGISTER_SINT9 0x40000099 -#define HV_REGISTER_SINT10 0x4000009A -#define HV_REGISTER_SINT11 0x4000009B -#define HV_REGISTER_SINT12 0x4000009C -#define HV_REGISTER_SINT13 0x4000009D -#define HV_REGISTER_SINT14 0x4000009E -#define HV_REGISTER_SINT15 0x4000009F +#define HV_X64_MSR_SCONTROL 0x40000080 +#define HV_X64_MSR_SVERSION 0x40000081 +#define HV_X64_MSR_SIEFP 0x40000082 +#define HV_X64_MSR_SIMP 0x40000083 +#define HV_X64_MSR_EOM 0x40000084 +#define HV_X64_MSR_SINT0 0x40000090 +#define HV_X64_MSR_SINT1 0x40000091 +#define HV_X64_MSR_SINT2 0x40000092 +#define HV_X64_MSR_SINT3 0x40000093 +#define HV_X64_MSR_SINT4 0x40000094 +#define HV_X64_MSR_SINT5 0x40000095 +#define HV_X64_MSR_SINT6 0x40000096 +#define HV_X64_MSR_SINT7 0x40000097 +#define HV_X64_MSR_SINT8 0x40000098 +#define HV_X64_MSR_SINT9 0x40000099 +#define HV_X64_MSR_SINT10 0x4000009A +#define HV_X64_MSR_SINT11 0x4000009B +#define HV_X64_MSR_SINT12 0x4000009C +#define HV_X64_MSR_SINT13 0x4000009D +#define HV_X64_MSR_SINT14 0x4000009E +#define HV_X64_MSR_SINT15 0x4000009F /* * Define synthetic interrupt controller model specific registers for * nested hypervisor. */ -#define HV_REGISTER_NESTED_SCONTROL 0x40001080 -#define HV_REGISTER_NESTED_SVERSION 0x40001081 -#define HV_REGISTER_NESTED_SIEFP 0x40001082 -#define HV_REGISTER_NESTED_SIMP 0x40001083 -#define HV_REGISTER_NESTED_EOM 0x40001084 -#define HV_REGISTER_NESTED_SINT0 0x40001090 +#define HV_X64_MSR_NESTED_SCONTROL 0x40001080 +#define HV_X64_MSR_NESTED_SVERSION 0x40001081 +#define HV_X64_MSR_NESTED_SIEFP 0x40001082 +#define HV_X64_MSR_NESTED_SIMP 0x40001083 +#define HV_X64_MSR_NESTED_EOM 0x40001084 +#define HV_X64_MSR_NESTED_SINT0 0x40001090 /* * Synthetic Timer MSRs. Four timers per vcpu. */ -#define HV_REGISTER_STIMER0_CONFIG 0x400000B0 -#define HV_REGISTER_STIMER0_COUNT 0x400000B1 -#define HV_REGISTER_STIMER1_CONFIG 0x400000B2 -#define HV_REGISTER_STIMER1_COUNT 0x400000B3 -#define HV_REGISTER_STIMER2_CONFIG 0x400000B4 -#define HV_REGISTER_STIMER2_COUNT 0x400000B5 -#define HV_REGISTER_STIMER3_CONFIG 0x400000B6 -#define HV_REGISTER_STIMER3_COUNT 0x400000B7 +#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0 +#define HV_X64_MSR_STIMER0_COUNT 0x400000B1 +#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2 +#define HV_X64_MSR_STIMER1_COUNT 0x400000B3 +#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4 +#define HV_X64_MSR_STIMER2_COUNT 0x400000B5 +#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 +#define HV_X64_MSR_STIMER3_COUNT 0x400000B7 /* Hyper-V guest idle MSR */ #define HV_X64_MSR_GUEST_IDLE 0x400000F0 /* Hyper-V guest crash notification MSR's */ -#define HV_REGISTER_CRASH_P0 0x40000100 -#define HV_REGISTER_CRASH_P1 0x40000101 -#define HV_REGISTER_CRASH_P2 0x40000102 -#define HV_REGISTER_CRASH_P3 0x40000103 -#define HV_REGISTER_CRASH_P4 0x40000104 -#define HV_REGISTER_CRASH_CTL 0x40000105 +#define HV_X64_MSR_CRASH_P0 0x40000100 +#define HV_X64_MSR_CRASH_P1 0x40000101 +#define HV_X64_MSR_CRASH_P2 0x40000102 +#define HV_X64_MSR_CRASH_P3 0x40000103 +#define HV_X64_MSR_CRASH_P4 0x40000104 +#define HV_X64_MSR_CRASH_CTL 0x40000105 /* TSC emulation after migration */ #define HV_X64_MSR_REENLIGHTENMENT_CONTROL 0x40000106 @@ -276,31 +276,38 @@ enum hv_isolation_type { /* HV_X64_MSR_TSC_INVARIANT_CONTROL bits */ #define HV_EXPOSE_INVARIANT_TSC BIT_ULL(0) -/* Register name aliases for temporary compatibility */ -#define HV_X64_MSR_STIMER0_COUNT HV_REGISTER_STIMER0_COUNT -#define HV_X64_MSR_STIMER0_CONFIG HV_REGISTER_STIMER0_CONFIG -#define HV_X64_MSR_STIMER1_COUNT HV_REGISTER_STIMER1_COUNT -#define HV_X64_MSR_STIMER1_CONFIG HV_REGISTER_STIMER1_CONFIG -#define HV_X64_MSR_STIMER2_COUNT HV_REGISTER_STIMER2_COUNT -#define HV_X64_MSR_STIMER2_CONFIG HV_REGISTER_STIMER2_CONFIG -#define HV_X64_MSR_STIMER3_COUNT HV_REGISTER_STIMER3_COUNT -#define HV_X64_MSR_STIMER3_CONFIG HV_REGISTER_STIMER3_CONFIG -#define HV_X64_MSR_SCONTROL HV_REGISTER_SCONTROL -#define HV_X64_MSR_SVERSION HV_REGISTER_SVERSION -#define HV_X64_MSR_SIMP HV_REGISTER_SIMP -#define HV_X64_MSR_SIEFP HV_REGISTER_SIEFP -#define HV_X64_MSR_VP_INDEX HV_REGISTER_VP_INDEX -#define HV_X64_MSR_EOM HV_REGISTER_EOM -#define HV_X64_MSR_SINT0 HV_REGISTER_SINT0 -#define HV_X64_MSR_SINT15 HV_REGISTER_SINT15 -#define HV_X64_MSR_CRASH_P0 HV_REGISTER_CRASH_P0 -#define HV_X64_MSR_CRASH_P1 HV_REGISTER_CRASH_P1 -#define HV_X64_MSR_CRASH_P2 HV_REGISTER_CRASH_P2 -#define HV_X64_MSR_CRASH_P3 HV_REGISTER_CRASH_P3 -#define HV_X64_MSR_CRASH_P4 HV_REGISTER_CRASH_P4 -#define HV_X64_MSR_CRASH_CTL HV_REGISTER_CRASH_CTL -#define HV_X64_MSR_TIME_REF_COUNT HV_REGISTER_TIME_REF_COUNT -#define HV_X64_MSR_REFERENCE_TSC HV_REGISTER_REFERENCE_TSC +/* + * To support arch-generic code calling hv_set/get_register: + * - On x86, HV_MSR_ indicates an MSR accessed via rdmsrl/wrmsrl + * - On ARM, HV_MSR_ indicates a VP register accessed via hypercall + */ +#define HV_MSR_CRASH_P0 (HV_X64_MSR_CRASH_P0) +#define HV_MSR_CRASH_P1 (HV_X64_MSR_CRASH_P1) +#define HV_MSR_CRASH_P2 (HV_X64_MSR_CRASH_P2) +#define HV_MSR_CRASH_P3 (HV_X64_MSR_CRASH_P3) +#define HV_MSR_CRASH_P4 (HV_X64_MSR_CRASH_P4) +#define HV_MSR_CRASH_CTL (HV_X64_MSR_CRASH_CTL) + +#define HV_MSR_VP_INDEX (HV_X64_MSR_VP_INDEX) +#define HV_MSR_TIME_REF_COUNT (HV_X64_MSR_TIME_REF_COUNT) +#define HV_MSR_REFERENCE_TSC (HV_X64_MSR_REFERENCE_TSC) + +#define HV_MSR_SINT0 (HV_X64_MSR_SINT0) +#define HV_MSR_SVERSION (HV_X64_MSR_SVERSION) +#define HV_MSR_SCONTROL (HV_X64_MSR_SCONTROL) +#define HV_MSR_SIEFP (HV_X64_MSR_SIEFP) +#define HV_MSR_SIMP (HV_X64_MSR_SIMP) +#define HV_MSR_EOM (HV_X64_MSR_EOM) + +#define HV_MSR_NESTED_SCONTROL (HV_X64_MSR_NESTED_SCONTROL) +#define HV_MSR_NESTED_SVERSION (HV_X64_MSR_NESTED_SVERSION) +#define HV_MSR_NESTED_SIEFP (HV_X64_MSR_NESTED_SIEFP) +#define HV_MSR_NESTED_SIMP (HV_X64_MSR_NESTED_SIMP) +#define HV_MSR_NESTED_EOM (HV_X64_MSR_NESTED_EOM) +#define HV_MSR_NESTED_SINT0 (HV_X64_MSR_NESTED_SINT0) + +#define HV_MSR_STIMER0_CONFIG (HV_X64_MSR_STIMER0_CONFIG) +#define HV_MSR_STIMER0_COUNT (HV_X64_MSR_STIMER0_COUNT) /* * Registers are only accessible via HVCALL_GET_VP_REGISTERS hvcall and diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index ce4ce8720d55..390c4d13956d 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -293,24 +293,24 @@ static inline void hv_ivm_msr_write(u64 msr, u64 value) {} static inline void hv_ivm_msr_read(u64 msr, u64 *value) {} #endif -static inline bool hv_is_synic_reg(unsigned int reg) +static inline bool hv_is_synic_msr(unsigned int reg) { - return (reg >= HV_REGISTER_SCONTROL) && - (reg <= HV_REGISTER_SINT15); + return (reg >= HV_X64_MSR_SCONTROL) && + (reg <= HV_X64_MSR_SINT15); } -static inline bool hv_is_sint_reg(unsigned int reg) +static inline bool hv_is_sint_msr(unsigned int reg) { - return (reg >= HV_REGISTER_SINT0) && - (reg <= HV_REGISTER_SINT15); + return (reg >= HV_X64_MSR_SINT0) && + (reg <= HV_X64_MSR_SINT15); } -u64 hv_get_register(unsigned int reg); -void hv_set_register(unsigned int reg, u64 value); -u64 hv_get_non_nested_register(unsigned int reg); -void hv_set_non_nested_register(unsigned int reg, u64 value); +u64 hv_get_msr(unsigned int reg); +void hv_set_msr(unsigned int reg, u64 value); +u64 hv_get_non_nested_msr(unsigned int reg); +void hv_set_non_nested_msr(unsigned int reg, u64 value); -static __always_inline u64 hv_raw_get_register(unsigned int reg) +static __always_inline u64 hv_raw_get_msr(unsigned int reg) { return __rdmsr(reg); } @@ -331,10 +331,10 @@ static inline int hyperv_flush_guest_mapping_range(u64 as, { return -1; } -static inline void hv_set_register(unsigned int reg, u64 value) { } -static inline u64 hv_get_register(unsigned int reg) { return 0; } -static inline void hv_set_non_nested_register(unsigned int reg, u64 value) { } -static inline u64 hv_get_non_nested_register(unsigned int reg) { return 0; } +static inline void hv_set_msr(unsigned int reg, u64 value) { } +static inline u64 hv_get_msr(unsigned int reg) { return 0; } +static inline void hv_set_non_nested_msr(unsigned int reg, u64 value) { } +static inline u64 hv_get_non_nested_msr(unsigned int reg) { return 0; } #endif /* CONFIG_HYPERV */ diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 01fa06dd06b6..d306f6184cee 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -45,70 +45,70 @@ bool hyperv_paravisor_present __ro_after_init; EXPORT_SYMBOL_GPL(hyperv_paravisor_present); #if IS_ENABLED(CONFIG_HYPERV) -static inline unsigned int hv_get_nested_reg(unsigned int reg) +static inline unsigned int hv_get_nested_msr(unsigned int reg) { - if (hv_is_sint_reg(reg)) - return reg - HV_REGISTER_SINT0 + HV_REGISTER_NESTED_SINT0; + if (hv_is_sint_msr(reg)) + return reg - HV_X64_MSR_SINT0 + HV_X64_MSR_NESTED_SINT0; switch (reg) { - case HV_REGISTER_SIMP: - return HV_REGISTER_NESTED_SIMP; - case HV_REGISTER_SIEFP: - return HV_REGISTER_NESTED_SIEFP; - case HV_REGISTER_SVERSION: - return HV_REGISTER_NESTED_SVERSION; - case HV_REGISTER_SCONTROL: - return HV_REGISTER_NESTED_SCONTROL; - case HV_REGISTER_EOM: - return HV_REGISTER_NESTED_EOM; + case HV_X64_MSR_SIMP: + return HV_X64_MSR_NESTED_SIMP; + case HV_X64_MSR_SIEFP: + return HV_X64_MSR_NESTED_SIEFP; + case HV_X64_MSR_SVERSION: + return HV_X64_MSR_NESTED_SVERSION; + case HV_X64_MSR_SCONTROL: + return HV_X64_MSR_NESTED_SCONTROL; + case HV_X64_MSR_EOM: + return HV_X64_MSR_NESTED_EOM; default: return reg; } } -u64 hv_get_non_nested_register(unsigned int reg) +u64 hv_get_non_nested_msr(unsigned int reg) { u64 value; - if (hv_is_synic_reg(reg) && ms_hyperv.paravisor_present) + if (hv_is_synic_msr(reg) && ms_hyperv.paravisor_present) hv_ivm_msr_read(reg, &value); else rdmsrl(reg, value); return value; } -EXPORT_SYMBOL_GPL(hv_get_non_nested_register); +EXPORT_SYMBOL_GPL(hv_get_non_nested_msr); -void hv_set_non_nested_register(unsigned int reg, u64 value) +void hv_set_non_nested_msr(unsigned int reg, u64 value) { - if (hv_is_synic_reg(reg) && ms_hyperv.paravisor_present) { + if (hv_is_synic_msr(reg) && ms_hyperv.paravisor_present) { hv_ivm_msr_write(reg, value); /* Write proxy bit via wrmsl instruction */ - if (hv_is_sint_reg(reg)) + if (hv_is_sint_msr(reg)) wrmsrl(reg, value | 1 << 20); } else { wrmsrl(reg, value); } } -EXPORT_SYMBOL_GPL(hv_set_non_nested_register); +EXPORT_SYMBOL_GPL(hv_set_non_nested_msr); -u64 hv_get_register(unsigned int reg) +u64 hv_get_msr(unsigned int reg) { if (hv_nested) - reg = hv_get_nested_reg(reg); + reg = hv_get_nested_msr(reg); - return hv_get_non_nested_register(reg); + return hv_get_non_nested_msr(reg); } -EXPORT_SYMBOL_GPL(hv_get_register); +EXPORT_SYMBOL_GPL(hv_get_msr); -void hv_set_register(unsigned int reg, u64 value) +void hv_set_msr(unsigned int reg, u64 value) { if (hv_nested) - reg = hv_get_nested_reg(reg); + reg = hv_get_nested_msr(reg); - hv_set_non_nested_register(reg, value); + hv_set_non_nested_msr(reg, value); } -EXPORT_SYMBOL_GPL(hv_set_register); +EXPORT_SYMBOL_GPL(hv_set_msr); static void (*vmbus_handler)(void); static void (*hv_stimer0_handler)(void); diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 8ff7cd4e20bb..b2a080647e41 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -81,14 +81,14 @@ static int hv_ce_set_next_event(unsigned long delta, current_tick = hv_read_reference_counter(); current_tick += delta; - hv_set_register(HV_REGISTER_STIMER0_COUNT, current_tick); + hv_set_msr(HV_MSR_STIMER0_COUNT, current_tick); return 0; } static int hv_ce_shutdown(struct clock_event_device *evt) { - hv_set_register(HV_REGISTER_STIMER0_COUNT, 0); - hv_set_register(HV_REGISTER_STIMER0_CONFIG, 0); + hv_set_msr(HV_MSR_STIMER0_COUNT, 0); + hv_set_msr(HV_MSR_STIMER0_CONFIG, 0); if (direct_mode_enabled && stimer0_irq >= 0) disable_percpu_irq(stimer0_irq); @@ -119,7 +119,7 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt) timer_cfg.direct_mode = 0; timer_cfg.sintx = stimer0_message_sint; } - hv_set_register(HV_REGISTER_STIMER0_CONFIG, timer_cfg.as_uint64); + hv_set_msr(HV_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); return 0; } @@ -372,11 +372,11 @@ static __always_inline u64 read_hv_clock_msr(void) * is set to 0 when the partition is created and is incremented in 100 * nanosecond units. * - * Use hv_raw_get_register() because this function is used from - * noinstr. Notable; while HV_REGISTER_TIME_REF_COUNT is a synthetic + * Use hv_raw_get_msr() because this function is used from + * noinstr. Notable; while HV_MSR_TIME_REF_COUNT is a synthetic * register it doesn't need the GHCB path. */ - return hv_raw_get_register(HV_REGISTER_TIME_REF_COUNT); + return hv_raw_get_msr(HV_MSR_TIME_REF_COUNT); } /* @@ -439,9 +439,9 @@ static void suspend_hv_clock_tsc(struct clocksource *arg) union hv_reference_tsc_msr tsc_msr; /* Disable the TSC page */ - tsc_msr.as_uint64 = hv_get_register(HV_REGISTER_REFERENCE_TSC); + tsc_msr.as_uint64 = hv_get_msr(HV_MSR_REFERENCE_TSC); tsc_msr.enable = 0; - hv_set_register(HV_REGISTER_REFERENCE_TSC, tsc_msr.as_uint64); + hv_set_msr(HV_MSR_REFERENCE_TSC, tsc_msr.as_uint64); } @@ -450,10 +450,10 @@ static void resume_hv_clock_tsc(struct clocksource *arg) union hv_reference_tsc_msr tsc_msr; /* Re-enable the TSC page */ - tsc_msr.as_uint64 = hv_get_register(HV_REGISTER_REFERENCE_TSC); + tsc_msr.as_uint64 = hv_get_msr(HV_MSR_REFERENCE_TSC); tsc_msr.enable = 1; tsc_msr.pfn = tsc_pfn; - hv_set_register(HV_REGISTER_REFERENCE_TSC, tsc_msr.as_uint64); + hv_set_msr(HV_MSR_REFERENCE_TSC, tsc_msr.as_uint64); } #ifdef HAVE_VDSO_CLOCKMODE_HVCLOCK @@ -555,14 +555,14 @@ static void __init hv_init_tsc_clocksource(void) * thus TSC clocksource will work even without the real TSC page * mapped. */ - tsc_msr.as_uint64 = hv_get_register(HV_REGISTER_REFERENCE_TSC); + tsc_msr.as_uint64 = hv_get_msr(HV_MSR_REFERENCE_TSC); if (hv_root_partition) tsc_pfn = tsc_msr.pfn; else tsc_pfn = HVPFN_DOWN(virt_to_phys(tsc_page)); tsc_msr.enable = 1; tsc_msr.pfn = tsc_pfn; - hv_set_register(HV_REGISTER_REFERENCE_TSC, tsc_msr.as_uint64); + hv_set_msr(HV_MSR_REFERENCE_TSC, tsc_msr.as_uint64); clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 51e5018ac9b2..a8ad728354cb 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -270,7 +270,7 @@ void hv_synic_enable_regs(unsigned int cpu) union hv_synic_scontrol sctrl; /* Setup the Synic's message page */ - simp.as_uint64 = hv_get_register(HV_REGISTER_SIMP); + simp.as_uint64 = hv_get_msr(HV_MSR_SIMP); simp.simp_enabled = 1; if (ms_hyperv.paravisor_present || hv_root_partition) { @@ -286,10 +286,10 @@ void hv_synic_enable_regs(unsigned int cpu) >> HV_HYP_PAGE_SHIFT; } - hv_set_register(HV_REGISTER_SIMP, simp.as_uint64); + hv_set_msr(HV_MSR_SIMP, simp.as_uint64); /* Setup the Synic's event page */ - siefp.as_uint64 = hv_get_register(HV_REGISTER_SIEFP); + siefp.as_uint64 = hv_get_msr(HV_MSR_SIEFP); siefp.siefp_enabled = 1; if (ms_hyperv.paravisor_present || hv_root_partition) { @@ -305,13 +305,12 @@ void hv_synic_enable_regs(unsigned int cpu) >> HV_HYP_PAGE_SHIFT; } - hv_set_register(HV_REGISTER_SIEFP, siefp.as_uint64); + hv_set_msr(HV_MSR_SIEFP, siefp.as_uint64); /* Setup the shared SINT. */ if (vmbus_irq != -1) enable_percpu_irq(vmbus_irq, 0); - shared_sint.as_uint64 = hv_get_register(HV_REGISTER_SINT0 + - VMBUS_MESSAGE_SINT); + shared_sint.as_uint64 = hv_get_msr(HV_MSR_SINT0 + VMBUS_MESSAGE_SINT); shared_sint.vector = vmbus_interrupt; shared_sint.masked = false; @@ -326,14 +325,13 @@ void hv_synic_enable_regs(unsigned int cpu) #else shared_sint.auto_eoi = 0; #endif - hv_set_register(HV_REGISTER_SINT0 + VMBUS_MESSAGE_SINT, - shared_sint.as_uint64); + hv_set_msr(HV_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); /* Enable the global synic bit */ - sctrl.as_uint64 = hv_get_register(HV_REGISTER_SCONTROL); + sctrl.as_uint64 = hv_get_msr(HV_MSR_SCONTROL); sctrl.enable = 1; - hv_set_register(HV_REGISTER_SCONTROL, sctrl.as_uint64); + hv_set_msr(HV_MSR_SCONTROL, sctrl.as_uint64); } int hv_synic_init(unsigned int cpu) @@ -357,17 +355,15 @@ void hv_synic_disable_regs(unsigned int cpu) union hv_synic_siefp siefp; union hv_synic_scontrol sctrl; - shared_sint.as_uint64 = hv_get_register(HV_REGISTER_SINT0 + - VMBUS_MESSAGE_SINT); + shared_sint.as_uint64 = hv_get_msr(HV_MSR_SINT0 + VMBUS_MESSAGE_SINT); shared_sint.masked = 1; /* Need to correctly cleanup in the case of SMP!!! */ /* Disable the interrupt */ - hv_set_register(HV_REGISTER_SINT0 + VMBUS_MESSAGE_SINT, - shared_sint.as_uint64); + hv_set_msr(HV_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - simp.as_uint64 = hv_get_register(HV_REGISTER_SIMP); + simp.as_uint64 = hv_get_msr(HV_MSR_SIMP); /* * In Isolation VM, sim and sief pages are allocated by * paravisor. These pages also will be used by kdump @@ -382,9 +378,9 @@ void hv_synic_disable_regs(unsigned int cpu) simp.base_simp_gpa = 0; } - hv_set_register(HV_REGISTER_SIMP, simp.as_uint64); + hv_set_msr(HV_MSR_SIMP, simp.as_uint64); - siefp.as_uint64 = hv_get_register(HV_REGISTER_SIEFP); + siefp.as_uint64 = hv_get_msr(HV_MSR_SIEFP); siefp.siefp_enabled = 0; if (ms_hyperv.paravisor_present || hv_root_partition) { @@ -394,12 +390,12 @@ void hv_synic_disable_regs(unsigned int cpu) siefp.base_siefp_gpa = 0; } - hv_set_register(HV_REGISTER_SIEFP, siefp.as_uint64); + hv_set_msr(HV_MSR_SIEFP, siefp.as_uint64); /* Disable the global synic bit */ - sctrl.as_uint64 = hv_get_register(HV_REGISTER_SCONTROL); + sctrl.as_uint64 = hv_get_msr(HV_MSR_SCONTROL); sctrl.enable = 0; - hv_set_register(HV_REGISTER_SCONTROL, sctrl.as_uint64); + hv_set_msr(HV_MSR_SCONTROL, sctrl.as_uint64); if (vmbus_irq != -1) disable_percpu_irq(vmbus_irq); diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 0285a74363b3..2f1dd4b07f9a 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -227,19 +227,19 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper, * contain the size of the panic data in that page. Rest of the * registers are no-op when the NOTIFY_MSG flag is set. */ - hv_set_register(HV_REGISTER_CRASH_P0, 0); - hv_set_register(HV_REGISTER_CRASH_P1, 0); - hv_set_register(HV_REGISTER_CRASH_P2, 0); - hv_set_register(HV_REGISTER_CRASH_P3, virt_to_phys(hv_panic_page)); - hv_set_register(HV_REGISTER_CRASH_P4, bytes_written); + hv_set_msr(HV_MSR_CRASH_P0, 0); + hv_set_msr(HV_MSR_CRASH_P1, 0); + hv_set_msr(HV_MSR_CRASH_P2, 0); + hv_set_msr(HV_MSR_CRASH_P3, virt_to_phys(hv_panic_page)); + hv_set_msr(HV_MSR_CRASH_P4, bytes_written); /* * Let Hyper-V know there is crash data available along with * the panic message. */ - hv_set_register(HV_REGISTER_CRASH_CTL, - (HV_CRASH_CTL_CRASH_NOTIFY | - HV_CRASH_CTL_CRASH_NOTIFY_MSG)); + hv_set_msr(HV_MSR_CRASH_CTL, + (HV_CRASH_CTL_CRASH_NOTIFY | + HV_CRASH_CTL_CRASH_NOTIFY_MSG)); } static struct kmsg_dumper hv_kmsg_dumper = { @@ -310,7 +310,7 @@ int __init hv_common_init(void) * Register for panic kmsg callback only if the right * capability is supported by the hypervisor. */ - hyperv_crash_ctl = hv_get_register(HV_REGISTER_CRASH_CTL); + hyperv_crash_ctl = hv_get_msr(HV_MSR_CRASH_CTL); if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) hv_kmsg_dump_register(); @@ -409,7 +409,7 @@ int hv_common_cpu_init(unsigned int cpu) *inputarg = mem; } - msr_vp_index = hv_get_register(HV_REGISTER_VP_INDEX); + msr_vp_index = hv_get_msr(HV_MSR_VP_INDEX); hv_vp_index[cpu] = msr_vp_index; @@ -506,7 +506,7 @@ EXPORT_SYMBOL_GPL(hv_is_hibernation_supported); */ static u64 __hv_read_ref_counter(void) { - return hv_get_register(HV_REGISTER_TIME_REF_COUNT); + return hv_get_msr(HV_MSR_TIME_REF_COUNT); } u64 (*hv_read_reference_counter)(void) = __hv_read_ref_counter; diff --git a/include/asm-generic/hyperv-tlfs.h b/include/asm-generic/hyperv-tlfs.h index fdac4a1714ec..3d1b31f90ed6 100644 --- a/include/asm-generic/hyperv-tlfs.h +++ b/include/asm-generic/hyperv-tlfs.h @@ -625,6 +625,37 @@ struct hv_retarget_device_interrupt { struct hv_device_interrupt_target int_target; } __packed __aligned(8); +/* + * These Hyper-V registers provide information equivalent to the CPUID + * instruction on x86/x64. + */ +#define HV_REGISTER_HYPERVISOR_VERSION 0x00000100 /*CPUID 0x40000002 */ +#define HV_REGISTER_FEATURES 0x00000200 /*CPUID 0x40000003 */ +#define HV_REGISTER_ENLIGHTENMENTS 0x00000201 /*CPUID 0x40000004 */ + +/* + * Synthetic register definitions equivalent to MSRs on x86/x64 + */ +#define HV_REGISTER_CRASH_P0 0x00000210 +#define HV_REGISTER_CRASH_P1 0x00000211 +#define HV_REGISTER_CRASH_P2 0x00000212 +#define HV_REGISTER_CRASH_P3 0x00000213 +#define HV_REGISTER_CRASH_P4 0x00000214 +#define HV_REGISTER_CRASH_CTL 0x00000215 + +#define HV_REGISTER_GUEST_OSID 0x00090002 +#define HV_REGISTER_VP_INDEX 0x00090003 +#define HV_REGISTER_TIME_REF_COUNT 0x00090004 +#define HV_REGISTER_REFERENCE_TSC 0x00090017 + +#define HV_REGISTER_SINT0 0x000A0000 +#define HV_REGISTER_SCONTROL 0x000A0010 +#define HV_REGISTER_SIEFP 0x000A0012 +#define HV_REGISTER_SIMP 0x000A0013 +#define HV_REGISTER_EOM 0x000A0014 + +#define HV_REGISTER_STIMER0_CONFIG 0x000B0000 +#define HV_REGISTER_STIMER0_COUNT 0x000B0001 /* HvGetVpRegisters hypercall input with variable size reg name list*/ struct hv_get_vp_registers_input { @@ -640,7 +671,6 @@ struct hv_get_vp_registers_input { } element[]; } __packed; - /* HvGetVpRegisters returns an array of these output elements */ struct hv_get_vp_registers_output { union { diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 430f0ae0dde2..04424a446bb7 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -157,7 +157,7 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) * possibly deliver another msg from the * hypervisor */ - hv_set_register(HV_REGISTER_EOM, 0); + hv_set_msr(HV_MSR_EOM, 0); } } -- cgit v1.2.3 From d28240785e00ffb889d90368882bf382e22e9555 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 29 Feb 2024 14:17:33 +0100 Subject: usb: typec: pd: no opencoding of FIELD_GET If we have a neat macro, at least new code should use it. Signed-off-by: Oliver Neukum Link: https://lore.kernel.org/r/20240229131851.16148-2-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/pd_vdo.h | 5 +++-- include/linux/usb/typec_dp.h | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h index c09c5a12e273..5c48e8a81403 100644 --- a/include/linux/usb/pd_vdo.h +++ b/include/linux/usb/pd_vdo.h @@ -7,6 +7,7 @@ #define __LINUX_USB_PD_VDO_H #include "pd.h" +#include /* * VDO : Vendor Defined Message Object @@ -188,7 +189,7 @@ * <5:3> :: Alternate modes * <2:0> :: USB highest speed */ -#define PD_VDO_UFP_DEVCAP(vdo) (((vdo) & GENMASK(27, 24)) >> 24) +#define PD_VDO_UFP_DEVCAP(vdo) FIELD_GET(GENMASK(27, 24), vdo) /* UFP VDO Version */ #define UFP_VDO_VER1_2 2 @@ -247,7 +248,7 @@ * <21:5> :: Reserved * <4:0> :: Port number */ -#define PD_VDO_DFP_HOSTCAP(vdo) (((vdo) & GENMASK(26, 24)) >> 24) +#define PD_VDO_DFP_HOSTCAP(vdo) FIELD_GET(GENMASK(26, 24), vdo) #define DFP_VDO_VER1_1 1 #define HOST_USB2_CAPABLE BIT(0) diff --git a/include/linux/usb/typec_dp.h b/include/linux/usb/typec_dp.h index 1f358098522d..f2da264d9c14 100644 --- a/include/linux/usb/typec_dp.h +++ b/include/linux/usb/typec_dp.h @@ -3,6 +3,7 @@ #define __USB_TYPEC_DP_H #include +#include #define USB_TYPEC_DP_SID 0xff01 /* USB IF has not assigned a Standard ID (SID) for VirtualLink, @@ -67,21 +68,21 @@ enum { #define DP_CAP_UFP_D 1 #define DP_CAP_DFP_D 2 #define DP_CAP_DFP_D_AND_UFP_D 3 -#define DP_CAP_DP_SIGNALLING(_cap_) (((_cap_) & GENMASK(5, 2)) >> 2) +#define DP_CAP_DP_SIGNALLING(_cap_) FIELD_GET(GENMASK(5, 2), _cap_) #define DP_CAP_SIGNALLING_HBR3 1 #define DP_CAP_SIGNALLING_UHBR10 2 #define DP_CAP_SIGNALLING_UHBR20 3 #define DP_CAP_RECEPTACLE BIT(6) #define DP_CAP_USB BIT(7) -#define DP_CAP_DFP_D_PIN_ASSIGN(_cap_) (((_cap_) & GENMASK(15, 8)) >> 8) -#define DP_CAP_UFP_D_PIN_ASSIGN(_cap_) (((_cap_) & GENMASK(23, 16)) >> 16) +#define DP_CAP_DFP_D_PIN_ASSIGN(_cap_) FIELD_GET(GENMASK(15, 8), _cap_) +#define DP_CAP_UFP_D_PIN_ASSIGN(_cap_) FIELD_GET(GENMASK(23, 16), _cap_) /* Get pin assignment taking plug & receptacle into consideration */ #define DP_CAP_PIN_ASSIGN_UFP_D(_cap_) ((_cap_ & DP_CAP_RECEPTACLE) ? \ DP_CAP_UFP_D_PIN_ASSIGN(_cap_) : DP_CAP_DFP_D_PIN_ASSIGN(_cap_)) #define DP_CAP_PIN_ASSIGN_DFP_D(_cap_) ((_cap_ & DP_CAP_RECEPTACLE) ? \ DP_CAP_DFP_D_PIN_ASSIGN(_cap_) : DP_CAP_UFP_D_PIN_ASSIGN(_cap_)) #define DP_CAP_UHBR_13_5_SUPPORT BIT(26) -#define DP_CAP_CABLE_TYPE(_cap_) (((_cap_) & GENMASK(29, 28)) >> 28) +#define DP_CAP_CABLE_TYPE(_cap_) FIELD_GET(GENMASK(29, 28), _cap_) #define DP_CAP_CABLE_TYPE_PASSIVE 0 #define DP_CAP_CABLE_TYPE_RE_TIMER 1 #define DP_CAP_CABLE_TYPE_RE_DRIVER 2 @@ -116,7 +117,7 @@ enum { /* Helper for setting/getting the pin assignment value to the configuration */ #define DP_CONF_SET_PIN_ASSIGN(_a_) ((_a_) << 8) -#define DP_CONF_GET_PIN_ASSIGN(_conf_) (((_conf_) & GENMASK(15, 8)) >> 8) +#define DP_CONF_GET_PIN_ASSIGN(_conf_) FIELD_GET(GENMASK(15, 8), _conf_) #define DP_CONF_UHBR13_5_SUPPORT BIT(26) #define DP_CONF_CABLE_TYPE_MASK GENMASK(29, 28) #define DP_CONF_CABLE_TYPE_SHIFT 28 -- cgit v1.2.3 From 82e82130a78b75a9ce5225df24d5a0b1b3290eb0 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 22 Feb 2024 16:58:21 -0800 Subject: usb: core: Set connect_type of ports based on DT node When a USB hub is described in DT, such as any device that matches the onboard-hub driver, the connect_type is set to "unknown" or USB_PORT_CONNECT_TYPE_UNKNOWN. This makes any device plugged into that USB port report their 'removable' device attribute as "unknown". ChromeOS userspace would like to know if the USB device is actually removable or not so that security policies can be applied. Improve the connect_type attribute for ports, and in turn the removable attribute for USB devices, by looking for child devices with a reg property or an OF graph when the device is described in DT. If the graph exists, endpoints that are connected to a remote node must be something like a usb-{a,b,c}-connector compatible node, or an intermediate node like a redriver, and not a hardwired USB device on the board. Set the connect_type to USB_PORT_CONNECT_TYPE_HOT_PLUG in this case because the device is going to be plugged in. Set the connect_type to USB_PORT_CONNECT_TYPE_HARD_WIRED if there's a child node for the port like 'device@2' for port2. Set the connect_type to USB_PORT_NOT_USED if there isn't an endpoint or child node corresponding to the port number. To make sure things don't change, only set the port to not used if there are child nodes. This way an onboard hub connect_type doesn't change until ports are added or child nodes are added to describe hardwired devices. It's assumed that all ports or no ports will be described for a device. Cc: Matthias Kaehlcke Cc: linux-usb@vger.kernel.org Cc: devicetree@vger.kernel.org Cc: Pin-yen Lin Cc: maciek swiech Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20240223005823.3074029-3-swboyd@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/of.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/core/port.c | 2 ++ include/linux/usb/of.h | 7 +++++ 3 files changed, 80 insertions(+) (limited to 'include') diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index db4ccf9ce3d9..f1a499ee482c 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -8,6 +8,7 @@ */ #include +#include #include /** @@ -75,6 +76,76 @@ bool usb_of_has_combined_node(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_of_has_combined_node); +static bool usb_of_has_devices_or_graph(const struct usb_device *hub) +{ + const struct device_node *np = hub->dev.of_node; + struct device_node *child; + + if (of_graph_is_present(np)) + return true; + + for_each_child_of_node(np, child) + if (of_property_present(child, "reg")) + return true; + + return false; +} + +/** + * usb_of_get_connect_type() - get a USB hub's port connect_type + * @hub: hub to which port is for @port1 + * @port1: one-based index of port + * + * Get the connect_type of @port1 based on the device node for @hub. If the + * port is described in the OF graph, the connect_type is "hotplug". If the + * @hub has a child device has with a 'reg' property equal to @port1 the + * connect_type is "hard-wired". If there isn't an OF graph or child node at + * all then the connect_type is "unknown". Otherwise, the port is considered + * "unused" because it isn't described at all. + * + * Return: A connect_type for @port1 based on the device node for @hub. + */ +enum usb_port_connect_type usb_of_get_connect_type(struct usb_device *hub, int port1) +{ + struct device_node *np, *child, *ep, *remote_np; + enum usb_port_connect_type connect_type; + + /* Only set connect_type if binding has ports/hardwired devices. */ + if (!usb_of_has_devices_or_graph(hub)) + return USB_PORT_CONNECT_TYPE_UNKNOWN; + + /* Assume port is unused if there's a graph or a child node. */ + connect_type = USB_PORT_NOT_USED; + + np = hub->dev.of_node; + /* + * Hotplug ports are connected to an available remote node, e.g. + * usb-a-connector compatible node, in the OF graph. + */ + if (of_graph_is_present(np)) { + ep = of_graph_get_endpoint_by_regs(np, port1, -1); + if (ep) { + remote_np = of_graph_get_remote_port_parent(ep); + of_node_put(ep); + if (of_device_is_available(remote_np)) + connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG; + of_node_put(remote_np); + } + } + + /* + * Hard-wired ports are child nodes with a reg property corresponding + * to the port number, i.e. a usb device. + */ + child = usb_of_get_device_node(hub, port1); + if (of_device_is_available(child)) + connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED; + of_node_put(child); + + return connect_type; +} +EXPORT_SYMBOL_GPL(usb_of_get_connect_type); + /** * usb_of_get_interface_node() - get a USB interface node * @udev: USB device of interface diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 84d36172b040..e1613b02c19f 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "hub.h" @@ -708,6 +709,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) return -ENOMEM; } + port_dev->connect_type = usb_of_get_connect_type(hdev, port1); hub->ports[port1 - 1] = port_dev; port_dev->portnum = port1; set_bit(port1, hub->power_bits); diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index 98487fd7ab11..de42f14bd280 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -6,6 +6,7 @@ #ifndef __LINUX_USB_OF_H #define __LINUX_USB_OF_H +#include #include #include #include @@ -17,6 +18,7 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0); bool of_usb_host_tpl_support(struct device_node *np); int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps); +enum usb_port_connect_type usb_of_get_connect_type(struct usb_device *hub, int port1); struct device_node *usb_of_get_device_node(struct usb_device *hub, int port1); bool usb_of_has_combined_node(struct usb_device *udev); struct device_node *usb_of_get_interface_node(struct usb_device *udev, @@ -37,6 +39,11 @@ static inline int of_usb_update_otg_caps(struct device_node *np, { return 0; } +static inline enum usb_port_connect_type +usb_of_get_connect_type(const struct usb_device *hub, int port1) +{ + return USB_PORT_CONNECT_TYPE_UNKNOWN; +} static inline struct device_node * usb_of_get_device_node(struct usb_device *hub, int port1) { -- cgit v1.2.3 From ac92ea6b656374abab230f9f38fd3f0ab6cd0d61 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 22 Feb 2024 22:09:02 +0100 Subject: usb: typec: tcpm: add support to set tcpc connector orientatition This adds the support to set the connector orientation value accordingly. This is part of the optional CONFIG_STANDARD_OUTPUT register 0x18, specified within the USB port controller spsicification rev. 2.0 [1]. [1] https://www.usb.org/sites/default/files/documents/usb-port_controller_specification_rev2.0_v1.0_0.pdf Signed-off-by: Marco Felsch Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240222210903.208901-4-m.felsch@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 6 ++++++ include/linux/usb/tcpm.h | 2 ++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index b6d7131228f9..3d505614bff1 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1167,6 +1167,12 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached, if (ret < 0) return ret; + if (port->tcpc->set_orientation) { + ret = port->tcpc->set_orientation(port->tcpc, orientation); + if (ret < 0) + return ret; + } + port->pwr_role = role; port->data_role = data; typec_set_data_role(port->typec_port, data); diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 6671427f7eeb..061da9546a81 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -144,6 +144,8 @@ struct tcpc_dev { enum typec_cc_status *cc2); int (*set_polarity)(struct tcpc_dev *dev, enum typec_cc_polarity polarity); + int (*set_orientation)(struct tcpc_dev *dev, + enum typec_orientation orientation); int (*set_vconn)(struct tcpc_dev *dev, bool on); int (*set_vbus)(struct tcpc_dev *dev, bool on, bool charge); int (*set_current_limit)(struct tcpc_dev *dev, u32 max_ma, u32 mv); -- cgit v1.2.3 From 7bfb915a597a301abb892f620fe5c283a9fdbd77 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Sun, 3 Mar 2024 16:08:07 +0100 Subject: serial: core: only stop transmit when HW fifo is empty If the circular buffer is empty, it just means we fit all characters to send into the HW fifo, but not that the hardware finished transmitting them. So if we immediately call stop_tx() after that, this may abort any pending characters in the HW fifo, and cause dropped characters on the console. Fix this by only stopping tx when the tx HW fifo is actually empty. Fixes: 8275b48b2780 ("tty: serial: introduce transmit helpers") Cc: stable@vger.kernel.org Signed-off-by: Jonas Gorski Link: https://lore.kernel.org/r/20240303150807.68117-1-jonas.gorski@gmail.com Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 55b1f3ba48ac..bb0f2d4ac62f 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -786,7 +786,8 @@ enum UART_TX_FLAGS { if (pending < WAKEUP_CHARS) { \ uart_write_wakeup(__port); \ \ - if (!((flags) & UART_TX_NOSTOP) && pending == 0) \ + if (!((flags) & UART_TX_NOSTOP) && pending == 0 && \ + __port->ops->tx_empty(__port)) \ __port->ops->stop_tx(__port); \ } \ \ -- cgit v1.2.3 From 35c822a34b2293aedf475238c395e75858d1e8c8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Mar 2024 14:27:02 +0200 Subject: serial: core: Move struct uart_port::quirks closer to possible values Currently it's not crystal clear what UPIO_* and UPQ_* definitions belong to. Reindent the code, so it will be easy to read and understand. No functional changes intended. Reviewed-by: Andi Shyti Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240304123035.758700-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index bb0f2d4ac62f..f9d7f0a625fd 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -467,8 +467,8 @@ struct uart_port { unsigned int fifosize; /* tx fifo size */ unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* reg offset shift */ + unsigned char iotype; /* io access style */ - unsigned char quirks; /* internal quirks */ #define UPIO_PORT (SERIAL_IO_PORT) /* 8b I/O port access */ #define UPIO_HUB6 (SERIAL_IO_HUB6) /* Hub6 ISA card */ @@ -479,7 +479,9 @@ struct uart_port { #define UPIO_MEM32BE (SERIAL_IO_MEM32BE) /* 32b big endian */ #define UPIO_MEM16 (SERIAL_IO_MEM16) /* 16b little endian */ - /* quirks must be updated while holding port mutex */ + unsigned char quirks; /* internal quirks */ + + /* internal quirks must be updated while holding port mutex */ #define UPQ_NO_TXEN_TEST BIT(0) unsigned int read_status_mask; /* driver specific */ -- cgit v1.2.3 From 79d713baf63c8f23cc58b304c40be33d64a12aaf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Mar 2024 14:27:03 +0200 Subject: serial: core: Add UPIO_UNKNOWN constant for unknown port type In some APIs we would like to assign the special value to iotype and compare against it in another places. Introduce UPIO_UNKNOWN for this purpose. Note, we can't use 0, because it's a valid value for IO port access. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240304123035.758700-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index f9d7f0a625fd..3b64c9a26945 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -470,6 +470,7 @@ struct uart_port { unsigned char iotype; /* io access style */ +#define UPIO_UNKNOWN ((unsigned char)~0U) /* UCHAR_MAX */ #define UPIO_PORT (SERIAL_IO_PORT) /* 8b I/O port access */ #define UPIO_HUB6 (SERIAL_IO_HUB6) /* Hub6 ISA card */ #define UPIO_MEM (SERIAL_IO_MEM) /* driver-specific */ -- cgit v1.2.3 From e894b6005dce0ed621b2788d6a249708fb6f95f9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Mar 2024 14:27:04 +0200 Subject: serial: port: Introduce a common helper to read properties Several serial drivers want to read the same or similar set of the port properties. Make a common helper for them. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240304123035.758700-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_port.c | 145 +++++++++++++++++++++++++++++++++++++++ include/linux/serial_core.h | 2 + 2 files changed, 147 insertions(+) (limited to 'include') diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c index 88975a4df306..6779a008e67a 100644 --- a/drivers/tty/serial/serial_port.c +++ b/drivers/tty/serial/serial_port.c @@ -8,7 +8,10 @@ #include #include +#include +#include #include +#include #include #include @@ -82,6 +85,148 @@ void uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) } EXPORT_SYMBOL(uart_remove_one_port); +/** + * __uart_read_properties - read firmware properties of the given UART port + * @port: corresponding port + * @use_defaults: apply defaults (when %true) or validate the values (when %false) + * + * The following device properties are supported: + * - clock-frequency (optional) + * - fifo-size (optional) + * - no-loopback-test (optional) + * - reg-shift (defaults may apply) + * - reg-offset (value may be validated) + * - reg-io-width (defaults may apply or value may be validated) + * - interrupts (OF only) + * - serial [alias ID] (OF only) + * + * If the port->dev is of struct platform_device type the interrupt line + * will be retrieved via platform_get_irq() call against that device. + * Otherwise it will be assigned by fwnode_irq_get() call. In both cases + * the index 0 of the resource is used. + * + * The caller is responsible to initialize the following fields of the @port + * ->dev (must be valid) + * ->flags + * ->mapbase + * ->mapsize + * ->regshift (if @use_defaults is false) + * before calling this function. Alternatively the above mentioned fields + * may be zeroed, in such case the only ones, that have associated properties + * found, will be set to the respective values. + * + * If no error happened, the ->irq, ->mapbase, ->mapsize will be altered. + * The ->iotype is always altered. + * + * When @use_defaults is true and the respective property is not found + * the following values will be applied: + * ->regshift = 0 + * In this case IRQ must be provided, otherwise an error will be returned. + * + * When @use_defaults is false and the respective property is found + * the following values will be validated: + * - reg-io-width (->iotype) + * - reg-offset (->mapsize against ->mapbase) + * + * Returns: 0 on success or negative errno on failure + */ +static int __uart_read_properties(struct uart_port *port, bool use_defaults) +{ + struct device *dev = port->dev; + u32 value; + int ret; + + /* Read optional UART functional clock frequency */ + device_property_read_u32(dev, "clock-frequency", &port->uartclk); + + /* Read the registers alignment (default: 8-bit) */ + ret = device_property_read_u32(dev, "reg-shift", &value); + if (ret) + port->regshift = use_defaults ? 0 : port->regshift; + else + port->regshift = value; + + /* Read the registers I/O access type (default: MMIO 8-bit) */ + ret = device_property_read_u32(dev, "reg-io-width", &value); + if (ret) { + port->iotype = UPIO_MEM; + } else { + switch (value) { + case 1: + port->iotype = UPIO_MEM; + break; + case 2: + port->iotype = UPIO_MEM16; + break; + case 4: + port->iotype = device_is_big_endian(dev) ? UPIO_MEM32BE : UPIO_MEM32; + break; + default: + if (!use_defaults) { + dev_err(dev, "Unsupported reg-io-width (%u)\n", value); + return -EINVAL; + } + port->iotype = UPIO_UNKNOWN; + break; + } + } + + /* Read the address mapping base offset (default: no offset) */ + ret = device_property_read_u32(dev, "reg-offset", &value); + if (ret) + value = 0; + + /* Check for shifted address mapping overflow */ + if (!use_defaults && port->mapsize < value) { + dev_err(dev, "reg-offset %u exceeds region size %pa\n", value, &port->mapsize); + return -EINVAL; + } + + port->mapbase += value; + port->mapsize -= value; + + /* Read optional FIFO size */ + device_property_read_u32(dev, "fifo-size", &port->fifosize); + + if (device_property_read_bool(dev, "no-loopback-test")) + port->flags |= UPF_SKIP_TEST; + + /* Get index of serial line, if found in DT aliases */ + ret = of_alias_get_id(dev_of_node(dev), "serial"); + if (ret >= 0) + port->line = ret; + + if (dev_is_platform(dev)) + ret = platform_get_irq(to_platform_device(dev), 0); + else + ret = fwnode_irq_get(dev_fwnode(dev), 0); + if (ret == -EPROBE_DEFER) + return ret; + if (ret > 0) + port->irq = ret; + else if (use_defaults) + /* By default IRQ support is mandatory */ + return ret; + else + port->irq = 0; + + port->flags |= UPF_SHARE_IRQ; + + return 0; +} + +int uart_read_port_properties(struct uart_port *port) +{ + return __uart_read_properties(port, true); +} +EXPORT_SYMBOL_GPL(uart_read_port_properties); + +int uart_read_and_validate_port_properties(struct uart_port *port) +{ + return __uart_read_properties(port, false); +} +EXPORT_SYMBOL_GPL(uart_read_and_validate_port_properties); + static struct device_driver serial_port_driver = { .name = "port", .suppress_bind_attrs = true, diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 3b64c9a26945..0a0f6e21d40e 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -963,6 +963,8 @@ int uart_register_driver(struct uart_driver *uart); void uart_unregister_driver(struct uart_driver *uart); int uart_add_one_port(struct uart_driver *reg, struct uart_port *port); void uart_remove_one_port(struct uart_driver *reg, struct uart_port *port); +int uart_read_port_properties(struct uart_port *port); +int uart_read_and_validate_port_properties(struct uart_port *port); bool uart_match_port(const struct uart_port *port1, const struct uart_port *port2); -- cgit v1.2.3 From 7ba59ac7da2aae2fbcfe90352ee40d30cecca10d Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 4 Mar 2024 13:19:45 -0800 Subject: greybus: Avoid fake flexible array for response data FORTIFY_SOURCE has been ignoring 0-sized destinations while the kernel code base has been converted to flexible arrays. In order to enforce the 0-sized destinations (e.g. with __counted_by), the remaining 0-sized destinations need to be handled. Instead of converting an empty struct into using a flexible array, just directly use a pointer without any additional indirection. Remove struct gb_bootrom_get_firmware_response and struct gb_fw_download_fetch_firmware_response. Signed-off-by: Kees Cook Reviewed-by: Alex Elder Link: https://lore.kernel.org/r/20240304211940.it.083-kees@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bootrom.c | 8 ++++---- drivers/staging/greybus/fw-download.c | 8 ++++---- include/linux/greybus/greybus_protocols.h | 8 ++------ 3 files changed, 10 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index 79581457c4af..c0d338db6b52 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -243,10 +243,10 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); const struct firmware *fw; struct gb_bootrom_get_firmware_request *firmware_request; - struct gb_bootrom_get_firmware_response *firmware_response; struct device *dev = &op->connection->bundle->dev; unsigned int offset, size; enum next_request_type next_request; + u8 *firmware_response; int ret = 0; /* Disable timeouts */ @@ -280,15 +280,15 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) goto unlock; } - if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, - GFP_KERNEL)) { + /* gb_bootrom_get_firmware_response contains only a byte array */ + if (!gb_operation_response_alloc(op, size, GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); ret = -ENOMEM; goto unlock; } firmware_response = op->response->payload; - memcpy(firmware_response->data, fw->data + offset, size); + memcpy(firmware_response, fw->data + offset, size); dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, size); diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 2a5c6d1b049c..9a09bd3af79b 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -270,11 +270,11 @@ static int fw_download_fetch_firmware(struct gb_operation *op) struct gb_connection *connection = op->connection; struct fw_download *fw_download = gb_connection_get_data(connection); struct gb_fw_download_fetch_firmware_request *request; - struct gb_fw_download_fetch_firmware_response *response; struct fw_request *fw_req; const struct firmware *fw; unsigned int offset, size; u8 firmware_id; + u8 *response; int ret = 0; if (op->request->payload_size != sizeof(*request)) { @@ -324,8 +324,8 @@ static int fw_download_fetch_firmware(struct gb_operation *op) goto put_fw; } - if (!gb_operation_response_alloc(op, sizeof(*response) + size, - GFP_KERNEL)) { + /* gb_fw_download_fetch_firmware_response contains only a byte array */ + if (!gb_operation_response_alloc(op, size, GFP_KERNEL)) { dev_err(fw_download->parent, "error allocating fetch firmware response\n"); ret = -ENOMEM; @@ -333,7 +333,7 @@ static int fw_download_fetch_firmware(struct gb_operation *op) } response = op->response->payload; - memcpy(response->data, fw->data + offset, size); + memcpy(response, fw->data + offset, size); dev_dbg(fw_download->parent, "responding with firmware (offs = %u, size = %u)\n", offset, diff --git a/include/linux/greybus/greybus_protocols.h b/include/linux/greybus/greybus_protocols.h index aeb8f9243545..820134b0105c 100644 --- a/include/linux/greybus/greybus_protocols.h +++ b/include/linux/greybus/greybus_protocols.h @@ -232,9 +232,7 @@ struct gb_fw_download_fetch_firmware_request { __le32 size; } __packed; -struct gb_fw_download_fetch_firmware_response { - __u8 data[0]; -} __packed; +/* gb_fw_download_fetch_firmware_response contains no other data */ /* firmware download release firmware request */ struct gb_fw_download_release_firmware_request { @@ -414,9 +412,7 @@ struct gb_bootrom_get_firmware_request { __le32 size; } __packed; -struct gb_bootrom_get_firmware_response { - __u8 data[0]; -} __packed; +/* gb_bootrom_get_firmware_response contains no other data */ /* Bootrom protocol Ready to boot request */ struct gb_bootrom_ready_to_boot_request { -- cgit v1.2.3 From 00b9850e7307ff690639dd0584a50dd8a72d3548 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 5 Jan 2024 11:16:17 +0100 Subject: greybus: make greybus_bus_type const Now that the driver core can properly handle constant struct bus_type, move the greybus_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Alex Elder Cc: greybus-dev@lists.linaro.org Reviewed-by: Johan Hovold Link: https://lore.kernel.org/r/2024010517-handgun-scoreless-05e7@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/core.c | 2 +- include/linux/greybus.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/greybus/core.c b/drivers/greybus/core.c index 5714be740470..b0aadc1e7bbe 100644 --- a/drivers/greybus/core.c +++ b/drivers/greybus/core.c @@ -155,7 +155,7 @@ static void greybus_shutdown(struct device *dev) } } -struct bus_type greybus_bus_type = { +const struct bus_type greybus_bus_type = { .name = "greybus", .match = greybus_match_device, .uevent = greybus_uevent, diff --git a/include/linux/greybus.h b/include/linux/greybus.h index 18c0fb958b74..92da9ec4f5f0 100644 --- a/include/linux/greybus.h +++ b/include/linux/greybus.h @@ -104,7 +104,7 @@ void gb_debugfs_init(void); void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); -extern struct bus_type greybus_bus_type; +extern const struct bus_type greybus_bus_type; extern struct device_type greybus_hd_type; extern struct device_type greybus_module_type; -- cgit v1.2.3 From e869b72b33731063b50433eb6146d51a479995a1 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Mon, 19 Feb 2024 09:40:50 -0300 Subject: greybus: constify the struct device_type usage Since commit aed65af1cc2f ("drivers: make device_type const"), the driver core can properly handle constant struct device_type. Move the greybus_hd_type, greybus_module_type, greybus_interface_type, greybus_control_type, greybus_bundle_type and greybus_svc_type variables to be constant structures as well, placing it into read-only memory which can not be modified at runtime. Signed-off-by: "Ricardo B. Marliere" Reviewed-by: Alex Elder Link: https://lore.kernel.org/r/20240219-device_cleanup-greybus-v1-1-babb3f65e8cc@marliere.net Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/bundle.c | 2 +- drivers/greybus/control.c | 2 +- drivers/greybus/hd.c | 2 +- drivers/greybus/interface.c | 2 +- drivers/greybus/module.c | 2 +- drivers/greybus/svc.c | 2 +- include/linux/greybus.h | 12 ++++++------ 7 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/greybus/bundle.c b/drivers/greybus/bundle.c index 84660729538b..a6e1cca06172 100644 --- a/drivers/greybus/bundle.c +++ b/drivers/greybus/bundle.c @@ -166,7 +166,7 @@ static const struct dev_pm_ops gb_bundle_pm_ops = { SET_RUNTIME_PM_OPS(gb_bundle_suspend, gb_bundle_resume, gb_bundle_idle) }; -struct device_type greybus_bundle_type = { +const struct device_type greybus_bundle_type = { .name = "greybus_bundle", .release = gb_bundle_release, .pm = &gb_bundle_pm_ops, diff --git a/drivers/greybus/control.c b/drivers/greybus/control.c index 359a25841973..b5cf49d09df2 100644 --- a/drivers/greybus/control.c +++ b/drivers/greybus/control.c @@ -436,7 +436,7 @@ static void gb_control_release(struct device *dev) kfree(control); } -struct device_type greybus_control_type = { +const struct device_type greybus_control_type = { .name = "greybus_control", .release = gb_control_release, }; diff --git a/drivers/greybus/hd.c b/drivers/greybus/hd.c index 72b21bf2d7d3..e2f3496bddc3 100644 --- a/drivers/greybus/hd.c +++ b/drivers/greybus/hd.c @@ -116,7 +116,7 @@ static void gb_hd_release(struct device *dev) kfree(hd); } -struct device_type greybus_hd_type = { +const struct device_type greybus_hd_type = { .name = "greybus_host_device", .release = gb_hd_release, }; diff --git a/drivers/greybus/interface.c b/drivers/greybus/interface.c index 9ec949a438ef..a88dc701289c 100644 --- a/drivers/greybus/interface.c +++ b/drivers/greybus/interface.c @@ -765,7 +765,7 @@ static const struct dev_pm_ops gb_interface_pm_ops = { gb_interface_runtime_idle) }; -struct device_type greybus_interface_type = { +const struct device_type greybus_interface_type = { .name = "greybus_interface", .release = gb_interface_release, .pm = &gb_interface_pm_ops, diff --git a/drivers/greybus/module.c b/drivers/greybus/module.c index 36f77f9e1d74..7f7153a1dd60 100644 --- a/drivers/greybus/module.c +++ b/drivers/greybus/module.c @@ -81,7 +81,7 @@ static void gb_module_release(struct device *dev) kfree(module); } -struct device_type greybus_module_type = { +const struct device_type greybus_module_type = { .name = "greybus_module", .release = gb_module_release, }; diff --git a/drivers/greybus/svc.c b/drivers/greybus/svc.c index 0d7e749174a4..4256467fcd35 100644 --- a/drivers/greybus/svc.c +++ b/drivers/greybus/svc.c @@ -1305,7 +1305,7 @@ static void gb_svc_release(struct device *dev) kfree(svc); } -struct device_type greybus_svc_type = { +const struct device_type greybus_svc_type = { .name = "greybus_svc", .release = gb_svc_release, }; diff --git a/include/linux/greybus.h b/include/linux/greybus.h index 92da9ec4f5f0..2cc570ea63bf 100644 --- a/include/linux/greybus.h +++ b/include/linux/greybus.h @@ -106,12 +106,12 @@ struct dentry *gb_debugfs_get(void); extern const struct bus_type greybus_bus_type; -extern struct device_type greybus_hd_type; -extern struct device_type greybus_module_type; -extern struct device_type greybus_interface_type; -extern struct device_type greybus_control_type; -extern struct device_type greybus_bundle_type; -extern struct device_type greybus_svc_type; +extern const struct device_type greybus_hd_type; +extern const struct device_type greybus_module_type; +extern const struct device_type greybus_interface_type; +extern const struct device_type greybus_control_type; +extern const struct device_type greybus_bundle_type; +extern const struct device_type greybus_svc_type; static inline int is_gb_host_device(const struct device *dev) { -- cgit v1.2.3 From ab23f1bffcf690ffae2b0c4bb8b09420299be05c Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sat, 24 Feb 2024 11:44:03 +0000 Subject: slimbus: core: make slimbus_bus const Since commit d492cc2573a0 ("driver core: device.h: make struct bus_type a const *"), the driver core can properly handle constant struct bus_type, move the slimbus_bus variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: "Ricardo B. Marliere" Reviewed-by: Greg Kroah-Hartman Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20240224114403.86230-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/core.c | 2 +- include/linux/slimbus.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index 01cbd4621981..41e62de1f91f 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -100,7 +100,7 @@ static int slim_device_uevent(const struct device *dev, struct kobj_uevent_env * return add_uevent_var(env, "MODALIAS=slim:%s", dev_name(&sbdev->dev)); } -struct bus_type slimbus_bus = { +const struct bus_type slimbus_bus = { .name = "slimbus", .match = slim_device_match, .probe = slim_device_probe, diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h index 12c9719b2a55..3042385b7b40 100644 --- a/include/linux/slimbus.h +++ b/include/linux/slimbus.h @@ -10,7 +10,7 @@ #include #include -extern struct bus_type slimbus_bus; +extern const struct bus_type slimbus_bus; /** * struct slim_eaddr - Enumeration address for a SLIMbus device -- cgit v1.2.3 From e34b943068d30f20db31f28100affdaaedc7efab Mon Sep 17 00:00:00 2001 From: Praveen Teja Kundanala Date: Sat, 24 Feb 2024 11:45:10 +0000 Subject: firmware: xilinx: Add ZynqMP efuse access API Add zynqmp_pm_efuse_access API in the ZynqMP firmware for read/write access of efuse memory. Signed-off-by: Praveen Teja Kundanala Acked-by: Michal Simek Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20240224114516.86365-6-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/xilinx/zynqmp.c | 25 +++++++++++++++++++++++++ include/linux/firmware/xlnx-zynqmp.h | 8 ++++++++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 79789f0563f6..9bc45357e1a8 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -3,6 +3,7 @@ * Xilinx Zynq MPSoC Firmware layer * * Copyright (C) 2014-2022 Xilinx, Inc. + * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc. * * Michal Simek * Davorin Mista @@ -1384,6 +1385,30 @@ int zynqmp_pm_aes_engine(const u64 address, u32 *out) } EXPORT_SYMBOL_GPL(zynqmp_pm_aes_engine); +/** + * zynqmp_pm_efuse_access - Provides access to efuse memory. + * @address: Address of the efuse params structure + * @out: Returned output value + * + * Return: Returns status, either success or error code. + */ +int zynqmp_pm_efuse_access(const u64 address, u32 *out) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!out) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_EFUSE_ACCESS, ret_payload, 2, + upper_32_bits(address), + lower_32_bits(address)); + *out = ret_payload[1]; + + return ret; +} +EXPORT_SYMBOL_GPL(zynqmp_pm_efuse_access); + /** * zynqmp_pm_sha_hash - Access the SHA engine to calculate the hash * @address: Address of the data/ Address of output buffer where diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 9a7e52739251..1a069a56c961 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -3,6 +3,7 @@ * Xilinx Zynq MPSoC Firmware layer * * Copyright (C) 2014-2021 Xilinx + * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc. * * Michal Simek * Davorin Mista @@ -171,6 +172,7 @@ enum pm_api_id { PM_CLOCK_GETPARENT = 44, PM_FPGA_READ = 46, PM_SECURE_AES = 47, + PM_EFUSE_ACCESS = 53, PM_FEATURE_CHECK = 63, }; @@ -562,6 +564,7 @@ int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities, const u32 qos, const enum zynqmp_pm_request_ack ack); int zynqmp_pm_aes_engine(const u64 address, u32 *out); +int zynqmp_pm_efuse_access(const u64 address, u32 *out); int zynqmp_pm_sha_hash(const u64 address, const u32 size, const u32 flags); int zynqmp_pm_fpga_load(const u64 address, const u32 size, const u32 flags); int zynqmp_pm_fpga_get_status(u32 *value); @@ -749,6 +752,11 @@ static inline int zynqmp_pm_aes_engine(const u64 address, u32 *out) return -ENODEV; } +static inline int zynqmp_pm_efuse_access(const u64 address, u32 *out) +{ + return -ENODEV; +} + static inline int zynqmp_pm_sha_hash(const u64 address, const u32 size, const u32 flags) { -- cgit v1.2.3 From cb1c1224193e648b4108dd06ebb7cc86b5c514ad Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Mon, 12 Feb 2024 08:41:01 -0300 Subject: dio: make dio_bus_type const Now that the driver core can properly handle constant struct bus_type, move the dio_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: "Ricardo B. Marliere" Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240212-bus_cleanup-dio-v2-1-3b1ba4c0547d@marliere.net Signed-off-by: Greg Kroah-Hartman --- drivers/dio/dio-driver.c | 2 +- include/linux/dio.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/dio/dio-driver.c b/drivers/dio/dio-driver.c index 69c46935ffc7..2d9fa6011945 100644 --- a/drivers/dio/dio-driver.c +++ b/drivers/dio/dio-driver.c @@ -123,7 +123,7 @@ static int dio_bus_match(struct device *dev, struct device_driver *drv) } -struct bus_type dio_bus_type = { +const struct bus_type dio_bus_type = { .name = "dio", .match = dio_bus_match, .probe = dio_device_probe, diff --git a/include/linux/dio.h b/include/linux/dio.h index 5abd07361eb5..2b5923909f96 100644 --- a/include/linux/dio.h +++ b/include/linux/dio.h @@ -68,7 +68,7 @@ struct dio_bus { }; extern struct dio_bus dio_bus; /* Single DIO bus */ -extern struct bus_type dio_bus_type; +extern const struct bus_type dio_bus_type; /* * DIO device IDs -- cgit v1.2.3 From d843f031d9e90462253015bc0bd9e3852d206bf2 Mon Sep 17 00:00:00 2001 From: Wayne Chang Date: Thu, 7 Mar 2024 11:03:27 +0800 Subject: phy: tegra: xusb: Add API to retrieve the port number of phy This patch introduces a new API, tegra_xusb_padctl_get_port_number, to the Tegra XUSB Pad Controller driver. This API is used to identify the USB port that is associated with a given PHY. The function takes a PHY pointer for either a USB2 PHY or USB3 PHY as input and returns the corresponding port number. If the PHY pointer is invalid, it returns -ENODEV. Cc: stable@vger.kernel.org Signed-off-by: Wayne Chang Reviewed-by: Jon Hunter Tested-by: Jon Hunter Link: https://lore.kernel.org/r/20240307030328.1487748-2-waynec@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/phy/tegra/xusb.c | 13 +++++++++++++ include/linux/phy/tegra/xusb.h | 1 + 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index 142ebe0247cc..983a6e6173bd 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -1531,6 +1531,19 @@ int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl, } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get_usb3_companion); +int tegra_xusb_padctl_get_port_number(struct phy *phy) +{ + struct tegra_xusb_lane *lane; + + if (!phy) + return -ENODEV; + + lane = phy_get_drvdata(phy); + + return lane->index; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get_port_number); + MODULE_AUTHOR("Thierry Reding "); MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h index 70998e6dd6fd..6ca51e0080ec 100644 --- a/include/linux/phy/tegra/xusb.h +++ b/include/linux/phy/tegra/xusb.h @@ -26,6 +26,7 @@ void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy); int tegra_phy_xusb_utmi_port_reset(struct phy *phy); int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl, unsigned int port); +int tegra_xusb_padctl_get_port_number(struct phy *phy); int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy, enum usb_device_speed speed); int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy); -- cgit v1.2.3 From a13bd6f3c936edff957f2d02cf65c44046cb1243 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Mon, 26 Feb 2024 18:05:19 -0300 Subject: greybus: move is_gb_* functions out of greybus.h The functions below are only used within the context of drivers/greybus/core.c, so move them all into core and drop their 'inline' specifiers: is_gb_host_device(), is_gb_module(), is_gb_interface(), is_gb_control(), is_gb_bundle() and is_gb_svc(). Suggested-by: Alex Elder Cc: Greg Kroah-Hartman Signed-off-by: "Ricardo B. Marliere" Reviewed-by: Alex Elder Link: https://lore.kernel.org/r/20240226-device_cleanup-greybus2-v1-1-5f7d1161e684@marliere.net Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/core.c | 30 ++++++++++++++++++++++++++++++ include/linux/greybus.h | 30 ------------------------------ 2 files changed, 30 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/greybus/core.c b/drivers/greybus/core.c index b0aadc1e7bbe..95c09d4f3a86 100644 --- a/drivers/greybus/core.c +++ b/drivers/greybus/core.c @@ -27,6 +27,36 @@ int greybus_disabled(void) } EXPORT_SYMBOL_GPL(greybus_disabled); +static int is_gb_host_device(const struct device *dev) +{ + return dev->type == &greybus_hd_type; +} + +static int is_gb_module(const struct device *dev) +{ + return dev->type == &greybus_module_type; +} + +static int is_gb_interface(const struct device *dev) +{ + return dev->type == &greybus_interface_type; +} + +static int is_gb_control(const struct device *dev) +{ + return dev->type == &greybus_control_type; +} + +static int is_gb_bundle(const struct device *dev) +{ + return dev->type == &greybus_bundle_type; +} + +static int is_gb_svc(const struct device *dev) +{ + return dev->type == &greybus_svc_type; +} + static bool greybus_match_one_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { diff --git a/include/linux/greybus.h b/include/linux/greybus.h index 2cc570ea63bf..634c9511cf78 100644 --- a/include/linux/greybus.h +++ b/include/linux/greybus.h @@ -113,36 +113,6 @@ extern const struct device_type greybus_control_type; extern const struct device_type greybus_bundle_type; extern const struct device_type greybus_svc_type; -static inline int is_gb_host_device(const struct device *dev) -{ - return dev->type == &greybus_hd_type; -} - -static inline int is_gb_module(const struct device *dev) -{ - return dev->type == &greybus_module_type; -} - -static inline int is_gb_interface(const struct device *dev) -{ - return dev->type == &greybus_interface_type; -} - -static inline int is_gb_control(const struct device *dev) -{ - return dev->type == &greybus_control_type; -} - -static inline int is_gb_bundle(const struct device *dev) -{ - return dev->type == &greybus_bundle_type; -} - -static inline int is_gb_svc(const struct device *dev) -{ - return dev->type == &greybus_svc_type; -} - static inline bool cport_id_valid(struct gb_host_device *hd, u16 cport_id) { return cport_id != CPORT_ID_BAD && cport_id < hd->num_cports; -- cgit v1.2.3 From 0e439ba38e615e505404b3935585f1898bafaea9 Mon Sep 17 00:00:00 2001 From: Nipun Gupta Date: Mon, 26 Feb 2024 13:58:16 +0530 Subject: cdx: add MSI support for CDX bus Add CDX-MSI domain per CDX controller with gic-its domain as a parent, to support MSI for CDX devices. CDX devices allocate MSIs from the CDX domain. Also, introduce APIs to alloc and free IRQs for CDX domain. In CDX subsystem firmware is a controller for all devices and their configuration. CDX bus controller sends all the write_msi_msg commands to firmware running on RPU and the firmware interfaces with actual devices to pass this information to devices Since, CDX controller is the only way to communicate with the Firmware for MSI write info, CDX domain per controller required in contrast to having a CDX domain per device. Co-developed-by: Nikhil Agarwal Signed-off-by: Nikhil Agarwal Co-developed-by: Abhijit Gangurde Signed-off-by: Abhijit Gangurde Signed-off-by: Nipun Gupta Reviewed-by: Pieter Jansen van Vuuren Reviewed-by: Thomas Gleixner Tested-by: Nikhil Agarwal Link: https://lore.kernel.org/r/20240226082816.100872-1-nipun.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/cdx/Makefile | 4 + drivers/cdx/cdx.c | 20 ++++ drivers/cdx/cdx.h | 12 ++ drivers/cdx/cdx_msi.c | 192 ++++++++++++++++++++++++++++++++ drivers/cdx/controller/Kconfig | 1 + drivers/cdx/controller/cdx_controller.c | 25 +++++ drivers/cdx/controller/mc_cdx_pcol.h | 64 +++++++++++ drivers/cdx/controller/mcdi_functions.c | 33 +++++- drivers/cdx/controller/mcdi_functions.h | 33 ++++++ include/linux/cdx/cdx_bus.h | 53 ++++++++- 10 files changed, 434 insertions(+), 3 deletions(-) create mode 100644 drivers/cdx/cdx_msi.c (limited to 'include') diff --git a/drivers/cdx/Makefile b/drivers/cdx/Makefile index 5d1ea482419f..749a3295c2bd 100644 --- a/drivers/cdx/Makefile +++ b/drivers/cdx/Makefile @@ -8,3 +8,7 @@ ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=CDX_BUS obj-$(CONFIG_CDX_BUS) += cdx.o controller/ + +ifdef CONFIG_GENERIC_MSI_IRQ +obj-$(CONFIG_CDX_BUS) += cdx_msi.o +endif diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index b74d76afccb6..236d381dc5f7 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -56,6 +56,7 @@ */ #include +#include #include #include #include @@ -302,8 +303,19 @@ static int cdx_probe(struct device *dev) { struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver); struct cdx_device *cdx_dev = to_cdx_device(dev); + struct cdx_controller *cdx = cdx_dev->cdx; int error; + /* + * Setup MSI device data so that generic MSI alloc/free can + * be used by the device driver. + */ + if (cdx->msi_domain) { + error = msi_setup_device_data(&cdx_dev->dev); + if (error) + return error; + } + error = cdx_drv->probe(cdx_dev); if (error) { dev_err_probe(dev, error, "%s failed\n", __func__); @@ -787,6 +799,7 @@ int cdx_device_add(struct cdx_dev_params *dev_params) /* Populate CDX dev params */ cdx_dev->req_id = dev_params->req_id; + cdx_dev->msi_dev_id = dev_params->msi_dev_id; cdx_dev->vendor = dev_params->vendor; cdx_dev->device = dev_params->device; cdx_dev->subsystem_vendor = dev_params->subsys_vendor; @@ -804,12 +817,19 @@ int cdx_device_add(struct cdx_dev_params *dev_params) cdx_dev->dev.bus = &cdx_bus_type; cdx_dev->dev.dma_mask = &cdx_dev->dma_mask; cdx_dev->dev.release = cdx_device_release; + cdx_dev->msi_write_pending = false; + mutex_init(&cdx_dev->irqchip_lock); /* Set Name */ dev_set_name(&cdx_dev->dev, "cdx-%02x:%02x", ((cdx->id << CDX_CONTROLLER_ID_SHIFT) | (cdx_dev->bus_num & CDX_BUS_NUM_MASK)), cdx_dev->dev_num); + if (cdx->msi_domain) { + cdx_dev->num_msi = dev_params->num_msi; + dev_set_msi_domain(&cdx_dev->dev, cdx->msi_domain); + } + ret = device_add(&cdx_dev->dev); if (ret) { dev_err(&cdx_dev->dev, diff --git a/drivers/cdx/cdx.h b/drivers/cdx/cdx.h index 300ad8be7a34..9c60c04dcf87 100644 --- a/drivers/cdx/cdx.h +++ b/drivers/cdx/cdx.h @@ -25,6 +25,8 @@ * @req_id: Requestor ID associated with CDX device * @class: Class of the CDX Device * @revision: Revision of the CDX device + * @msi_dev_id: MSI device ID associated with CDX device + * @num_msi: Number of MSI's supported by the device */ struct cdx_dev_params { struct cdx_controller *cdx; @@ -40,6 +42,8 @@ struct cdx_dev_params { u32 req_id; u32 class; u8 revision; + u32 msi_dev_id; + u32 num_msi; }; /** @@ -79,4 +83,12 @@ int cdx_device_add(struct cdx_dev_params *dev_params); */ struct device *cdx_bus_add(struct cdx_controller *cdx, u8 bus_num); +/** + * cdx_msi_domain_init - Init the CDX bus MSI domain. + * @dev: Device of the CDX bus controller + * + * Return: CDX MSI domain, NULL on failure + */ +struct irq_domain *cdx_msi_domain_init(struct device *dev); + #endif /* _CDX_H_ */ diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c new file mode 100644 index 000000000000..e55f1716cfcb --- /dev/null +++ b/drivers/cdx/cdx_msi.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD CDX bus driver MSI support + * + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdx.h" + +static void cdx_msi_write_msg(struct irq_data *irq_data, struct msi_msg *msg) +{ + struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); + struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev); + + /* We would not operate on msg here rather we wait for irq_bus_sync_unlock() + * to be called from preemptible task context. + */ + msi_desc->msg = *msg; + cdx_dev->msi_write_pending = true; +} + +static void cdx_msi_write_irq_lock(struct irq_data *irq_data) +{ + struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); + struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev); + + mutex_lock(&cdx_dev->irqchip_lock); +} + +static void cdx_msi_write_irq_unlock(struct irq_data *irq_data) +{ + struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); + struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev); + struct cdx_controller *cdx = cdx_dev->cdx; + struct cdx_device_config dev_config; + + if (!cdx_dev->msi_write_pending) { + mutex_unlock(&cdx_dev->irqchip_lock); + return; + } + + cdx_dev->msi_write_pending = false; + mutex_unlock(&cdx_dev->irqchip_lock); + + dev_config.msi.msi_index = msi_desc->msi_index; + dev_config.msi.data = msi_desc->msg.data; + dev_config.msi.addr = ((u64)(msi_desc->msg.address_hi) << 32) | msi_desc->msg.address_lo; + + /* + * dev_configure() is a controller callback which can interact with + * Firmware or other entities, and can sleep, so invoke this function + * outside of the mutex held region. + */ + dev_config.type = CDX_DEV_MSI_CONF; + if (cdx->ops->dev_configure) + cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config); +} + +int cdx_enable_msi(struct cdx_device *cdx_dev) +{ + struct cdx_controller *cdx = cdx_dev->cdx; + struct cdx_device_config dev_config; + + dev_config.type = CDX_DEV_MSI_ENABLE; + dev_config.msi_enable = true; + if (cdx->ops->dev_configure) { + return cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, + &dev_config); + } + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(cdx_enable_msi); + +void cdx_disable_msi(struct cdx_device *cdx_dev) +{ + struct cdx_controller *cdx = cdx_dev->cdx; + struct cdx_device_config dev_config; + + dev_config.type = CDX_DEV_MSI_ENABLE; + dev_config.msi_enable = false; + if (cdx->ops->dev_configure) + cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config); +} +EXPORT_SYMBOL_GPL(cdx_disable_msi); + +static struct irq_chip cdx_msi_irq_chip = { + .name = "CDX-MSI", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_affinity = msi_domain_set_affinity, + .irq_write_msi_msg = cdx_msi_write_msg, + .irq_bus_lock = cdx_msi_write_irq_lock, + .irq_bus_sync_unlock = cdx_msi_write_irq_unlock +}; + +/* Convert an msi_desc to a unique identifier within the domain. */ +static irq_hw_number_t cdx_domain_calc_hwirq(struct cdx_device *dev, + struct msi_desc *desc) +{ + return ((irq_hw_number_t)dev->msi_dev_id << 10) | desc->msi_index; +} + +static void cdx_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) +{ + arg->desc = desc; + arg->hwirq = cdx_domain_calc_hwirq(to_cdx_device(desc->dev), desc); +} + +static int cdx_msi_prepare(struct irq_domain *msi_domain, + struct device *dev, + int nvec, msi_alloc_info_t *info) +{ + struct cdx_device *cdx_dev = to_cdx_device(dev); + struct device *parent = cdx_dev->cdx->dev; + struct msi_domain_info *msi_info; + u32 dev_id; + int ret; + + /* Retrieve device ID from requestor ID using parent device */ + ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask", + NULL, &dev_id); + if (ret) { + dev_err(dev, "of_map_id failed for MSI: %d\n", ret); + return ret; + } + +#ifdef GENERIC_MSI_DOMAIN_OPS + /* Set the device Id to be passed to the GIC-ITS */ + info->scratchpad[0].ul = dev_id; +#endif + + msi_info = msi_get_domain_info(msi_domain->parent); + + return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info); +} + +static struct msi_domain_ops cdx_msi_ops = { + .msi_prepare = cdx_msi_prepare, + .set_desc = cdx_msi_set_desc +}; + +static struct msi_domain_info cdx_msi_domain_info = { + .ops = &cdx_msi_ops, + .chip = &cdx_msi_irq_chip, + .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS +}; + +struct irq_domain *cdx_msi_domain_init(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct fwnode_handle *fwnode_handle; + struct irq_domain *cdx_msi_domain; + struct device_node *parent_node; + struct irq_domain *parent; + + fwnode_handle = of_node_to_fwnode(np); + + parent_node = of_parse_phandle(np, "msi-map", 1); + if (!parent_node) { + dev_err(dev, "msi-map not present on cdx controller\n"); + return NULL; + } + + parent = irq_find_matching_fwnode(of_node_to_fwnode(parent_node), DOMAIN_BUS_NEXUS); + if (!parent || !msi_get_domain_info(parent)) { + dev_err(dev, "unable to locate ITS domain\n"); + return NULL; + } + + cdx_msi_domain = msi_create_irq_domain(fwnode_handle, &cdx_msi_domain_info, parent); + if (!cdx_msi_domain) { + dev_err(dev, "unable to create CDX-MSI domain\n"); + return NULL; + } + + dev_dbg(dev, "CDX-MSI domain created\n"); + + return cdx_msi_domain; +} +EXPORT_SYMBOL_NS_GPL(cdx_msi_domain_init, CDX_BUS_CONTROLLER); diff --git a/drivers/cdx/controller/Kconfig b/drivers/cdx/controller/Kconfig index 61bf17fbe433..f8e729761aee 100644 --- a/drivers/cdx/controller/Kconfig +++ b/drivers/cdx/controller/Kconfig @@ -9,6 +9,7 @@ if CDX_BUS config CDX_CONTROLLER tristate "CDX bus controller" + select GENERIC_MSI_IRQ select REMOTEPROC select RPMSG help diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c index 85fe4b1c4e5e..112a1541de6d 100644 --- a/drivers/cdx/controller/cdx_controller.c +++ b/drivers/cdx/controller/cdx_controller.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "cdx_controller.h" #include "../cdx.h" @@ -60,9 +61,19 @@ static int cdx_configure_device(struct cdx_controller *cdx, u8 bus_num, u8 dev_num, struct cdx_device_config *dev_config) { + u16 msi_index; int ret = 0; + u32 data; + u64 addr; switch (dev_config->type) { + case CDX_DEV_MSI_CONF: + msi_index = dev_config->msi.msi_index; + data = dev_config->msi.data; + addr = dev_config->msi.addr; + + ret = cdx_mcdi_write_msi(cdx->priv, bus_num, dev_num, msi_index, addr, data); + break; case CDX_DEV_RESET_CONF: ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num); break; @@ -70,6 +81,9 @@ static int cdx_configure_device(struct cdx_controller *cdx, ret = cdx_mcdi_bus_master_enable(cdx->priv, bus_num, dev_num, dev_config->bus_master_enable); break; + case CDX_DEV_MSI_ENABLE: + ret = cdx_mcdi_msi_enable(cdx->priv, bus_num, dev_num, dev_config->msi_enable); + break; default: ret = -EINVAL; } @@ -178,6 +192,14 @@ static int xlnx_cdx_probe(struct platform_device *pdev) cdx->priv = cdx_mcdi; cdx->ops = &cdx_ops; + /* Create MSI domain */ + cdx->msi_domain = cdx_msi_domain_init(&pdev->dev); + if (!cdx->msi_domain) { + dev_err(&pdev->dev, "cdx_msi_domain_init() failed"); + ret = -ENODEV; + goto cdx_msi_fail; + } + ret = cdx_setup_rpmsg(pdev); if (ret) { if (ret != -EPROBE_DEFER) @@ -189,6 +211,8 @@ static int xlnx_cdx_probe(struct platform_device *pdev) return 0; cdx_rpmsg_fail: + irq_domain_remove(cdx->msi_domain); +cdx_msi_fail: kfree(cdx); cdx_alloc_fail: cdx_mcdi_finish(cdx_mcdi); @@ -205,6 +229,7 @@ static int xlnx_cdx_remove(struct platform_device *pdev) cdx_destroy_rpmsg(pdev); + irq_domain_remove(cdx->msi_domain); kfree(cdx); cdx_mcdi_finish(cdx_mcdi); diff --git a/drivers/cdx/controller/mc_cdx_pcol.h b/drivers/cdx/controller/mc_cdx_pcol.h index 2de019406b57..832a44af963e 100644 --- a/drivers/cdx/controller/mc_cdx_pcol.h +++ b/drivers/cdx/controller/mc_cdx_pcol.h @@ -455,6 +455,12 @@ #define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_OFST 84 #define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_LEN 4 +/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2 msgresponse */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN 92 +/* Requester ID used by device for GIC ITS DeviceID */ +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID_OFST 88 +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID_LEN 4 + /***********************************/ /* * MC_CMD_CDX_BUS_DOWN @@ -616,6 +622,64 @@ #define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_LBN 2 #define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_WIDTH 1 +/***********************************/ +/* + * MC_CMD_CDX_DEVICE_WRITE_MSI_MSG + * Populates the MSI message to be used by the hardware to raise the specified + * interrupt vector. Versal-net implementation specific limitations are that + * only 4 CDX devices with MSI interrupt capability are supported and all + * vectors within a device must use the same write address. The command will + * return EINVAL if any of these limitations is violated. + */ +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG 0x9 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_MSGSET 0x9 +#undef MC_CMD_0x9_PRIVILEGE_CTG + +#define MC_CMD_0x9_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN msgrequest */ +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_LEN 28 +/* Device bus number, in range 0 to BUS_COUNT-1 */ +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_BUS_OFST 0 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_BUS_LEN 4 +/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */ +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE_OFST 4 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE_LEN 4 +/* + * Device-relative MSI vector number. Must be < MSI_COUNT reported for the + * device. + */ +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR_OFST 8 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR_LEN 4 +/* Reserved (alignment) */ +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_RESERVED_OFST 12 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_RESERVED_LEN 4 +/* + * MSI address to be used by the hardware. Typically, on ARM systems this + * address is translated by the IOMMU (if enabled) and it is the responsibility + * of the entity managing the IOMMU (APU kernel) to supply the correct IOVA + * here. + */ +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_OFST 16 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LEN 8 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_OFST 16 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_LEN 4 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_LBN 128 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_WIDTH 32 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_OFST 20 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_LEN 4 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_LBN 160 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_WIDTH 32 +/* + * MSI data to be used by the hardware. On versal-net, only the lower 16-bits + * are used, the remaining bits are ignored and should be set to zero. + */ +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA_OFST 24 +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA_LEN 4 + +/* MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_OUT msgresponse */ +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_OUT_LEN 0 + /***********************************/ /* MC_CMD_V2_EXTN - Encapsulation for a v2 extended command */ #define MC_CMD_V2_EXTN 0x7f diff --git a/drivers/cdx/controller/mcdi_functions.c b/drivers/cdx/controller/mcdi_functions.c index b1f530946389..885c69e6ebe5 100644 --- a/drivers/cdx/controller/mcdi_functions.c +++ b/drivers/cdx/controller/mcdi_functions.c @@ -49,7 +49,7 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num, struct cdx_dev_params *dev_params) { - MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN); + MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN); MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_LEN); struct resource *res = &dev_params->res[0]; size_t outlen; @@ -64,7 +64,7 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, if (ret) return ret; - if (outlen != MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN) + if (outlen != MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN) return -EIO; dev_params->bus_num = bus_num; @@ -73,6 +73,9 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, req_id = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID); dev_params->req_id = req_id; + dev_params->msi_dev_id = MCDI_DWORD(outbuf, + CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID); + dev_params->res_count = 0; if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE) != 0) { res[dev_params->res_count].start = @@ -127,6 +130,7 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, dev_params->class = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS) & 0xFFFFFF; dev_params->revision = MCDI_BYTE(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION); + dev_params->num_msi = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT); return 0; } @@ -155,6 +159,24 @@ int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num) return ret; } +int cdx_mcdi_write_msi(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num, + u32 msi_vector, u64 msi_address, u32 msi_data) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_LEN); + int ret; + + MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_BUS, bus_num); + MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE, dev_num); + MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR, msi_vector); + MCDI_SET_QWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS, msi_address); + MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA, msi_data); + + ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_WRITE_MSI_MSG, inbuf, sizeof(inbuf), + NULL, 0, NULL); + + return ret; +} + int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num) { MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_RESET_IN_LEN); @@ -226,3 +248,10 @@ int cdx_mcdi_bus_master_enable(struct cdx_mcdi *cdx, u8 bus_num, return cdx_mcdi_ctrl_flag_set(cdx, bus_num, dev_num, enable, MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_LBN); } + +int cdx_mcdi_msi_enable(struct cdx_mcdi *cdx, u8 bus_num, + u8 dev_num, bool enable) +{ + return cdx_mcdi_ctrl_flag_set(cdx, bus_num, dev_num, enable, + MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_LBN); +} diff --git a/drivers/cdx/controller/mcdi_functions.h b/drivers/cdx/controller/mcdi_functions.h index 258a5462fbe3..b9942affdc6b 100644 --- a/drivers/cdx/controller/mcdi_functions.h +++ b/drivers/cdx/controller/mcdi_functions.h @@ -65,6 +65,26 @@ int cdx_mcdi_bus_enable(struct cdx_mcdi *cdx, u8 bus_num); */ int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num); +/** + * cdx_mcdi_write_msi - Write MSI configuration for CDX device + * @cdx: pointer to MCDI interface. + * @bus_num: Bus number. + * @dev_num: Device number. + * @msi_vector: Device-relative MSI vector number. + * Must be < MSI_COUNT reported for the device. + * @msi_address: MSI address to be used by the hardware. Typically, on ARM + * systems this address is translated by the IOMMU (if enabled) and + * it is the responsibility of the entity managing the IOMMU (APU kernel) + * to supply the correct IOVA here. + * @msi_data: MSI data to be used by the hardware. On versal-net, only the + * lower 16-bits are used, the remaining bits are ignored and should be + * set to zero. + * + * Return: 0 on success, <0 on failure + */ +int cdx_mcdi_write_msi(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num, + u32 msi_vector, u64 msi_address, u32 msi_data); + /** * cdx_mcdi_reset_device - Reset cdx device represented by bus_num:dev_num * @cdx: pointer to MCDI interface. @@ -89,4 +109,17 @@ int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, int cdx_mcdi_bus_master_enable(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num, bool enable); +/** + * cdx_mcdi_msi_enable - Enable/Disable MSIs for cdx device represented + * by bus_num:dev_num + * @cdx: pointer to MCDI interface. + * @bus_num: Bus number. + * @dev_num: Device number. + * @enable: Enable msi's if set, disable otherwise. + * + * Return: 0 on success, <0 on failure + */ +int cdx_mcdi_msi_enable(struct cdx_mcdi *cdx, u8 bus_num, + u8 dev_num, bool enable); + #endif /* CDX_MCDI_FUNCTIONS_H */ diff --git a/include/linux/cdx/cdx_bus.h b/include/linux/cdx/cdx_bus.h index 6355a36a3f81..b57118aaa679 100644 --- a/include/linux/cdx/cdx_bus.h +++ b/include/linux/cdx/cdx_bus.h @@ -12,6 +12,7 @@ #include #include #include +#include #define MAX_CDX_DEV_RESOURCES 4 #define CDX_CONTROLLER_ID_SHIFT 4 @@ -21,13 +22,25 @@ struct cdx_controller; enum { + CDX_DEV_MSI_CONF, CDX_DEV_BUS_MASTER_CONF, CDX_DEV_RESET_CONF, + CDX_DEV_MSI_ENABLE, +}; + +struct cdx_msi_config { + u64 addr; + u32 data; + u16 msi_index; }; struct cdx_device_config { u8 type; - bool bus_master_enable; + union { + struct cdx_msi_config msi; + bool bus_master_enable; + bool msi_enable; + }; }; typedef int (*cdx_bus_enable_cb)(struct cdx_controller *cdx, u8 bus_num); @@ -87,6 +100,7 @@ struct cdx_ops { * struct cdx_controller: CDX controller object * @dev: Linux device associated with the CDX controller. * @priv: private data + * @msi_domain: MSI domain * @id: Controller ID * @controller_registered: controller registered with bus * @ops: CDX controller ops @@ -94,6 +108,7 @@ struct cdx_ops { struct cdx_controller { struct device *dev; void *priv; + struct irq_domain *msi_domain; u32 id; bool controller_registered; struct cdx_ops *ops; @@ -120,9 +135,13 @@ struct cdx_controller { * @req_id: Requestor ID associated with CDX device * @is_bus: Is this bus device * @enabled: is this bus enabled + * @msi_dev_id: MSI Device ID associated with CDX device + * @num_msi: Number of MSI's supported by the device * @driver_override: driver name to force a match; do not set directly, * because core frees it; use driver_set_override() to * set or clear it. + * @irqchip_lock: lock to synchronize irq/msi configuration + * @msi_write_pending: MSI write pending for this device */ struct cdx_device { struct device dev; @@ -144,7 +163,11 @@ struct cdx_device { u32 req_id; bool is_bus; bool enabled; + u32 msi_dev_id; + u32 num_msi; const char *driver_override; + struct mutex irqchip_lock; + bool msi_write_pending; }; #define to_cdx_device(_dev) \ @@ -237,4 +260,32 @@ int cdx_set_master(struct cdx_device *cdx_dev); */ int cdx_clear_master(struct cdx_device *cdx_dev); +#ifdef CONFIG_GENERIC_MSI_IRQ +/** + * cdx_enable_msi - Enable MSI for the CDX device. + * @cdx_dev: device pointer + * + * Return: 0 for success, -errno on failure + */ +int cdx_enable_msi(struct cdx_device *cdx_dev); + +/** + * cdx_disable_msi - Disable MSI for the CDX device. + * @cdx_dev: device pointer + */ +void cdx_disable_msi(struct cdx_device *cdx_dev); + +#else /* CONFIG_GENERIC_MSI_IRQ */ + +static inline int cdx_enable_msi(struct cdx_device *cdx_dev) +{ + return -ENODEV; +} + +static inline void cdx_disable_msi(struct cdx_device *cdx_dev) +{ +} + +#endif /* CONFIG_GENERIC_MSI_IRQ */ + #endif /* _CDX_BUS_H_ */ -- cgit v1.2.3 From 576882ef5e7fce030b65c92b508a0f84ea5a81c2 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Mon, 5 Feb 2024 12:01:37 -0800 Subject: uio: introduce UIO_MEM_DMA_COHERENT type Add a UIO memtype specifically for sharing dma_alloc_coherent memory with userspace, backed by dma_mmap_coherent. This is mainly for the bnx2/bnx2x/bnx2i "cnic" interface, although there are a few other uio drivers which map dma_alloc_coherent memory and will be converted to use dma_mmap_coherent as well. Signed-off-by: Nilesh Javali Signed-off-by: Chris Leech Reviewed-by: Christoph Hellwig Link: https://lore.kernel.org/r/20240205200137.138302-1-cleech@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/uio_driver.h | 13 +++++++++++++ 2 files changed, 60 insertions(+) (limited to 'include') diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 2d572f6c8ec8..bb77de6fa067 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -24,6 +24,7 @@ #include #include #include +#include #define UIO_MAX_DEVICES (1U << MINORBITS) @@ -759,6 +760,49 @@ static int uio_mmap_physical(struct vm_area_struct *vma) vma->vm_page_prot); } +static int uio_mmap_dma_coherent(struct vm_area_struct *vma) +{ + struct uio_device *idev = vma->vm_private_data; + struct uio_mem *mem; + void *addr; + int ret = 0; + int mi; + + mi = uio_find_mem_index(vma); + if (mi < 0) + return -EINVAL; + + mem = idev->info->mem + mi; + + if (mem->addr & ~PAGE_MASK) + return -ENODEV; + if (mem->dma_addr & ~PAGE_MASK) + return -ENODEV; + if (!mem->dma_device) + return -ENODEV; + if (vma->vm_end - vma->vm_start > mem->size) + return -EINVAL; + + dev_warn(mem->dma_device, + "use of UIO_MEM_DMA_COHERENT is highly discouraged"); + + /* + * UIO uses offset to index into the maps for a device. + * We need to clear vm_pgoff for dma_mmap_coherent. + */ + vma->vm_pgoff = 0; + + addr = (void *)mem->addr; + ret = dma_mmap_coherent(mem->dma_device, + vma, + addr, + mem->dma_addr, + vma->vm_end - vma->vm_start); + vma->vm_pgoff = mi; + + return ret; +} + static int uio_mmap(struct file *filep, struct vm_area_struct *vma) { struct uio_listener *listener = filep->private_data; @@ -806,6 +850,9 @@ static int uio_mmap(struct file *filep, struct vm_area_struct *vma) case UIO_MEM_VIRTUAL: ret = uio_mmap_logical(vma); break; + case UIO_MEM_DMA_COHERENT: + ret = uio_mmap_dma_coherent(vma); + break; default: ret = -EINVAL; } diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index 47c5962b876b..18238dc8bfd3 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -28,19 +28,26 @@ struct uio_map; * logical, virtual, or physical & phys_addr_t * should always be large enough to handle any of * the address types) + * @dma_addr: DMA handle set by dma_alloc_coherent, used with + * UIO_MEM_DMA_COHERENT only (@addr should be the + * void * returned from the same dma_alloc_coherent call) * @offs: offset of device memory within the page * @size: size of IO (multiple of page size) * @memtype: type of memory addr points to * @internal_addr: ioremap-ped version of addr, for driver internal use + * @dma_device: device struct that was passed to dma_alloc_coherent, + * used with UIO_MEM_DMA_COHERENT only * @map: for use by the UIO core only. */ struct uio_mem { const char *name; phys_addr_t addr; + dma_addr_t dma_addr; unsigned long offs; resource_size_t size; int memtype; void __iomem *internal_addr; + struct device *dma_device; struct uio_map *map; }; @@ -158,6 +165,12 @@ extern int __must_check #define UIO_MEM_LOGICAL 2 #define UIO_MEM_VIRTUAL 3 #define UIO_MEM_IOVA 4 +/* + * UIO_MEM_DMA_COHERENT exists for legacy drivers that had been getting by with + * improperly mapping DMA coherent allocations through the other modes. + * Do not use in new drivers. + */ +#define UIO_MEM_DMA_COHERENT 5 /* defines for uio_port->porttype */ #define UIO_PORT_NONE 0 -- cgit v1.2.3 From 8dde8fa0cc3edce73c050b9882d06c1a575f6402 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 17 Jan 2024 00:33:07 -0800 Subject: firmware_loader: introduce __free() cleanup hanler Define cleanup handler using facilities from linux/cleanup.h to simplify error handling in code using firmware loader. This will allow writing code like this: int driver_update_firmware(...) { const struct firmware *fw_entry __free(firmware) = NULL; int error; ... error = request_firmware(&fw_entry, fw_name, dev); if (error) { dev_err(dev, "failed to request firmware %s: %d", fw_name, error); return error; } error = check_firmware_valid(fw_entry); if (error) return error; guard(mutex)(&instance->lock); error = use_firmware(instance, fw); if (error) return error; return 0; } Signed-off-by: Dmitry Torokhov Acked-by: Luis Chamberalin Link: https://lore.kernel.org/r/ZaeQw7VXhnirX4pQ@google.com Signed-off-by: Greg Kroah-Hartman --- include/linux/firmware.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 0311858b46ce..f026f8926d79 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -4,6 +4,7 @@ #include #include +#include #include #define FW_ACTION_NOUEVENT 0 @@ -198,4 +199,6 @@ static inline void firmware_upload_unregister(struct fw_upload *fw_upload) int firmware_request_cache(struct device *device, const char *name); +DEFINE_FREE(firmware, struct firmware *, release_firmware(_T)) + #endif -- cgit v1.2.3 From bbf6cfba49a117c502ec5df66d3ab3b485c113f8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Mar 2024 20:00:05 +0200 Subject: driver core: Drop unneeded 'extern' keyword in fwnode.h We do not use 'extern' keyword with functions. Remove the last one mistakenly added to fwnode.h. Reviewed-by: Sakari Ailus Acked-by: Saravana Kannan Acked-by: "Rafael J. Wysocki" Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240301180138.271590-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/fwnode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 2a72f55d26eb..2d23a14857c7 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -209,9 +209,9 @@ static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode, fwnode->flags &= ~FWNODE_FLAG_INITIALIZED; } -extern bool fw_devlink_is_strict(void); int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup); void fwnode_links_purge(struct fwnode_handle *fwnode); void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode); +bool fw_devlink_is_strict(void); #endif -- cgit v1.2.3 From 1c4002aeab3c81afa8a00ae76b1ea38d066e9978 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Mar 2024 20:00:06 +0200 Subject: driver core: Move fw_devlink stuff to where it belongs A few APIs, i.e. fwnode_is_ancestor_of(), fwnode_get_next_parent_dev(), and get_dev_from_fwnode(), that belong specifically to the fw_devlink APIs, may be static, but they are not. Resolve this mess by moving them to the driver/base/core where the all users are being resided and make static. No functional changes intended. Reviewed-by: Sakari Ailus Acked-by: "Rafael J. Wysocki" Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240301180138.271590-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/base/property.c | 56 ---------------------------------------------- include/linux/fwnode.h | 1 - include/linux/property.h | 2 -- 4 files changed, 58 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/drivers/base/core.c b/drivers/base/core.c index 9828da9b933c..35ccd8bb2c9b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1871,6 +1871,7 @@ static void fw_devlink_unblock_consumers(struct device *dev) device_links_write_unlock(); } +#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev) static bool fwnode_init_without_drv(struct fwnode_handle *fwnode) { @@ -1901,6 +1902,63 @@ static bool fwnode_ancestor_init_without_drv(struct fwnode_handle *fwnode) return false; } +/** + * fwnode_is_ancestor_of - Test if @ancestor is ancestor of @child + * @ancestor: Firmware which is tested for being an ancestor + * @child: Firmware which is tested for being the child + * + * A node is considered an ancestor of itself too. + * + * Return: true if @ancestor is an ancestor of @child. Otherwise, returns false. + */ +static bool fwnode_is_ancestor_of(const struct fwnode_handle *ancestor, + const struct fwnode_handle *child) +{ + struct fwnode_handle *parent; + + if (IS_ERR_OR_NULL(ancestor)) + return false; + + if (child == ancestor) + return true; + + fwnode_for_each_parent_node(child, parent) { + if (parent == ancestor) { + fwnode_handle_put(parent); + return true; + } + } + return false; +} + +/** + * fwnode_get_next_parent_dev - Find device of closest ancestor fwnode + * @fwnode: firmware node + * + * Given a firmware node (@fwnode), this function finds its closest ancestor + * firmware node that has a corresponding struct device and returns that struct + * device. + * + * The caller is responsible for calling put_device() on the returned device + * pointer. + * + * Return: a pointer to the device of the @fwnode's closest ancestor. + */ +static struct device *fwnode_get_next_parent_dev(const struct fwnode_handle *fwnode) +{ + struct fwnode_handle *parent; + struct device *dev; + + fwnode_for_each_parent_node(fwnode, parent) { + dev = get_dev_from_fwnode(parent); + if (dev) { + fwnode_handle_put(parent); + return dev; + } + } + return NULL; +} + /** * __fw_devlink_relax_cycles - Relax and mark dependency cycles. * @con: Potential consumer device. diff --git a/drivers/base/property.c b/drivers/base/property.c index a1b01ab42052..afa1bf2b3c5a 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -699,34 +699,6 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode) } EXPORT_SYMBOL_GPL(fwnode_get_next_parent); -/** - * fwnode_get_next_parent_dev - Find device of closest ancestor fwnode - * @fwnode: firmware node - * - * Given a firmware node (@fwnode), this function finds its closest ancestor - * firmware node that has a corresponding struct device and returns that struct - * device. - * - * The caller is responsible for calling put_device() on the returned device - * pointer. - * - * Return: a pointer to the device of the @fwnode's closest ancestor. - */ -struct device *fwnode_get_next_parent_dev(const struct fwnode_handle *fwnode) -{ - struct fwnode_handle *parent; - struct device *dev; - - fwnode_for_each_parent_node(fwnode, parent) { - dev = get_dev_from_fwnode(parent); - if (dev) { - fwnode_handle_put(parent); - return dev; - } - } - return NULL; -} - /** * fwnode_count_parents - Return the number of parents a node has * @fwnode: The node the parents of which are to be counted @@ -773,34 +745,6 @@ struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode, } EXPORT_SYMBOL_GPL(fwnode_get_nth_parent); -/** - * fwnode_is_ancestor_of - Test if @ancestor is ancestor of @child - * @ancestor: Firmware which is tested for being an ancestor - * @child: Firmware which is tested for being the child - * - * A node is considered an ancestor of itself too. - * - * Return: true if @ancestor is an ancestor of @child. Otherwise, returns false. - */ -bool fwnode_is_ancestor_of(const struct fwnode_handle *ancestor, const struct fwnode_handle *child) -{ - struct fwnode_handle *parent; - - if (IS_ERR_OR_NULL(ancestor)) - return false; - - if (child == ancestor) - return true; - - fwnode_for_each_parent_node(child, parent) { - if (parent == ancestor) { - fwnode_handle_put(parent); - return true; - } - } - return false; -} - /** * fwnode_get_next_child_node - Return the next child node handle for a node * @fwnode: Firmware node to find the next child node for. diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 2d23a14857c7..416cbe72f0c7 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -187,7 +187,6 @@ struct fwnode_operations { if (fwnode_has_op(fwnode, op)) \ (fwnode)->ops->op(fwnode, ## __VA_ARGS__); \ } while (false) -#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev) static inline void fwnode_init(struct fwnode_handle *fwnode, const struct fwnode_operations *ops) diff --git a/include/linux/property.h b/include/linux/property.h index e6516d0b7d52..284ff79ebf03 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -156,11 +156,9 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode); for (parent = fwnode_get_parent(fwnode); parent; \ parent = fwnode_get_next_parent(parent)) -struct device *fwnode_get_next_parent_dev(const struct fwnode_handle *fwnode); unsigned int fwnode_count_parents(const struct fwnode_handle *fwn); struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwn, unsigned int depth); -bool fwnode_is_ancestor_of(const struct fwnode_handle *ancestor, const struct fwnode_handle *child); struct fwnode_handle *fwnode_get_next_child_node( const struct fwnode_handle *fwnode, struct fwnode_handle *child); struct fwnode_handle *fwnode_get_next_available_child_node( -- cgit v1.2.3 From 420b104dd116cddd1615588a400b557bf4e436b4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Mar 2024 20:00:07 +0200 Subject: device property: Move enum dev_dma_attr to fwnode.h The struct fwnode_operations defines one of the callback to return enum dev_dma_attr. But this currently is defined in property.h. Move it to the correct location. Reviewed-by: Sakari Ailus Acked-by: "Rafael J. Wysocki" Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240301180138.271590-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/fwnode.h | 6 ++++++ include/linux/property.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 416cbe72f0c7..4228c45d5ccc 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -14,6 +14,12 @@ #include #include +enum dev_dma_attr { + DEV_DMA_NOT_SUPPORTED, + DEV_DMA_NON_COHERENT, + DEV_DMA_COHERENT, +}; + struct fwnode_operations; struct device; diff --git a/include/linux/property.h b/include/linux/property.h index 284ff79ebf03..1f0135e24d00 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -27,12 +27,6 @@ enum dev_prop_type { DEV_PROP_REF, }; -enum dev_dma_attr { - DEV_DMA_NOT_SUPPORTED, - DEV_DMA_NON_COHERENT, - DEV_DMA_COHERENT, -}; - const struct fwnode_handle *__dev_fwnode_const(const struct device *dev); struct fwnode_handle *__dev_fwnode(struct device *dev); #define dev_fwnode(dev) \ -- cgit v1.2.3 From 4dc3d612ee5c3be2a4d1a73ab31bcfaaa850aa19 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Mar 2024 20:00:08 +0200 Subject: device property: Don't use "proxy" headers Update header inclusions to follow IWYU (Include What You Use) principle. Reviewed-by: Sakari Ailus Acked-by: "Rafael J. Wysocki" Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240301180138.271590-5-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/property.c | 11 ++++++----- drivers/base/swnode.c | 13 ++++++++++++- include/linux/fwnode.h | 4 ++-- include/linux/property.h | 1 + 4 files changed, 21 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/base/property.c b/drivers/base/property.c index afa1bf2b3c5a..7324a704a9a1 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -7,15 +7,16 @@ * Mika Westerberg */ -#include +#include +#include #include -#include +#include #include -#include -#include -#include #include #include +#include +#include +#include struct fwnode_handle *__dev_fwnode(struct device *dev) { diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 36512fb75a20..eb6eb25b343b 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -6,10 +6,21 @@ * Author: Heikki Krogerus */ +#include #include -#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include #include "base.h" diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 4228c45d5ccc..80f3cd91b471 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -9,10 +9,10 @@ #ifndef _LINUX_FWNODE_H_ #define _LINUX_FWNODE_H_ -#include -#include #include #include +#include +#include enum dev_dma_attr { DEV_DMA_NOT_SUPPORTED, diff --git a/include/linux/property.h b/include/linux/property.h index 1f0135e24d00..3a1045eb786c 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -11,6 +11,7 @@ #define _LINUX_PROPERTY_H_ #include +#include #include #include #include -- cgit v1.2.3 From 75cde56a5b504d07a64ce0e3f8c7410df70308a3 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 4 Mar 2024 21:04:54 -0800 Subject: driver core: Adds flags param to fwnode_link_add() Allow the callers to set fwnode link flags when adding fwnode links. Signed-off-by: Saravana Kannan Acked-by: "Rafael J. Wysocki" Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20240305050458.1400667-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 5 +++-- drivers/firmware/efi/sysfb_efi.c | 2 +- drivers/of/property.c | 2 +- include/linux/fwnode.h | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/base/core.c b/drivers/base/core.c index 35ccd8bb2c9b..83a6f429bddb 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -92,12 +92,13 @@ static int __fwnode_link_add(struct fwnode_handle *con, return 0; } -int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup) +int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup, + u8 flags) { int ret; mutex_lock(&fwnode_link_lock); - ret = __fwnode_link_add(con, sup, 0); + ret = __fwnode_link_add(con, sup, flags); mutex_unlock(&fwnode_link_lock); return ret; } diff --git a/drivers/firmware/efi/sysfb_efi.c b/drivers/firmware/efi/sysfb_efi.c index 456d0e5eaf78..cc807ed35aed 100644 --- a/drivers/firmware/efi/sysfb_efi.c +++ b/drivers/firmware/efi/sysfb_efi.c @@ -336,7 +336,7 @@ static int efifb_add_links(struct fwnode_handle *fwnode) if (!sup_np) return 0; - fwnode_link_add(fwnode, of_fwnode_handle(sup_np)); + fwnode_link_add(fwnode, of_fwnode_handle(sup_np), 0); of_node_put(sup_np); return 0; diff --git a/drivers/of/property.c b/drivers/of/property.c index b71267c6667c..bce849f21ae2 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1085,7 +1085,7 @@ static void of_link_to_phandle(struct device_node *con_np, tmp_np = of_get_next_parent(tmp_np); } - fwnode_link_add(of_fwnode_handle(con_np), of_fwnode_handle(sup_np)); + fwnode_link_add(of_fwnode_handle(con_np), of_fwnode_handle(sup_np), 0); } /** diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 80f3cd91b471..70d9c40269b9 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -214,7 +214,8 @@ static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode, fwnode->flags &= ~FWNODE_FLAG_INITIALIZED; } -int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup); +int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup, + u8 flags); void fwnode_links_purge(struct fwnode_handle *fwnode); void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode); bool fw_devlink_is_strict(void); -- cgit v1.2.3 From b7e1241d8f77ed64404a5e4450f43a319310fc91 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 4 Mar 2024 21:04:55 -0800 Subject: driver core: Add FWLINK_FLAG_IGNORE to completely ignore a fwnode link A fwnode link between specific supplier-consumer fwnodes can be added multiple times for multiple reasons. If that dependency doesn't exist, deleting the fwnode link once doesn't guarantee that it won't get created again. So, add FWLINK_FLAG_IGNORE flag to mark a fwnode link as one that needs to be completely ignored. Since a fwnode link's flags is an OR of all the flags passed to all the fwnode_link_add() calls to create that specific fwnode link, the FWLINK_FLAG_IGNORE flag is preserved and can be used to mark a fwnode link as on that need to be completely ignored until it is deleted. Signed-off-by: Saravana Kannan Acked-by: "Rafael J. Wysocki" Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20240305050458.1400667-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 9 ++++++++- include/linux/fwnode.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/base/core.c b/drivers/base/core.c index 83a6f429bddb..b93f3c5716ae 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1012,7 +1012,8 @@ static struct fwnode_handle *fwnode_links_check_suppliers( return NULL; list_for_each_entry(link, &fwnode->suppliers, c_hook) - if (!(link->flags & FWLINK_FLAG_CYCLE)) + if (!(link->flags & + (FWLINK_FLAG_CYCLE | FWLINK_FLAG_IGNORE))) return link->supplier; return NULL; @@ -2021,6 +2022,9 @@ static bool __fw_devlink_relax_cycles(struct device *con, } list_for_each_entry(link, &sup_handle->suppliers, c_hook) { + if (link->flags & FWLINK_FLAG_IGNORE) + continue; + if (__fw_devlink_relax_cycles(con, link->supplier)) { __fwnode_link_cycle(link); ret = true; @@ -2099,6 +2103,9 @@ static int fw_devlink_create_devlink(struct device *con, int ret = 0; u32 flags; + if (link->flags & FWLINK_FLAG_IGNORE) + return 0; + if (con->fwnode == link->consumer) flags = fw_devlink_get_flags(link->flags); else diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 70d9c40269b9..0d79070c5a70 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -59,8 +59,10 @@ struct fwnode_handle { * fwnode link flags * * CYCLE: The fwnode link is part of a cycle. Don't defer probe. + * IGNORE: Completely ignore this link, even during cycle detection. */ #define FWLINK_FLAG_CYCLE BIT(0) +#define FWLINK_FLAG_IGNORE BIT(1) struct fwnode_link { struct fwnode_handle *supplier; -- cgit v1.2.3 From 6b6ca096115e5b7a85e8313f4e68a72d52db91b3 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Tue, 5 Mar 2024 15:22:28 -0300 Subject: rtc: class: make rtc_class constant Since commit 43a7206b0963 ("driver core: class: make class_register() take a const *"), the driver core allows for struct class to be in read-only memory, so move the rtc_class structure to be declared at build time placing it into read-only memory, instead of having to be dynamically allocated at boot time. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Link: https://lore.kernel.org/r/20240305-class_cleanup-abelloni-v1-1-944c026137c8@marliere.net Signed-off-by: Alexandre Belloni --- drivers/rtc/class.c | 21 +++++++++++++-------- drivers/rtc/interface.c | 2 +- include/linux/rtc.h | 2 +- kernel/power/suspend_test.c | 2 +- kernel/time/alarmtimer.c | 2 +- 5 files changed, 17 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 921ee1827974..e31fa0ad127e 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -21,7 +21,6 @@ #include "rtc-core.h" static DEFINE_IDA(rtc_ida); -struct class *rtc_class; static void rtc_device_release(struct device *dev) { @@ -199,6 +198,11 @@ static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume); #define RTC_CLASS_DEV_PM_OPS NULL #endif +const struct class rtc_class = { + .name = "rtc", + .pm = RTC_CLASS_DEV_PM_OPS, +}; + /* Ensure the caller will set the id before releasing the device */ static struct rtc_device *rtc_allocate_device(void) { @@ -220,7 +224,7 @@ static struct rtc_device *rtc_allocate_device(void) rtc->irq_freq = 1; rtc->max_user_freq = 64; - rtc->dev.class = rtc_class; + rtc->dev.class = &rtc_class; rtc->dev.groups = rtc_get_dev_attribute_groups(); rtc->dev.release = rtc_device_release; @@ -475,13 +479,14 @@ EXPORT_SYMBOL_GPL(devm_rtc_device_register); static int __init rtc_init(void) { - rtc_class = class_create("rtc"); - if (IS_ERR(rtc_class)) { - pr_err("couldn't create class\n"); - return PTR_ERR(rtc_class); - } - rtc_class->pm = RTC_CLASS_DEV_PM_OPS; + int err; + + err = class_register(&rtc_class); + if (err) + return err; + rtc_dev_init(); + return 0; } subsys_initcall(rtc_init); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 1b63111cdda2..5faafb4aa55c 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -696,7 +696,7 @@ struct rtc_device *rtc_class_open(const char *name) struct device *dev; struct rtc_device *rtc = NULL; - dev = class_find_device_by_name(rtc_class, name); + dev = class_find_device_by_name(&rtc_class, name); if (dev) rtc = to_rtc_device(dev); diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 5f8e438a0312..3f4d315aaec9 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -42,7 +42,7 @@ static inline time64_t rtc_tm_sub(struct rtc_time *lhs, struct rtc_time *rhs) #include #include -extern struct class *rtc_class; +extern const struct class rtc_class; /* * For these RTC methods the device parameter is the physical device diff --git a/kernel/power/suspend_test.c b/kernel/power/suspend_test.c index b663a97f5867..d4856ec61570 100644 --- a/kernel/power/suspend_test.c +++ b/kernel/power/suspend_test.c @@ -201,7 +201,7 @@ static int __init test_suspend(void) } /* RTCs have initialized by now too ... can we use one? */ - dev = class_find_device(rtc_class, NULL, NULL, has_wakealarm); + dev = class_find_device(&rtc_class, NULL, NULL, has_wakealarm); if (dev) { rtc = rtc_class_open(dev_name(dev)); put_device(dev); diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 4657cb8e8b1f..5abfa4390673 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -134,7 +134,7 @@ static struct class_interface alarmtimer_rtc_interface = { static int alarmtimer_rtc_interface_setup(void) { - alarmtimer_rtc_interface.class = rtc_class; + alarmtimer_rtc_interface.class = &rtc_class; return class_interface_register(&alarmtimer_rtc_interface); } static void alarmtimer_rtc_interface_remove(void) -- cgit v1.2.3 From db06ae7ce9fdd3076d81588e80a2a41c3ce82765 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Fri, 1 Mar 2024 11:46:10 +0800 Subject: scsi: ufs: core: Add config_scsi_dev vops comment Add config_scsi_dev vops comment. Signed-off-by: Peter Wang Link: https://lore.kernel.org/r/20240301034610.24928-1-peter.wang@mediatek.com Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- include/ufs/ufshcd.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index cb2afcebbdf5..a35e12f8e68b 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -328,6 +328,7 @@ struct ufs_pwr_mode_info { * @op_runtime_config: called to config Operation and runtime regs Pointers * @get_outstanding_cqs: called to get outstanding completion queues * @config_esi: called to config Event Specific Interrupt + * @config_scsi_dev: called to configure SCSI device parameters */ struct ufs_hba_variant_ops { const char *name; -- cgit v1.2.3 From cb8a2ef0848ca80d67d6d56e2df757cfdf6b3355 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 11 Mar 2024 22:23:47 +0800 Subject: LoongArch: Add ORC stack unwinder support The kernel CONFIG_UNWINDER_ORC option enables the ORC unwinder, which is similar in concept to a DWARF unwinder. The difference is that the format of the ORC data is much simpler than DWARF, which in turn allows the ORC unwinder to be much simpler and faster. The ORC data consists of unwind tables which are generated by objtool. After analyzing all the code paths of a .o file, it determines information about the stack state at each instruction address in the file and outputs that information to the .orc_unwind and .orc_unwind_ip sections. The per-object ORC sections are combined at link time and are sorted and post-processed at boot time. The unwinder uses the resulting data to correlate instruction addresses with their stack states at run time. Most of the logic are similar with x86, in order to get ra info before ra is saved into stack, add ra_reg and ra_offset into orc_entry. At the same time, modify some arch-specific code to silence the objtool warnings. Co-developed-by: Jinyang He Signed-off-by: Jinyang He Co-developed-by: Youling Tang Signed-off-by: Youling Tang Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- arch/loongarch/Kconfig | 2 + arch/loongarch/Kconfig.debug | 11 + arch/loongarch/Makefile | 23 +- arch/loongarch/include/asm/Kbuild | 2 + arch/loongarch/include/asm/bug.h | 1 + arch/loongarch/include/asm/exception.h | 2 + arch/loongarch/include/asm/module.h | 7 + arch/loongarch/include/asm/orc_header.h | 18 + arch/loongarch/include/asm/orc_lookup.h | 31 ++ arch/loongarch/include/asm/orc_types.h | 58 ++++ arch/loongarch/include/asm/stackframe.h | 3 + arch/loongarch/include/asm/unwind.h | 20 +- arch/loongarch/include/asm/unwind_hints.h | 28 ++ arch/loongarch/kernel/Makefile | 4 + arch/loongarch/kernel/entry.S | 5 + arch/loongarch/kernel/fpu.S | 7 + arch/loongarch/kernel/genex.S | 6 + arch/loongarch/kernel/lbt.S | 3 + arch/loongarch/kernel/mcount_dyn.S | 6 + arch/loongarch/kernel/module.c | 22 +- arch/loongarch/kernel/relocate_kernel.S | 7 +- arch/loongarch/kernel/rethook_trampoline.S | 1 + arch/loongarch/kernel/setup.c | 2 + arch/loongarch/kernel/stacktrace.c | 1 + arch/loongarch/kernel/traps.c | 42 ++- arch/loongarch/kernel/unwind_orc.c | 528 +++++++++++++++++++++++++++++ arch/loongarch/kernel/vmlinux.lds.S | 3 + arch/loongarch/kvm/switch.S | 9 +- arch/loongarch/lib/clear_user.S | 3 + arch/loongarch/lib/copy_user.S | 3 + arch/loongarch/lib/memcpy.S | 3 + arch/loongarch/lib/memset.S | 3 + arch/loongarch/mm/tlb.c | 27 +- arch/loongarch/mm/tlbex.S | 9 + arch/loongarch/vdso/Makefile | 1 + include/linux/compiler.h | 9 + scripts/Makefile | 7 +- 37 files changed, 875 insertions(+), 42 deletions(-) create mode 100644 arch/loongarch/include/asm/orc_header.h create mode 100644 arch/loongarch/include/asm/orc_lookup.h create mode 100644 arch/loongarch/include/asm/orc_types.h create mode 100644 arch/loongarch/include/asm/unwind_hints.h create mode 100644 arch/loongarch/kernel/unwind_orc.c (limited to 'include') diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 929f68926b34..8d6725115ac6 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -136,6 +136,7 @@ config LOONGARCH select HAVE_KVM select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI + select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS select HAVE_PCI select HAVE_PERF_EVENTS select HAVE_PERF_REGS @@ -148,6 +149,7 @@ config LOONGARCH select HAVE_SAMPLE_FTRACE_DIRECT select HAVE_SAMPLE_FTRACE_DIRECT_MULTI select HAVE_SETUP_PER_CPU_AREA if NUMA + select HAVE_STACK_VALIDATION if HAVE_OBJTOOL select HAVE_STACKPROTECTOR select HAVE_SYSCALL_TRACEPOINTS select HAVE_TIF_NOHZ diff --git a/arch/loongarch/Kconfig.debug b/arch/loongarch/Kconfig.debug index 8d36aab53008..98d60630c3d4 100644 --- a/arch/loongarch/Kconfig.debug +++ b/arch/loongarch/Kconfig.debug @@ -26,4 +26,15 @@ config UNWINDER_PROLOGUE Some of the addresses it reports may be incorrect (but better than the Guess unwinder). +config UNWINDER_ORC + bool "ORC unwinder" + select OBJTOOL + help + This option enables the ORC (Oops Rewind Capability) unwinder for + unwinding kernel stack traces. It uses a custom data format which is + a simplified version of the DWARF Call Frame Information standard. + + Enabling this option will increase the kernel's runtime memory usage + by roughly 2-4MB, depending on your kernel config. + endchoice diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index 983aa2b1629a..e3bc02fb7fdc 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -26,6 +26,18 @@ endif 32bit-emul = elf32loongarch 64bit-emul = elf64loongarch +ifdef CONFIG_UNWINDER_ORC +orc_hash_h := arch/$(SRCARCH)/include/generated/asm/orc_hash.h +orc_hash_sh := $(srctree)/scripts/orc_hash.sh +targets += $(orc_hash_h) +quiet_cmd_orc_hash = GEN $@ + cmd_orc_hash = mkdir -p $(dir $@); \ + $(CONFIG_SHELL) $(orc_hash_sh) < $< > $@ +$(orc_hash_h): $(srctree)/arch/loongarch/include/asm/orc_types.h $(orc_hash_sh) FORCE + $(call if_changed,orc_hash) +archprepare: $(orc_hash_h) +endif + ifdef CONFIG_DYNAMIC_FTRACE KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY CC_FLAGS_FTRACE := -fpatchable-function-entry=2 @@ -72,8 +84,6 @@ KBUILD_CFLAGS_KERNEL += $(call cc-option,-mdirect-extern-access) KBUILD_CFLAGS_KERNEL += $(call cc-option,-fdirect-access-external-data) KBUILD_AFLAGS_MODULE += $(call cc-option,-fno-direct-access-external-data) KBUILD_CFLAGS_MODULE += $(call cc-option,-fno-direct-access-external-data) -KBUILD_AFLAGS_MODULE += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) -KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) else cflags-y += $(call cc-option,-mno-explicit-relocs) KBUILD_AFLAGS_KERNEL += -Wa,-mla-global-with-pcrel @@ -82,6 +92,15 @@ KBUILD_AFLAGS_MODULE += -Wa,-mla-global-with-abs KBUILD_CFLAGS_MODULE += -fplt -Wa,-mla-global-with-abs,-mla-local-with-abs endif +KBUILD_AFLAGS += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) +KBUILD_CFLAGS += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) +KBUILD_AFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub) +KBUILD_CFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub) + +ifdef CONFIG_OBJTOOL +KBUILD_CFLAGS += -fno-jump-tables +endif + KBUILD_RUSTFLAGS_MODULE += -Crelocation-model=pic ifeq ($(CONFIG_RELOCATABLE),y) diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild index 93783fa24f6e..a97c0edbb866 100644 --- a/arch/loongarch/include/asm/Kbuild +++ b/arch/loongarch/include/asm/Kbuild @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +generated-y += orc_hash.h + generic-y += dma-contiguous.h generic-y += mcs_spinlock.h generic-y += parport.h diff --git a/arch/loongarch/include/asm/bug.h b/arch/loongarch/include/asm/bug.h index d4ca3ba25418..08388876ade4 100644 --- a/arch/loongarch/include/asm/bug.h +++ b/arch/loongarch/include/asm/bug.h @@ -44,6 +44,7 @@ do { \ instrumentation_begin(); \ __BUG_FLAGS(BUGFLAG_WARNING|(flags)); \ + annotate_reachable(); \ instrumentation_end(); \ } while (0) diff --git a/arch/loongarch/include/asm/exception.h b/arch/loongarch/include/asm/exception.h index af74a3fdcad1..c6d20736fd92 100644 --- a/arch/loongarch/include/asm/exception.h +++ b/arch/loongarch/include/asm/exception.h @@ -6,6 +6,8 @@ #include #include +extern void *exception_table[]; + void show_registers(struct pt_regs *regs); asmlinkage void cache_parity_error(void); diff --git a/arch/loongarch/include/asm/module.h b/arch/loongarch/include/asm/module.h index 2ecd82bb64e1..f33f3fd32ecc 100644 --- a/arch/loongarch/include/asm/module.h +++ b/arch/loongarch/include/asm/module.h @@ -6,6 +6,7 @@ #define _ASM_MODULE_H #include +#include #include #define RELA_STACK_DEPTH 16 @@ -21,6 +22,12 @@ struct mod_arch_specific { struct mod_section plt; struct mod_section plt_idx; +#ifdef CONFIG_UNWINDER_ORC + unsigned int num_orcs; + int *orc_unwind_ip; + struct orc_entry *orc_unwind; +#endif + /* For CONFIG_DYNAMIC_FTRACE */ struct plt_entry *ftrace_trampolines; }; diff --git a/arch/loongarch/include/asm/orc_header.h b/arch/loongarch/include/asm/orc_header.h new file mode 100644 index 000000000000..f9d509c3fd70 --- /dev/null +++ b/arch/loongarch/include/asm/orc_header.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _ORC_HEADER_H +#define _ORC_HEADER_H + +#include +#include +#include + +/* + * The header is currently a 20-byte hash of the ORC entry definition; see + * scripts/orc_hash.sh. + */ +#define ORC_HEADER \ + __used __section(".orc_header") __aligned(4) \ + static const u8 orc_header[] = { ORC_HASH } + +#endif /* _ORC_HEADER_H */ diff --git a/arch/loongarch/include/asm/orc_lookup.h b/arch/loongarch/include/asm/orc_lookup.h new file mode 100644 index 000000000000..b02e6357def4 --- /dev/null +++ b/arch/loongarch/include/asm/orc_lookup.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ORC_LOOKUP_H +#define _ORC_LOOKUP_H + +/* + * This is a lookup table for speeding up access to the .orc_unwind table. + * Given an input address offset, the corresponding lookup table entry + * specifies a subset of the .orc_unwind table to search. + * + * Each block represents the end of the previous range and the start of the + * next range. An extra block is added to give the last range an end. + * + * The block size should be a power of 2 to avoid a costly 'div' instruction. + * + * A block size of 256 was chosen because it roughly doubles unwinder + * performance while only adding ~5% to the ORC data footprint. + */ +#define LOOKUP_BLOCK_ORDER 8 +#define LOOKUP_BLOCK_SIZE (1 << LOOKUP_BLOCK_ORDER) + +#ifndef LINKER_SCRIPT + +extern unsigned int orc_lookup[]; +extern unsigned int orc_lookup_end[]; + +#define LOOKUP_START_IP (unsigned long)_stext +#define LOOKUP_STOP_IP (unsigned long)_etext + +#endif /* LINKER_SCRIPT */ + +#endif /* _ORC_LOOKUP_H */ diff --git a/arch/loongarch/include/asm/orc_types.h b/arch/loongarch/include/asm/orc_types.h new file mode 100644 index 000000000000..caf1f71a1057 --- /dev/null +++ b/arch/loongarch/include/asm/orc_types.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ORC_TYPES_H +#define _ORC_TYPES_H + +#include + +/* + * The ORC_REG_* registers are base registers which are used to find other + * registers on the stack. + * + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the + * address of the previous frame: the caller's SP before it called the current + * function. + * + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in + * the current frame. + * + * The most commonly used base registers are SP and FP -- which the previous SP + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous FP is + * usually based on. + * + * The rest of the base registers are needed for special cases like entry code + * and GCC realigned stacks. + */ +#define ORC_REG_UNDEFINED 0 +#define ORC_REG_PREV_SP 1 +#define ORC_REG_SP 2 +#define ORC_REG_FP 3 +#define ORC_REG_MAX 4 + +#define ORC_TYPE_UNDEFINED 0 +#define ORC_TYPE_END_OF_STACK 1 +#define ORC_TYPE_CALL 2 +#define ORC_TYPE_REGS 3 +#define ORC_TYPE_REGS_PARTIAL 4 + +#ifndef __ASSEMBLY__ +/* + * This struct is more or less a vastly simplified version of the DWARF Call + * Frame Information standard. It contains only the necessary parts of DWARF + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the + * unwinder how to find the previous SP and FP (and sometimes entry regs) on + * the stack for a given code address. Each instance of the struct corresponds + * to one or more code locations. + */ +struct orc_entry { + s16 sp_offset; + s16 fp_offset; + s16 ra_offset; + unsigned int sp_reg:4; + unsigned int fp_reg:4; + unsigned int ra_reg:4; + unsigned int type:3; + unsigned int signal:1; +}; +#endif /* __ASSEMBLY__ */ + +#endif /* _ORC_TYPES_H */ diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h index 4fb1e6408b98..45b507a7b06f 100644 --- a/arch/loongarch/include/asm/stackframe.h +++ b/arch/loongarch/include/asm/stackframe.h @@ -13,6 +13,7 @@ #include #include #include +#include /* Make the addition of cfi info a little easier. */ .macro cfi_rel_offset reg offset=0 docfi=0 @@ -162,6 +163,7 @@ li.w t0, CSR_CRMD_WE csrxchg t0, t0, LOONGARCH_CSR_CRMD #endif + UNWIND_HINT_REGS .endm .macro SAVE_ALL docfi=0 @@ -219,6 +221,7 @@ .macro RESTORE_SP_AND_RET docfi=0 cfi_ld sp, PT_R3, \docfi + UNWIND_HINT_FUNC ertn .endm diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h index b9dce87afd2e..40a6763c5aec 100644 --- a/arch/loongarch/include/asm/unwind.h +++ b/arch/loongarch/include/asm/unwind.h @@ -16,6 +16,7 @@ enum unwinder_type { UNWINDER_GUESS, UNWINDER_PROLOGUE, + UNWINDER_ORC, }; struct unwind_state { @@ -24,7 +25,7 @@ struct unwind_state { struct task_struct *task; bool first, error, reset; int graph_idx; - unsigned long sp, pc, ra; + unsigned long sp, fp, pc, ra; }; bool default_next_frame(struct unwind_state *state); @@ -61,14 +62,17 @@ static __always_inline void __unwind_start(struct unwind_state *state, state->sp = regs->regs[3]; state->pc = regs->csr_era; state->ra = regs->regs[1]; + state->fp = regs->regs[22]; } else if (task && task != current) { state->sp = thread_saved_fp(task); state->pc = thread_saved_ra(task); state->ra = 0; + state->fp = 0; } else { state->sp = (unsigned long)__builtin_frame_address(0); state->pc = (unsigned long)__builtin_return_address(0); state->ra = 0; + state->fp = 0; } state->task = task; get_stack_info(state->sp, state->task, &state->stack_info); @@ -77,6 +81,18 @@ static __always_inline void __unwind_start(struct unwind_state *state, static __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state) { - return unwind_done(state) ? 0 : state->pc; + if (unwind_done(state)) + return 0; + + return __kernel_text_address(state->pc) ? state->pc : 0; } + +#ifdef CONFIG_UNWINDER_ORC +void unwind_init(void); +void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size); +#else +static inline void unwind_init(void) {} +static inline void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size) {} +#endif + #endif /* _ASM_UNWIND_H */ diff --git a/arch/loongarch/include/asm/unwind_hints.h b/arch/loongarch/include/asm/unwind_hints.h new file mode 100644 index 000000000000..a01086ad9dde --- /dev/null +++ b/arch/loongarch/include/asm/unwind_hints.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_LOONGARCH_UNWIND_HINTS_H +#define _ASM_LOONGARCH_UNWIND_HINTS_H + +#include +#include + +#ifdef __ASSEMBLY__ + +.macro UNWIND_HINT_UNDEFINED + UNWIND_HINT type=UNWIND_HINT_TYPE_UNDEFINED +.endm + +.macro UNWIND_HINT_END_OF_STACK + UNWIND_HINT type=UNWIND_HINT_TYPE_END_OF_STACK +.endm + +.macro UNWIND_HINT_REGS + UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_REGS +.endm + +.macro UNWIND_HINT_FUNC + UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_CALL +.endm + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_LOONGARCH_UNWIND_HINTS_H */ diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 3c808c680370..3a7620b66bc6 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -3,6 +3,8 @@ # Makefile for the Linux/LoongArch kernel. # +OBJECT_FILES_NON_STANDARD_head.o := y + extra-y := vmlinux.lds obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ @@ -21,6 +23,7 @@ obj-$(CONFIG_ARCH_STRICT_ALIGN) += unaligned.o CFLAGS_module.o += $(call cc-option,-Wno-override-init,) CFLAGS_syscall.o += $(call cc-option,-Wno-override-init,) +CFLAGS_traps.o += $(call cc-option,-Wno-override-init,) CFLAGS_perf_event.o += $(call cc-option,-Wno-override-init,) ifdef CONFIG_FUNCTION_TRACER @@ -62,6 +65,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o +obj-$(CONFIG_UNWINDER_ORC) += unwind_orc.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_regs.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o diff --git a/arch/loongarch/kernel/entry.S b/arch/loongarch/kernel/entry.S index 1ec8e4c4cc2b..48e7e34e355e 100644 --- a/arch/loongarch/kernel/entry.S +++ b/arch/loongarch/kernel/entry.S @@ -14,11 +14,13 @@ #include #include #include +#include .text .cfi_sections .debug_frame .align 5 SYM_CODE_START(handle_syscall) + UNWIND_HINT_UNDEFINED csrrd t0, PERCPU_BASE_KS la.pcrel t1, kernelsp add.d t1, t1, t0 @@ -57,6 +59,7 @@ SYM_CODE_START(handle_syscall) cfi_st fp, PT_R22 SAVE_STATIC + UNWIND_HINT_REGS #ifdef CONFIG_KGDB li.w t1, CSR_CRMD_WE @@ -75,6 +78,7 @@ SYM_CODE_END(handle_syscall) _ASM_NOKPROBE(handle_syscall) SYM_CODE_START(ret_from_fork) + UNWIND_HINT_REGS bl schedule_tail # a0 = struct task_struct *prev move a0, sp bl syscall_exit_to_user_mode @@ -84,6 +88,7 @@ SYM_CODE_START(ret_from_fork) SYM_CODE_END(ret_from_fork) SYM_CODE_START(ret_from_kernel_thread) + UNWIND_HINT_REGS bl schedule_tail # a0 = struct task_struct *prev move a0, s1 jirl ra, s0, 0 diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S index 4382e36ae3d4..69a85f2479fb 100644 --- a/arch/loongarch/kernel/fpu.S +++ b/arch/loongarch/kernel/fpu.S @@ -15,6 +15,7 @@ #include #include #include +#include #define FPU_REG_WIDTH 8 #define LSX_REG_WIDTH 16 @@ -526,3 +527,9 @@ SYM_FUNC_END(_restore_lasx_context) .L_fpu_fault: li.w a0, -EFAULT # failure jr ra + +#ifdef CONFIG_CPU_HAS_LBT +STACK_FRAME_NON_STANDARD _restore_fp +STACK_FRAME_NON_STANDARD _restore_lsx +STACK_FRAME_NON_STANDARD _restore_lasx +#endif diff --git a/arch/loongarch/kernel/genex.S b/arch/loongarch/kernel/genex.S index 2bb3aa2dcfcb..86d5d90ebefe 100644 --- a/arch/loongarch/kernel/genex.S +++ b/arch/loongarch/kernel/genex.S @@ -32,6 +32,7 @@ SYM_FUNC_START(__arch_cpu_idle) SYM_FUNC_END(__arch_cpu_idle) SYM_CODE_START(handle_vint) + UNWIND_HINT_UNDEFINED BACKUP_T0T1 SAVE_ALL la_abs t1, __arch_cpu_idle @@ -49,6 +50,7 @@ SYM_CODE_START(handle_vint) SYM_CODE_END(handle_vint) SYM_CODE_START(except_vec_cex) + UNWIND_HINT_UNDEFINED b cache_parity_error SYM_CODE_END(except_vec_cex) @@ -67,6 +69,7 @@ SYM_CODE_END(except_vec_cex) .macro BUILD_HANDLER exception handler prep .align 5 SYM_CODE_START(handle_\exception) + UNWIND_HINT_UNDEFINED 666: BACKUP_T0T1 SAVE_ALL @@ -77,7 +80,9 @@ SYM_CODE_END(except_vec_cex) 668: RESTORE_ALL_AND_RET SYM_CODE_END(handle_\exception) + .pushsection ".data", "aw", %progbits SYM_DATA(unwind_hint_\exception, .word 668b - 666b) + .popsection .endm BUILD_HANDLER ade ade badv @@ -94,6 +99,7 @@ SYM_CODE_END(except_vec_cex) BUILD_HANDLER reserved reserved none /* others */ SYM_CODE_START(handle_sys) + UNWIND_HINT_UNDEFINED la_abs t0, handle_syscall jr t0 SYM_CODE_END(handle_sys) diff --git a/arch/loongarch/kernel/lbt.S b/arch/loongarch/kernel/lbt.S index 9c75120a26d8..001f061d226a 100644 --- a/arch/loongarch/kernel/lbt.S +++ b/arch/loongarch/kernel/lbt.S @@ -11,6 +11,7 @@ #include #include #include +#include #define SCR_REG_WIDTH 8 @@ -153,3 +154,5 @@ SYM_FUNC_END(_restore_ftop_context) .L_lbt_fault: li.w a0, -EFAULT # failure jr ra + +STACK_FRAME_NON_STANDARD _restore_ftop_context diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S index 482aa553aa2d..0c65cf09110c 100644 --- a/arch/loongarch/kernel/mcount_dyn.S +++ b/arch/loongarch/kernel/mcount_dyn.S @@ -73,6 +73,7 @@ SYM_FUNC_START(ftrace_stub) SYM_FUNC_END(ftrace_stub) SYM_CODE_START(ftrace_common) + UNWIND_HINT_UNDEFINED PTR_ADDI a0, ra, -8 /* arg0: ip */ move a1, t0 /* arg1: parent_ip */ la.pcrel t1, function_trace_op @@ -113,12 +114,14 @@ ftrace_common_return: SYM_CODE_END(ftrace_common) SYM_CODE_START(ftrace_caller) + UNWIND_HINT_UNDEFINED ftrace_regs_entry allregs=0 b ftrace_common SYM_CODE_END(ftrace_caller) #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS SYM_CODE_START(ftrace_regs_caller) + UNWIND_HINT_UNDEFINED ftrace_regs_entry allregs=1 b ftrace_common SYM_CODE_END(ftrace_regs_caller) @@ -126,6 +129,7 @@ SYM_CODE_END(ftrace_regs_caller) #ifdef CONFIG_FUNCTION_GRAPH_TRACER SYM_CODE_START(ftrace_graph_caller) + UNWIND_HINT_UNDEFINED PTR_L a0, sp, PT_ERA PTR_ADDI a0, a0, -8 /* arg0: self_addr */ PTR_ADDI a1, sp, PT_R1 /* arg1: parent */ @@ -134,6 +138,7 @@ SYM_CODE_START(ftrace_graph_caller) SYM_CODE_END(ftrace_graph_caller) SYM_CODE_START(return_to_handler) + UNWIND_HINT_UNDEFINED /* Save return value regs */ PTR_ADDI sp, sp, -FGRET_REGS_SIZE PTR_S a0, sp, FGRET_REGS_A0 @@ -155,6 +160,7 @@ SYM_CODE_END(return_to_handler) #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS SYM_CODE_START(ftrace_stub_direct_tramp) + UNWIND_HINT_UNDEFINED jr t0 SYM_CODE_END(ftrace_stub_direct_tramp) #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c index b13b2858fe39..c7d0338d12c1 100644 --- a/arch/loongarch/kernel/module.c +++ b/arch/loongarch/kernel/module.c @@ -20,6 +20,7 @@ #include #include #include +#include static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top) { @@ -515,15 +516,28 @@ static void module_init_ftrace_plt(const Elf_Ehdr *hdr, int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod) { - const Elf_Shdr *s, *se; const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + const Elf_Shdr *s, *alt = NULL, *orc = NULL, *orc_ip = NULL, *ftrace = NULL; - for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) { + for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { if (!strcmp(".altinstructions", secstrs + s->sh_name)) - apply_alternatives((void *)s->sh_addr, (void *)s->sh_addr + s->sh_size); + alt = s; + if (!strcmp(".orc_unwind", secstrs + s->sh_name)) + orc = s; + if (!strcmp(".orc_unwind_ip", secstrs + s->sh_name)) + orc_ip = s; if (!strcmp(".ftrace_trampoline", secstrs + s->sh_name)) - module_init_ftrace_plt(hdr, s, mod); + ftrace = s; } + if (alt) + apply_alternatives((void *)alt->sh_addr, (void *)alt->sh_addr + alt->sh_size); + + if (orc && orc_ip) + unwind_module_init(mod, (void *)orc_ip->sh_addr, orc_ip->sh_size, (void *)orc->sh_addr, orc->sh_size); + + if (ftrace) + module_init_ftrace_plt(hdr, ftrace, mod); + return 0; } diff --git a/arch/loongarch/kernel/relocate_kernel.S b/arch/loongarch/kernel/relocate_kernel.S index f49f6b053763..84e6de2fd973 100644 --- a/arch/loongarch/kernel/relocate_kernel.S +++ b/arch/loongarch/kernel/relocate_kernel.S @@ -15,6 +15,7 @@ #include SYM_CODE_START(relocate_new_kernel) + UNWIND_HINT_UNDEFINED /* * a0: EFI boot flag for the new kernel * a1: Command line pointer for the new kernel @@ -90,6 +91,7 @@ SYM_CODE_END(relocate_new_kernel) * then start at the entry point from LOONGARCH_IOCSR_MBUF0. */ SYM_CODE_START(kexec_smp_wait) + UNWIND_HINT_UNDEFINED 1: li.w t0, 0x100 /* wait for init loop */ 2: addi.w t0, t0, -1 /* limit mailbox access */ bnez t0, 2b @@ -106,6 +108,5 @@ SYM_CODE_END(kexec_smp_wait) relocate_new_kernel_end: -SYM_DATA_START(relocate_new_kernel_size) - PTR relocate_new_kernel_end - relocate_new_kernel -SYM_DATA_END(relocate_new_kernel_size) + .section ".data" +SYM_DATA(relocate_new_kernel_size, .long relocate_new_kernel_end - relocate_new_kernel) diff --git a/arch/loongarch/kernel/rethook_trampoline.S b/arch/loongarch/kernel/rethook_trampoline.S index bd5772c96338..d4ceb2fa2a5c 100644 --- a/arch/loongarch/kernel/rethook_trampoline.S +++ b/arch/loongarch/kernel/rethook_trampoline.S @@ -76,6 +76,7 @@ .endm SYM_CODE_START(arch_rethook_trampoline) + UNWIND_HINT_UNDEFINED addi.d sp, sp, -PT_SIZE save_all_base_regs diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 634ef17fd38b..7bf9afaeea00 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -47,6 +47,7 @@ #include #include #include +#include #define SMBIOS_BIOSSIZE_OFFSET 0x09 #define SMBIOS_BIOSEXTERN_OFFSET 0x13 @@ -587,6 +588,7 @@ static void __init prefill_possible_map(void) void __init setup_arch(char **cmdline_p) { cpu_probe(); + unwind_init(); init_environ(); efi_init(); diff --git a/arch/loongarch/kernel/stacktrace.c b/arch/loongarch/kernel/stacktrace.c index f623feb2129f..eaec82e02c92 100644 --- a/arch/loongarch/kernel/stacktrace.c +++ b/arch/loongarch/kernel/stacktrace.c @@ -29,6 +29,7 @@ void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, regs->csr_era = thread_saved_ra(task); } regs->regs[1] = 0; + regs->regs[22] = 0; } for (unwind_start(&state, task, regs); diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index aebfc3733a76..f9f4eb00c92e 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -53,6 +53,32 @@ #include "access-helper.h" +void *exception_table[EXCCODE_INT_START] = { + [0 ... EXCCODE_INT_START - 1] = handle_reserved, + + [EXCCODE_TLBI] = handle_tlb_load, + [EXCCODE_TLBL] = handle_tlb_load, + [EXCCODE_TLBS] = handle_tlb_store, + [EXCCODE_TLBM] = handle_tlb_modify, + [EXCCODE_TLBNR] = handle_tlb_protect, + [EXCCODE_TLBNX] = handle_tlb_protect, + [EXCCODE_TLBPE] = handle_tlb_protect, + [EXCCODE_ADE] = handle_ade, + [EXCCODE_ALE] = handle_ale, + [EXCCODE_BCE] = handle_bce, + [EXCCODE_SYS] = handle_sys, + [EXCCODE_BP] = handle_bp, + [EXCCODE_INE] = handle_ri, + [EXCCODE_IPE] = handle_ri, + [EXCCODE_FPDIS] = handle_fpu, + [EXCCODE_LSXDIS] = handle_lsx, + [EXCCODE_LASXDIS] = handle_lasx, + [EXCCODE_FPE] = handle_fpe, + [EXCCODE_WATCH] = handle_watch, + [EXCCODE_BTDIS] = handle_lbt, +}; +EXPORT_SYMBOL_GPL(exception_table); + static void show_backtrace(struct task_struct *task, const struct pt_regs *regs, const char *loglvl, bool user) { @@ -1150,19 +1176,9 @@ void __init trap_init(void) for (i = EXCCODE_INT_START; i <= EXCCODE_INT_END; i++) set_handler(i * VECSIZE, handle_vint, VECSIZE); - set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE); - set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE); - set_handler(EXCCODE_BCE * VECSIZE, handle_bce, VECSIZE); - set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE); - set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE); - set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE); - set_handler(EXCCODE_IPE * VECSIZE, handle_ri, VECSIZE); - set_handler(EXCCODE_FPDIS * VECSIZE, handle_fpu, VECSIZE); - set_handler(EXCCODE_LSXDIS * VECSIZE, handle_lsx, VECSIZE); - set_handler(EXCCODE_LASXDIS * VECSIZE, handle_lasx, VECSIZE); - set_handler(EXCCODE_FPE * VECSIZE, handle_fpe, VECSIZE); - set_handler(EXCCODE_BTDIS * VECSIZE, handle_lbt, VECSIZE); - set_handler(EXCCODE_WATCH * VECSIZE, handle_watch, VECSIZE); + /* Set exception vector handler */ + for (i = EXCCODE_ADE; i <= EXCCODE_BTDIS; i++) + set_handler(i * VECSIZE, exception_table[i], VECSIZE); cache_error_setup(); diff --git a/arch/loongarch/kernel/unwind_orc.c b/arch/loongarch/kernel/unwind_orc.c new file mode 100644 index 000000000000..b25722876331 --- /dev/null +++ b/arch/loongarch/kernel/unwind_orc.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ORC_HEADER; + +#define orc_warn(fmt, ...) \ + printk_deferred_once(KERN_WARNING "WARNING: " fmt, ##__VA_ARGS__) + +extern int __start_orc_unwind_ip[]; +extern int __stop_orc_unwind_ip[]; +extern struct orc_entry __start_orc_unwind[]; +extern struct orc_entry __stop_orc_unwind[]; + +static bool orc_init __ro_after_init; +static unsigned int lookup_num_blocks __ro_after_init; + +/* Fake frame pointer entry -- used as a fallback for generated code */ +static struct orc_entry orc_fp_entry = { + .sp_reg = ORC_REG_FP, + .sp_offset = 16, + .fp_reg = ORC_REG_PREV_SP, + .fp_offset = -16, + .ra_reg = ORC_REG_PREV_SP, + .ra_offset = -8, + .type = ORC_TYPE_CALL +}; + +/* + * If we crash with IP==0, the last successfully executed instruction + * was probably an indirect function call with a NULL function pointer, + * and we don't have unwind information for NULL. + * This hardcoded ORC entry for IP==0 allows us to unwind from a NULL function + * pointer into its parent and then continue normally from there. + */ +static struct orc_entry orc_null_entry = { + .sp_reg = ORC_REG_SP, + .sp_offset = sizeof(long), + .fp_reg = ORC_REG_UNDEFINED, + .type = ORC_TYPE_CALL +}; + +static inline unsigned long orc_ip(const int *ip) +{ + return (unsigned long)ip + *ip; +} + +static struct orc_entry *__orc_find(int *ip_table, struct orc_entry *u_table, + unsigned int num_entries, unsigned long ip) +{ + int *first = ip_table; + int *mid = first, *found = first; + int *last = ip_table + num_entries - 1; + + if (!num_entries) + return NULL; + + /* + * Do a binary range search to find the rightmost duplicate of a given + * starting address. Some entries are section terminators which are + * "weak" entries for ensuring there are no gaps. They should be + * ignored when they conflict with a real entry. + */ + while (first <= last) { + mid = first + ((last - first) / 2); + + if (orc_ip(mid) <= ip) { + found = mid; + first = mid + 1; + } else + last = mid - 1; + } + + return u_table + (found - ip_table); +} + +#ifdef CONFIG_MODULES +static struct orc_entry *orc_module_find(unsigned long ip) +{ + struct module *mod; + + mod = __module_address(ip); + if (!mod || !mod->arch.orc_unwind || !mod->arch.orc_unwind_ip) + return NULL; + + return __orc_find(mod->arch.orc_unwind_ip, mod->arch.orc_unwind, mod->arch.num_orcs, ip); +} +#else +static struct orc_entry *orc_module_find(unsigned long ip) +{ + return NULL; +} +#endif + +#ifdef CONFIG_DYNAMIC_FTRACE +static struct orc_entry *orc_find(unsigned long ip); + +/* + * Ftrace dynamic trampolines do not have orc entries of their own. + * But they are copies of the ftrace entries that are static and + * defined in ftrace_*.S, which do have orc entries. + * + * If the unwinder comes across a ftrace trampoline, then find the + * ftrace function that was used to create it, and use that ftrace + * function's orc entry, as the placement of the return code in + * the stack will be identical. + */ +static struct orc_entry *orc_ftrace_find(unsigned long ip) +{ + struct ftrace_ops *ops; + unsigned long tramp_addr, offset; + + ops = ftrace_ops_trampoline(ip); + if (!ops) + return NULL; + + /* Set tramp_addr to the start of the code copied by the trampoline */ + if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) + tramp_addr = (unsigned long)ftrace_regs_caller; + else + tramp_addr = (unsigned long)ftrace_caller; + + /* Now place tramp_addr to the location within the trampoline ip is at */ + offset = ip - ops->trampoline; + tramp_addr += offset; + + /* Prevent unlikely recursion */ + if (ip == tramp_addr) + return NULL; + + return orc_find(tramp_addr); +} +#else +static struct orc_entry *orc_ftrace_find(unsigned long ip) +{ + return NULL; +} +#endif + +static struct orc_entry *orc_find(unsigned long ip) +{ + static struct orc_entry *orc; + + if (ip == 0) + return &orc_null_entry; + + /* For non-init vmlinux addresses, use the fast lookup table: */ + if (ip >= LOOKUP_START_IP && ip < LOOKUP_STOP_IP) { + unsigned int idx, start, stop; + + idx = (ip - LOOKUP_START_IP) / LOOKUP_BLOCK_SIZE; + + if (unlikely((idx >= lookup_num_blocks-1))) { + orc_warn("WARNING: bad lookup idx: idx=%u num=%u ip=%pB\n", + idx, lookup_num_blocks, (void *)ip); + return NULL; + } + + start = orc_lookup[idx]; + stop = orc_lookup[idx + 1] + 1; + + if (unlikely((__start_orc_unwind + start >= __stop_orc_unwind) || + (__start_orc_unwind + stop > __stop_orc_unwind))) { + orc_warn("WARNING: bad lookup value: idx=%u num=%u start=%u stop=%u ip=%pB\n", + idx, lookup_num_blocks, start, stop, (void *)ip); + return NULL; + } + + return __orc_find(__start_orc_unwind_ip + start, + __start_orc_unwind + start, stop - start, ip); + } + + /* vmlinux .init slow lookup: */ + if (is_kernel_inittext(ip)) + return __orc_find(__start_orc_unwind_ip, __start_orc_unwind, + __stop_orc_unwind_ip - __start_orc_unwind_ip, ip); + + /* Module lookup: */ + orc = orc_module_find(ip); + if (orc) + return orc; + + return orc_ftrace_find(ip); +} + +#ifdef CONFIG_MODULES + +static DEFINE_MUTEX(sort_mutex); +static int *cur_orc_ip_table = __start_orc_unwind_ip; +static struct orc_entry *cur_orc_table = __start_orc_unwind; + +static void orc_sort_swap(void *_a, void *_b, int size) +{ + int delta = _b - _a; + int *a = _a, *b = _b, tmp; + struct orc_entry *orc_a, *orc_b; + + /* Swap the .orc_unwind_ip entries: */ + tmp = *a; + *a = *b + delta; + *b = tmp - delta; + + /* Swap the corresponding .orc_unwind entries: */ + orc_a = cur_orc_table + (a - cur_orc_ip_table); + orc_b = cur_orc_table + (b - cur_orc_ip_table); + swap(*orc_a, *orc_b); +} + +static int orc_sort_cmp(const void *_a, const void *_b) +{ + const int *a = _a, *b = _b; + unsigned long a_val = orc_ip(a); + unsigned long b_val = orc_ip(b); + struct orc_entry *orc_a; + + if (a_val > b_val) + return 1; + if (a_val < b_val) + return -1; + + /* + * The "weak" section terminator entries need to always be first + * to ensure the lookup code skips them in favor of real entries. + * These terminator entries exist to handle any gaps created by + * whitelisted .o files which didn't get objtool generation. + */ + orc_a = cur_orc_table + (a - cur_orc_ip_table); + + return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1; +} + +void unwind_module_init(struct module *mod, void *_orc_ip, size_t orc_ip_size, + void *_orc, size_t orc_size) +{ + int *orc_ip = _orc_ip; + struct orc_entry *orc = _orc; + unsigned int num_entries = orc_ip_size / sizeof(int); + + WARN_ON_ONCE(orc_ip_size % sizeof(int) != 0 || + orc_size % sizeof(*orc) != 0 || + num_entries != orc_size / sizeof(*orc)); + + /* + * The 'cur_orc_*' globals allow the orc_sort_swap() callback to + * associate an .orc_unwind_ip table entry with its corresponding + * .orc_unwind entry so they can both be swapped. + */ + mutex_lock(&sort_mutex); + cur_orc_ip_table = orc_ip; + cur_orc_table = orc; + sort(orc_ip, num_entries, sizeof(int), orc_sort_cmp, orc_sort_swap); + mutex_unlock(&sort_mutex); + + mod->arch.orc_unwind_ip = orc_ip; + mod->arch.orc_unwind = orc; + mod->arch.num_orcs = num_entries; +} +#endif + +void __init unwind_init(void) +{ + int i; + size_t orc_size = (void *)__stop_orc_unwind - (void *)__start_orc_unwind; + size_t orc_ip_size = (void *)__stop_orc_unwind_ip - (void *)__start_orc_unwind_ip; + size_t num_entries = orc_ip_size / sizeof(int); + struct orc_entry *orc; + + if (!num_entries || orc_ip_size % sizeof(int) != 0 || + orc_size % sizeof(struct orc_entry) != 0 || + num_entries != orc_size / sizeof(struct orc_entry)) { + orc_warn("WARNING: Bad or missing .orc_unwind table. Disabling unwinder.\n"); + return; + } + + /* + * Note, the orc_unwind and orc_unwind_ip tables were already + * sorted at build time via the 'sorttable' tool. + * It's ready for binary search straight away, no need to sort it. + */ + + /* Initialize the fast lookup table: */ + lookup_num_blocks = orc_lookup_end - orc_lookup; + for (i = 0; i < lookup_num_blocks-1; i++) { + orc = __orc_find(__start_orc_unwind_ip, __start_orc_unwind, + num_entries, LOOKUP_START_IP + (LOOKUP_BLOCK_SIZE * i)); + if (!orc) { + orc_warn("WARNING: Corrupt .orc_unwind table. Disabling unwinder.\n"); + return; + } + + orc_lookup[i] = orc - __start_orc_unwind; + } + + /* Initialize the ending block: */ + orc = __orc_find(__start_orc_unwind_ip, __start_orc_unwind, num_entries, LOOKUP_STOP_IP); + if (!orc) { + orc_warn("WARNING: Corrupt .orc_unwind table. Disabling unwinder.\n"); + return; + } + orc_lookup[lookup_num_blocks-1] = orc - __start_orc_unwind; + + orc_init = true; +} + +static inline bool on_stack(struct stack_info *info, unsigned long addr, size_t len) +{ + unsigned long begin = info->begin; + unsigned long end = info->end; + + return (info->type != STACK_TYPE_UNKNOWN && + addr >= begin && addr < end && addr + len > begin && addr + len <= end); +} + +static bool stack_access_ok(struct unwind_state *state, unsigned long addr, size_t len) +{ + struct stack_info *info = &state->stack_info; + + if (on_stack(info, addr, len)) + return true; + + return !get_stack_info(addr, state->task, info) && on_stack(info, addr, len); +} + +unsigned long unwind_get_return_address(struct unwind_state *state) +{ + return __unwind_get_return_address(state); +} +EXPORT_SYMBOL_GPL(unwind_get_return_address); + +void unwind_start(struct unwind_state *state, struct task_struct *task, + struct pt_regs *regs) +{ + __unwind_start(state, task, regs); + state->type = UNWINDER_ORC; + if (!unwind_done(state) && !__kernel_text_address(state->pc)) + unwind_next_frame(state); +} +EXPORT_SYMBOL_GPL(unwind_start); + +static bool is_entry_func(unsigned long addr) +{ + extern u32 kernel_entry; + extern u32 kernel_entry_end; + + return addr >= (unsigned long)&kernel_entry && addr < (unsigned long)&kernel_entry_end; +} + +static inline unsigned long bt_address(unsigned long ra) +{ + extern unsigned long eentry; + + if (__kernel_text_address(ra)) + return ra; + + if (__module_text_address(ra)) + return ra; + + if (ra >= eentry && ra < eentry + EXCCODE_INT_END * VECSIZE) { + unsigned long func; + unsigned long type = (ra - eentry) / VECSIZE; + unsigned long offset = (ra - eentry) % VECSIZE; + + switch (type) { + case 0 ... EXCCODE_INT_START - 1: + func = (unsigned long)exception_table[type]; + break; + case EXCCODE_INT_START ... EXCCODE_INT_END: + func = (unsigned long)handle_vint; + break; + default: + func = (unsigned long)handle_reserved; + break; + } + + return func + offset; + } + + return ra; +} + +bool unwind_next_frame(struct unwind_state *state) +{ + unsigned long *p, pc; + struct pt_regs *regs; + struct orc_entry *orc; + struct stack_info *info = &state->stack_info; + + if (unwind_done(state)) + return false; + + /* Don't let modules unload while we're reading their ORC data. */ + preempt_disable(); + + if (is_entry_func(state->pc)) + goto end; + + orc = orc_find(state->pc); + if (!orc) { + /* + * As a fallback, try to assume this code uses a frame pointer. + * This is useful for generated code, like BPF, which ORC + * doesn't know about. This is just a guess, so the rest of + * the unwind is no longer considered reliable. + */ + orc = &orc_fp_entry; + state->error = true; + } else { + if (orc->type == ORC_TYPE_UNDEFINED) + goto err; + + if (orc->type == ORC_TYPE_END_OF_STACK) + goto end; + } + + switch (orc->sp_reg) { + case ORC_REG_SP: + if (info->type == STACK_TYPE_IRQ && state->sp == info->end) + orc->type = ORC_TYPE_REGS; + else + state->sp = state->sp + orc->sp_offset; + break; + case ORC_REG_FP: + state->sp = state->fp; + break; + default: + orc_warn("unknown SP base reg %d at %pB\n", orc->sp_reg, (void *)state->pc); + goto err; + } + + switch (orc->fp_reg) { + case ORC_REG_PREV_SP: + p = (unsigned long *)(state->sp + orc->fp_offset); + if (!stack_access_ok(state, (unsigned long)p, sizeof(unsigned long))) + goto err; + + state->fp = *p; + break; + case ORC_REG_UNDEFINED: + /* Nothing. */ + break; + default: + orc_warn("unknown FP base reg %d at %pB\n", orc->fp_reg, (void *)state->pc); + goto err; + } + + switch (orc->type) { + case ORC_TYPE_CALL: + if (orc->ra_reg == ORC_REG_PREV_SP) { + p = (unsigned long *)(state->sp + orc->ra_offset); + if (!stack_access_ok(state, (unsigned long)p, sizeof(unsigned long))) + goto err; + + pc = unwind_graph_addr(state, *p, state->sp); + pc -= LOONGARCH_INSN_SIZE; + } else if (orc->ra_reg == ORC_REG_UNDEFINED) { + if (!state->ra || state->ra == state->pc) + goto err; + + pc = unwind_graph_addr(state, state->ra, state->sp); + pc -= LOONGARCH_INSN_SIZE; + state->ra = 0; + } else { + orc_warn("unknown ra base reg %d at %pB\n", orc->ra_reg, (void *)state->pc); + goto err; + } + break; + case ORC_TYPE_REGS: + if (info->type == STACK_TYPE_IRQ && state->sp == info->end) + regs = (struct pt_regs *)info->next_sp; + else + regs = (struct pt_regs *)state->sp; + + if (!stack_access_ok(state, (unsigned long)regs, sizeof(*regs))) + goto err; + + if ((info->end == (unsigned long)regs + sizeof(*regs)) && + !regs->regs[3] && !regs->regs[1]) + goto end; + + if (user_mode(regs)) + goto end; + + pc = regs->csr_era; + if (!__kernel_text_address(pc)) + goto err; + + state->sp = regs->regs[3]; + state->ra = regs->regs[1]; + state->fp = regs->regs[22]; + get_stack_info(state->sp, state->task, info); + + break; + default: + orc_warn("unknown .orc_unwind entry type %d at %pB\n", orc->type, (void *)state->pc); + goto err; + } + + state->pc = bt_address(pc); + if (!state->pc) { + pr_err("cannot find unwind pc at %pK\n", (void *)pc); + goto err; + } + + if (!__kernel_text_address(state->pc)) + goto err; + + preempt_enable(); + return true; + +err: + state->error = true; + +end: + preempt_enable(); + state->stack_info.type = STACK_TYPE_UNKNOWN; + return false; +} +EXPORT_SYMBOL_GPL(unwind_next_frame); diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index a5d0cd2035da..e8e97dbf9ca4 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -2,6 +2,7 @@ #include #include #include +#include #define PAGE_SIZE _PAGE_SIZE #define RO_EXCEPTION_TABLE_ALIGN 4 @@ -122,6 +123,8 @@ SECTIONS } #endif + ORC_UNWIND_TABLE + .sdata : { *(.sdata) } diff --git a/arch/loongarch/kvm/switch.S b/arch/loongarch/kvm/switch.S index ba976509bfe8..1fcc4b7eda32 100644 --- a/arch/loongarch/kvm/switch.S +++ b/arch/loongarch/kvm/switch.S @@ -8,7 +8,7 @@ #include #include #include -#include +#include #define HGPR_OFFSET(x) (PT_R0 + 8*x) #define GGPR_OFFSET(x) (KVM_ARCH_GGPR + 8*x) @@ -112,6 +112,7 @@ .text .cfi_sections .debug_frame SYM_CODE_START(kvm_exc_entry) + UNWIND_HINT_UNDEFINED csrwr a2, KVM_TEMP_KS csrrd a2, KVM_VCPU_KS addi.d a2, a2, KVM_VCPU_ARCH @@ -279,3 +280,9 @@ SYM_FUNC_END(kvm_restore_lasx) .section ".rodata" SYM_DATA(kvm_exception_size, .quad kvm_exc_entry_end - kvm_exc_entry) SYM_DATA(kvm_enter_guest_size, .quad kvm_enter_guest_end - kvm_enter_guest) + +#ifdef CONFIG_CPU_HAS_LBT +STACK_FRAME_NON_STANDARD kvm_restore_fpu +STACK_FRAME_NON_STANDARD kvm_restore_lsx +STACK_FRAME_NON_STANDARD kvm_restore_lasx +#endif diff --git a/arch/loongarch/lib/clear_user.S b/arch/loongarch/lib/clear_user.S index be741544e62b..7a0db643b286 100644 --- a/arch/loongarch/lib/clear_user.S +++ b/arch/loongarch/lib/clear_user.S @@ -10,6 +10,7 @@ #include #include #include +#include SYM_FUNC_START(__clear_user) /* @@ -204,3 +205,5 @@ SYM_FUNC_START(__clear_user_fast) _asm_extable 28b, .Lsmall_fixup _asm_extable 29b, .Lexit SYM_FUNC_END(__clear_user_fast) + +STACK_FRAME_NON_STANDARD __clear_user_fast diff --git a/arch/loongarch/lib/copy_user.S b/arch/loongarch/lib/copy_user.S index feec3d362803..095ce9181c6c 100644 --- a/arch/loongarch/lib/copy_user.S +++ b/arch/loongarch/lib/copy_user.S @@ -10,6 +10,7 @@ #include #include #include +#include SYM_FUNC_START(__copy_user) /* @@ -278,3 +279,5 @@ SYM_FUNC_START(__copy_user_fast) _asm_extable 58b, .Lexit _asm_extable 59b, .Lexit SYM_FUNC_END(__copy_user_fast) + +STACK_FRAME_NON_STANDARD __copy_user_fast diff --git a/arch/loongarch/lib/memcpy.S b/arch/loongarch/lib/memcpy.S index fa1148878d2b..9517a2f961af 100644 --- a/arch/loongarch/lib/memcpy.S +++ b/arch/loongarch/lib/memcpy.S @@ -9,6 +9,7 @@ #include #include #include +#include .section .noinstr.text, "ax" @@ -197,3 +198,5 @@ SYM_FUNC_START(__memcpy_fast) jr ra SYM_FUNC_END(__memcpy_fast) _ASM_NOKPROBE(__memcpy_fast) + +STACK_FRAME_NON_STANDARD __memcpy_small diff --git a/arch/loongarch/lib/memset.S b/arch/loongarch/lib/memset.S index 06d3ca54cbfe..df3846620553 100644 --- a/arch/loongarch/lib/memset.S +++ b/arch/loongarch/lib/memset.S @@ -9,6 +9,7 @@ #include #include #include +#include .macro fill_to_64 r0 bstrins.d \r0, \r0, 15, 8 @@ -166,3 +167,5 @@ SYM_FUNC_START(__memset_fast) jr ra SYM_FUNC_END(__memset_fast) _ASM_NOKPROBE(__memset_fast) + +STACK_FRAME_NON_STANDARD __memset_fast diff --git a/arch/loongarch/mm/tlb.c b/arch/loongarch/mm/tlb.c index 0b95d32b30c9..5ac9beb5f093 100644 --- a/arch/loongarch/mm/tlb.c +++ b/arch/loongarch/mm/tlb.c @@ -9,8 +9,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -266,24 +267,20 @@ static void setup_tlb_handler(int cpu) setup_ptwalker(); local_flush_tlb_all(); + if (cpu_has_ptw) { + exception_table[EXCCODE_TLBI] = handle_tlb_load_ptw; + exception_table[EXCCODE_TLBL] = handle_tlb_load_ptw; + exception_table[EXCCODE_TLBS] = handle_tlb_store_ptw; + exception_table[EXCCODE_TLBM] = handle_tlb_modify_ptw; + } + /* The tlb handlers are generated only once */ if (cpu == 0) { memcpy((void *)tlbrentry, handle_tlb_refill, 0x80); local_flush_icache_range(tlbrentry, tlbrentry + 0x80); - if (!cpu_has_ptw) { - set_handler(EXCCODE_TLBI * VECSIZE, handle_tlb_load, VECSIZE); - set_handler(EXCCODE_TLBL * VECSIZE, handle_tlb_load, VECSIZE); - set_handler(EXCCODE_TLBS * VECSIZE, handle_tlb_store, VECSIZE); - set_handler(EXCCODE_TLBM * VECSIZE, handle_tlb_modify, VECSIZE); - } else { - set_handler(EXCCODE_TLBI * VECSIZE, handle_tlb_load_ptw, VECSIZE); - set_handler(EXCCODE_TLBL * VECSIZE, handle_tlb_load_ptw, VECSIZE); - set_handler(EXCCODE_TLBS * VECSIZE, handle_tlb_store_ptw, VECSIZE); - set_handler(EXCCODE_TLBM * VECSIZE, handle_tlb_modify_ptw, VECSIZE); - } - set_handler(EXCCODE_TLBNR * VECSIZE, handle_tlb_protect, VECSIZE); - set_handler(EXCCODE_TLBNX * VECSIZE, handle_tlb_protect, VECSIZE); - set_handler(EXCCODE_TLBPE * VECSIZE, handle_tlb_protect, VECSIZE); + + for (int i = EXCCODE_TLBL; i <= EXCCODE_TLBPE; i++) + set_handler(i * VECSIZE, exception_table[i], VECSIZE); } else { int vec_sz __maybe_unused; void *addr __maybe_unused; diff --git a/arch/loongarch/mm/tlbex.S b/arch/loongarch/mm/tlbex.S index d5d682f3d29f..a44387b838af 100644 --- a/arch/loongarch/mm/tlbex.S +++ b/arch/loongarch/mm/tlbex.S @@ -18,6 +18,7 @@ .macro tlb_do_page_fault, write SYM_CODE_START(tlb_do_page_fault_\write) + UNWIND_HINT_UNDEFINED SAVE_ALL csrrd a2, LOONGARCH_CSR_BADV move a0, sp @@ -32,6 +33,7 @@ tlb_do_page_fault 1 SYM_CODE_START(handle_tlb_protect) + UNWIND_HINT_UNDEFINED BACKUP_T0T1 SAVE_ALL move a0, sp @@ -44,6 +46,7 @@ SYM_CODE_START(handle_tlb_protect) SYM_CODE_END(handle_tlb_protect) SYM_CODE_START(handle_tlb_load) + UNWIND_HINT_UNDEFINED csrwr t0, EXCEPTION_KS0 csrwr t1, EXCEPTION_KS1 csrwr ra, EXCEPTION_KS2 @@ -190,6 +193,7 @@ nopage_tlb_load: SYM_CODE_END(handle_tlb_load) SYM_CODE_START(handle_tlb_load_ptw) + UNWIND_HINT_UNDEFINED csrwr t0, LOONGARCH_CSR_KS0 csrwr t1, LOONGARCH_CSR_KS1 la_abs t0, tlb_do_page_fault_0 @@ -197,6 +201,7 @@ SYM_CODE_START(handle_tlb_load_ptw) SYM_CODE_END(handle_tlb_load_ptw) SYM_CODE_START(handle_tlb_store) + UNWIND_HINT_UNDEFINED csrwr t0, EXCEPTION_KS0 csrwr t1, EXCEPTION_KS1 csrwr ra, EXCEPTION_KS2 @@ -346,6 +351,7 @@ nopage_tlb_store: SYM_CODE_END(handle_tlb_store) SYM_CODE_START(handle_tlb_store_ptw) + UNWIND_HINT_UNDEFINED csrwr t0, LOONGARCH_CSR_KS0 csrwr t1, LOONGARCH_CSR_KS1 la_abs t0, tlb_do_page_fault_1 @@ -353,6 +359,7 @@ SYM_CODE_START(handle_tlb_store_ptw) SYM_CODE_END(handle_tlb_store_ptw) SYM_CODE_START(handle_tlb_modify) + UNWIND_HINT_UNDEFINED csrwr t0, EXCEPTION_KS0 csrwr t1, EXCEPTION_KS1 csrwr ra, EXCEPTION_KS2 @@ -500,6 +507,7 @@ nopage_tlb_modify: SYM_CODE_END(handle_tlb_modify) SYM_CODE_START(handle_tlb_modify_ptw) + UNWIND_HINT_UNDEFINED csrwr t0, LOONGARCH_CSR_KS0 csrwr t1, LOONGARCH_CSR_KS1 la_abs t0, tlb_do_page_fault_1 @@ -507,6 +515,7 @@ SYM_CODE_START(handle_tlb_modify_ptw) SYM_CODE_END(handle_tlb_modify_ptw) SYM_CODE_START(handle_tlb_refill) + UNWIND_HINT_UNDEFINED csrwr t0, LOONGARCH_CSR_TLBRSAVE csrrd t0, LOONGARCH_CSR_PGD lddir t0, t0, 3 diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index f597cd08a96b..75c6726382c3 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -4,6 +4,7 @@ KASAN_SANITIZE := n UBSAN_SANITIZE := n KCOV_INSTRUMENT := n +OBJECT_FILES_NON_STANDARD := y # Include the generic Makefile to check the built vdso. include $(srctree)/lib/vdso/Makefile diff --git a/include/linux/compiler.h b/include/linux/compiler.h index bb1339c7057b..39f2d4a05208 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -116,6 +116,14 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, */ #define __stringify_label(n) #n +#define __annotate_reachable(c) ({ \ + asm volatile(__stringify_label(c) ":\n\t" \ + ".pushsection .discard.reachable\n\t" \ + ".long " __stringify_label(c) "b - .\n\t" \ + ".popsection\n\t"); \ +}) +#define annotate_reachable() __annotate_reachable(__COUNTER__) + #define __annotate_unreachable(c) ({ \ asm volatile(__stringify_label(c) ":\n\t" \ ".pushsection .discard.unreachable\n\t" \ @@ -128,6 +136,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, #define __annotate_jump_table __section(".rodata..c_jump_table") #else /* !CONFIG_OBJTOOL */ +#define annotate_reachable() #define annotate_unreachable() #define __annotate_jump_table #endif /* CONFIG_OBJTOOL */ diff --git a/scripts/Makefile b/scripts/Makefile index 576cf64be667..e4cca53d2285 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -31,9 +31,12 @@ HOSTLDLIBS_sign-file = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null | ifdef CONFIG_UNWINDER_ORC ifeq ($(ARCH),x86_64) -ARCH := x86 +SRCARCH := x86 endif -HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/x86/include +ifeq ($(ARCH),loongarch) +SRCARCH := loongarch +endif +HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/$(SRCARCH)/include HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLED endif -- cgit v1.2.3 From 410779d8d81fcfb45f839238dc6505af5357f5b8 Mon Sep 17 00:00:00 2001 From: Nuno Das Neves Date: Thu, 7 Mar 2024 15:03:38 -0800 Subject: mshyperv: Introduce hv_get_hypervisor_version function Introduce x86_64 and arm64 functions to get the hypervisor version information and store it in a structure for simpler parsing. Use the new function to get and parse the version at boot time. While at it, move the printing code to hv_common_init() so it is not duplicated. Signed-off-by: Nuno Das Neves Acked-by: Wei Liu Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/1709852618-29110-1-git-send-email-nunodasneves@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <1709852618-29110-1-git-send-email-nunodasneves@linux.microsoft.com> --- arch/arm64/hyperv/mshyperv.c | 18 ++++++++---------- arch/x86/kernel/cpu/mshyperv.c | 34 +++++++++++++++------------------- drivers/hv/hv_common.c | 8 ++++++++ include/asm-generic/hyperv-tlfs.h | 23 +++++++++++++++++++++++ include/asm-generic/mshyperv.h | 2 ++ 5 files changed, 56 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c index f1b8a04ee9f2..99362716ac87 100644 --- a/arch/arm64/hyperv/mshyperv.c +++ b/arch/arm64/hyperv/mshyperv.c @@ -19,10 +19,17 @@ static bool hyperv_initialized; +int hv_get_hypervisor_version(union hv_hypervisor_version_info *info) +{ + hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION, + (struct hv_get_vp_registers_output *)info); + + return 0; +} + static int __init hyperv_init(void) { struct hv_get_vp_registers_output result; - u32 a, b, c, d; u64 guest_id; int ret; @@ -54,15 +61,6 @@ static int __init hyperv_init(void) ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints, ms_hyperv.misc_features); - /* Get information about the Hyper-V host version */ - hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION, &result); - a = result.as32.a; - b = result.as32.b; - c = result.as32.c; - d = result.as32.d; - pr_info("Hyper-V: Host Build %d.%d.%d.%d-%d-%d\n", - b >> 16, b & 0xFFFF, a, d & 0xFFFFFF, c, d >> 24); - ret = hv_common_init(); if (ret) return ret; diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index d306f6184cee..56e731d8f513 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -350,13 +350,24 @@ static void __init reduced_hw_init(void) x86_init.irqs.pre_vector_init = x86_init_noop; } +int hv_get_hypervisor_version(union hv_hypervisor_version_info *info) +{ + unsigned int hv_max_functions; + + hv_max_functions = cpuid_eax(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS); + if (hv_max_functions < HYPERV_CPUID_VERSION) { + pr_err("%s: Could not detect Hyper-V version\n", __func__); + return -ENODEV; + } + + cpuid(HYPERV_CPUID_VERSION, &info->eax, &info->ebx, &info->ecx, &info->edx); + + return 0; +} + static void __init ms_hyperv_init_platform(void) { int hv_max_functions_eax; - int hv_host_info_eax; - int hv_host_info_ebx; - int hv_host_info_ecx; - int hv_host_info_edx; #ifdef CONFIG_PARAVIRT pv_info.name = "Hyper-V"; @@ -407,21 +418,6 @@ static void __init ms_hyperv_init_platform(void) pr_info("Hyper-V: running on a nested hypervisor\n"); } - /* - * Extract host information. - */ - if (hv_max_functions_eax >= HYPERV_CPUID_VERSION) { - hv_host_info_eax = cpuid_eax(HYPERV_CPUID_VERSION); - hv_host_info_ebx = cpuid_ebx(HYPERV_CPUID_VERSION); - hv_host_info_ecx = cpuid_ecx(HYPERV_CPUID_VERSION); - hv_host_info_edx = cpuid_edx(HYPERV_CPUID_VERSION); - - pr_info("Hyper-V: Host Build %d.%d.%d.%d-%d-%d\n", - hv_host_info_ebx >> 16, hv_host_info_ebx & 0xFFFF, - hv_host_info_eax, hv_host_info_edx & 0xFFFFFF, - hv_host_info_ecx, hv_host_info_edx >> 24); - } - if (ms_hyperv.features & HV_ACCESS_FREQUENCY_MSRS && ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) { x86_platform.calibrate_tsc = hv_get_tsc_khz; diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 2f1dd4b07f9a..5d64cb0a709d 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -278,6 +278,14 @@ static void hv_kmsg_dump_register(void) int __init hv_common_init(void) { int i; + union hv_hypervisor_version_info version; + + /* Get information about the Hyper-V host version */ + if (!hv_get_hypervisor_version(&version)) + pr_info("Hyper-V: Host Build %d.%d.%d.%d-%d-%d\n", + version.major_version, version.minor_version, + version.build_number, version.service_number, + version.service_pack, version.service_branch); if (hv_is_isolation_supported()) sysctl_record_panic_msg = 0; diff --git a/include/asm-generic/hyperv-tlfs.h b/include/asm-generic/hyperv-tlfs.h index 3d1b31f90ed6..32514a870b98 100644 --- a/include/asm-generic/hyperv-tlfs.h +++ b/include/asm-generic/hyperv-tlfs.h @@ -817,6 +817,29 @@ struct hv_input_unmap_device_interrupt { #define HV_SOURCE_SHADOW_NONE 0x0 #define HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE 0x1 +/* + * Version info reported by hypervisor + */ +union hv_hypervisor_version_info { + struct { + u32 build_number; + + u32 minor_version : 16; + u32 major_version : 16; + + u32 service_pack; + + u32 service_number : 24; + u32 service_branch : 8; + }; + struct { + u32 eax; + u32 ebx; + u32 ecx; + u32 edx; + }; +}; + /* * The whole argument should fit in a page to be able to pass to the hypervisor * in one hypercall. diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 04424a446bb7..452b7c089b71 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -161,6 +161,8 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) } } +int hv_get_hypervisor_version(union hv_hypervisor_version_info *info); + void hv_setup_vmbus_handler(void (*handler)(void)); void hv_remove_vmbus_handler(void); void hv_setup_stimer0_handler(void (*handler)(void)); -- cgit v1.2.3 From 9f0c4a46be1fe9b97dbe66d49204c1371e3ece65 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 8 Mar 2024 09:08:34 +0800 Subject: f2fs: fix to truncate meta inode pages forcely Below race case can cause data corruption: Thread A GC thread - gc_data_segment - ra_data_block - locked meta_inode page - f2fs_inplace_write_data - invalidate_mapping_pages : fail to invalidate meta_inode page due to lock failure or dirty|writeback status - f2fs_submit_page_bio : write last dirty data to old blkaddr - move_data_block - load old data from meta_inode page - f2fs_submit_page_write : write old data to new blkaddr Because invalidate_mapping_pages() will skip invalidating page which has unclear status including locked, dirty, writeback and so on, so we need to use truncate_inode_pages_range() instead of invalidate_mapping_pages() to make sure meta_inode page will be dropped. Fixes: 6aa58d8ad20a ("f2fs: readahead encrypted block during GC") Fixes: e3b49ea36802 ("f2fs: invalidate META_MAPPING before IPU/DIO write") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 5 +++-- fs/f2fs/f2fs.h | 28 +++++++++++++++++++++++++++- fs/f2fs/segment.c | 5 ++--- include/linux/f2fs_fs.h | 1 + 4 files changed, 33 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index a09a9609e228..55b7d2cf030f 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1598,8 +1598,9 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) */ if (f2fs_sb_has_encrypt(sbi) || f2fs_sb_has_verity(sbi) || f2fs_sb_has_compression(sbi)) - invalidate_mapping_pages(META_MAPPING(sbi), - MAIN_BLKADDR(sbi), MAX_BLKADDR(sbi) - 1); + f2fs_bug_on(sbi, + invalidate_inode_pages2_range(META_MAPPING(sbi), + MAIN_BLKADDR(sbi), MAX_BLKADDR(sbi) - 1)); f2fs_release_ino_entry(sbi, false); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4836e7cb0efe..9814e5981a6a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4655,10 +4655,36 @@ static inline bool f2fs_is_readonly(struct f2fs_sb_info *sbi) return f2fs_sb_has_readonly(sbi) || f2fs_readonly(sbi->sb); } +static inline void f2fs_truncate_meta_inode_pages(struct f2fs_sb_info *sbi, + block_t blkaddr, unsigned int cnt) +{ + bool need_submit = false; + int i = 0; + + do { + struct page *page; + + page = find_get_page(META_MAPPING(sbi), blkaddr + i); + if (page) { + if (PageWriteback(page)) + need_submit = true; + f2fs_put_page(page, 0); + } + } while (++i < cnt && !need_submit); + + if (need_submit) + f2fs_submit_merged_write_cond(sbi, sbi->meta_inode, + NULL, 0, DATA); + + truncate_inode_pages_range(META_MAPPING(sbi), + F2FS_BLK_TO_BYTES((loff_t)blkaddr), + F2FS_BLK_END_BYTES((loff_t)(blkaddr + cnt - 1))); +} + static inline void f2fs_invalidate_internal_cache(struct f2fs_sb_info *sbi, block_t blkaddr) { - invalidate_mapping_pages(META_MAPPING(sbi), blkaddr, blkaddr); + f2fs_truncate_meta_inode_pages(sbi, blkaddr, 1); f2fs_invalidate_compress_page(sbi, blkaddr); } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 4ff3b2d14ddf..20af48d7f784 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3741,8 +3741,7 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio) } if (fio->post_read) - invalidate_mapping_pages(META_MAPPING(sbi), - fio->new_blkaddr, fio->new_blkaddr); + f2fs_truncate_meta_inode_pages(sbi, fio->new_blkaddr, 1); stat_inc_inplace_blocks(fio->sbi); @@ -3932,7 +3931,7 @@ void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr, for (i = 0; i < len; i++) f2fs_wait_on_block_writeback(inode, blkaddr + i); - invalidate_mapping_pages(META_MAPPING(sbi), blkaddr, blkaddr + len - 1); + f2fs_truncate_meta_inode_pages(sbi, blkaddr, len); } static int read_compacted_summaries(struct f2fs_sb_info *sbi) diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 755e9a41b196..a357287eac1e 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -27,6 +27,7 @@ #define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS) #define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS) +#define F2FS_BLK_END_BYTES(blk) (F2FS_BLK_TO_BYTES(blk + 1) - 1) /* 0, 1(node nid), 2(meta nid) are reserved node id */ #define F2FS_RESERVED_NODE_NUM 3 -- cgit v1.2.3 From 2aca9eaf08b1bad07776960087ba6f4367640339 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 7 Mar 2024 13:28:25 +0100 Subject: s390/iucv: use new address translation helpers Use virt_to_dma32() and friends to properly convert virtual to physical and physical to virtual addresses so that "make C=1" does not generate any warnings anymore. Signed-off-by: Heiko Carstens --- include/net/iucv/iucv.h | 3 ++- net/iucv/af_iucv.c | 8 ++++---- net/iucv/iucv.c | 22 +++++++++++----------- 3 files changed, 17 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/iucv/iucv.h b/include/net/iucv/iucv.h index 8b2055d64a6b..5cd7871127c9 100644 --- a/include/net/iucv/iucv.h +++ b/include/net/iucv/iucv.h @@ -30,6 +30,7 @@ #include #include +#include #include /* @@ -76,7 +77,7 @@ * and iucv_message_reply if IUCV_IPBUFLST or IUCV_IPANSLST are used. */ struct iucv_array { - u32 address; + dma32_t address; u32 length; } __attribute__ ((aligned (8))); diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 4aa1c72e6c49..7c8c3adcac6e 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1060,12 +1060,12 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg, int i; /* skip iucv_array lying in the headroom */ - iba[0].address = (u32)virt_to_phys(skb->data); + iba[0].address = virt_to_dma32(skb->data); iba[0].length = (u32)skb_headlen(skb); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - iba[i + 1].address = (u32)virt_to_phys(skb_frag_address(frag)); + iba[i + 1].address = virt_to_dma32(skb_frag_address(frag)); iba[i + 1].length = (u32)skb_frag_size(frag); } err = pr_iucv->message_send(iucv->path, &txmsg, @@ -1161,12 +1161,12 @@ static void iucv_process_message(struct sock *sk, struct sk_buff *skb, struct iucv_array *iba = (struct iucv_array *)skb->head; int i; - iba[0].address = (u32)virt_to_phys(skb->data); + iba[0].address = virt_to_dma32(skb->data); iba[0].length = (u32)skb_headlen(skb); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - iba[i + 1].address = (u32)virt_to_phys(skb_frag_address(frag)); + iba[i + 1].address = virt_to_dma32(skb_frag_address(frag)); iba[i + 1].length = (u32)skb_frag_size(frag); } rc = pr_iucv->message_receive(path, msg, diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 15b3a958fa08..7cb73db8b270 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -210,7 +210,7 @@ struct iucv_cmd_dpl { u8 iprmmsg[8]; u32 ipsrccls; u32 ipmsgtag; - u32 ipbfadr2; + dma32_t ipbfadr2; u32 ipbfln2f; u32 res; } __attribute__ ((packed,aligned(8))); @@ -226,11 +226,11 @@ struct iucv_cmd_db { u8 iprcode; u32 ipmsgid; u32 iptrgcls; - u32 ipbfadr1; + dma32_t ipbfadr1; u32 ipbfln1f; u32 ipsrccls; u32 ipmsgtag; - u32 ipbfadr2; + dma32_t ipbfadr2; u32 ipbfln2f; u32 res; } __attribute__ ((packed,aligned(8))); @@ -432,7 +432,7 @@ static void iucv_declare_cpu(void *data) /* Declare interrupt buffer. */ parm = iucv_param_irq[cpu]; memset(parm, 0, sizeof(union iucv_param)); - parm->db.ipbfadr1 = virt_to_phys(iucv_irq_data[cpu]); + parm->db.ipbfadr1 = virt_to_dma32(iucv_irq_data[cpu]); rc = iucv_call_b2f0(IUCV_DECLARE_BUFFER, parm); if (rc) { char *err = "Unknown"; @@ -1081,7 +1081,7 @@ static int iucv_message_receive_iprmdata(struct iucv_path *path, size = (size < 8) ? size : 8; for (array = buffer; size > 0; array++) { copy = min_t(size_t, size, array->length); - memcpy(phys_to_virt(array->address), rmmsg, copy); + memcpy(dma32_to_virt(array->address), rmmsg, copy); rmmsg += copy; size -= copy; } @@ -1123,7 +1123,7 @@ int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg, parm = iucv_param[smp_processor_id()]; memset(parm, 0, sizeof(union iucv_param)); - parm->db.ipbfadr1 = (u32)virt_to_phys(buffer); + parm->db.ipbfadr1 = virt_to_dma32(buffer); parm->db.ipbfln1f = (u32) size; parm->db.ipmsgid = msg->id; parm->db.ippathid = path->pathid; @@ -1241,7 +1241,7 @@ int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg, parm->dpl.iptrgcls = msg->class; memcpy(parm->dpl.iprmmsg, reply, min_t(size_t, size, 8)); } else { - parm->db.ipbfadr1 = (u32)virt_to_phys(reply); + parm->db.ipbfadr1 = virt_to_dma32(reply); parm->db.ipbfln1f = (u32) size; parm->db.ippathid = path->pathid; parm->db.ipflags1 = flags; @@ -1293,7 +1293,7 @@ int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg, parm->dpl.ipmsgtag = msg->tag; memcpy(parm->dpl.iprmmsg, buffer, 8); } else { - parm->db.ipbfadr1 = (u32)virt_to_phys(buffer); + parm->db.ipbfadr1 = virt_to_dma32(buffer); parm->db.ipbfln1f = (u32) size; parm->db.ippathid = path->pathid; parm->db.ipflags1 = flags | IUCV_IPNORPY; @@ -1378,7 +1378,7 @@ int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg, parm->dpl.iptrgcls = msg->class; parm->dpl.ipsrccls = srccls; parm->dpl.ipmsgtag = msg->tag; - parm->dpl.ipbfadr2 = (u32)virt_to_phys(answer); + parm->dpl.ipbfadr2 = virt_to_dma32(answer); parm->dpl.ipbfln2f = (u32) asize; memcpy(parm->dpl.iprmmsg, buffer, 8); } else { @@ -1387,9 +1387,9 @@ int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg, parm->db.iptrgcls = msg->class; parm->db.ipsrccls = srccls; parm->db.ipmsgtag = msg->tag; - parm->db.ipbfadr1 = (u32)virt_to_phys(buffer); + parm->db.ipbfadr1 = virt_to_dma32(buffer); parm->db.ipbfln1f = (u32) size; - parm->db.ipbfadr2 = (u32)virt_to_phys(answer); + parm->db.ipbfadr2 = virt_to_dma32(answer); parm->db.ipbfln2f = (u32) asize; } rc = iucv_call_b2f0(IUCV_SEND, parm); -- cgit v1.2.3 From 7af03e688792293ba33149fb8df619a8dff90e80 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 8 Mar 2024 18:03:39 +0200 Subject: drm/probe-helper: warn about negative .get_modes() The .get_modes() callback is supposed to return the number of modes, never a negative error code. If a negative value is returned, it'll just be interpreted as a negative count, and added to previous calculations. Document the rules, but handle the negative values gracefully with an error message. Cc: stable@vger.kernel.org Acked-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/50208c866facc33226a3c77b82bb96aeef8ef310.1709913674.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/drm_probe_helper.c | 7 +++++++ include/drm/drm_modeset_helper_vtables.h | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 19ecb749704b..75f84753f6ee 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -422,6 +422,13 @@ static int drm_helper_probe_get_modes(struct drm_connector *connector) count = connector_funcs->get_modes(connector); + /* The .get_modes() callback should not return negative values. */ + if (count < 0) { + drm_err(connector->dev, ".get_modes() returned %pe\n", + ERR_PTR(count)); + count = 0; + } + /* * Fallback for when DDC probe failed in drm_get_edid() and thus skipped * override/firmware EDID. diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 881b03e4dc28..9ed42469540e 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -898,7 +898,8 @@ struct drm_connector_helper_funcs { * * RETURNS: * - * The number of modes added by calling drm_mode_probed_add(). + * The number of modes added by calling drm_mode_probed_add(). Return 0 + * on failures (no modes) instead of negative error codes. */ int (*get_modes)(struct drm_connector *connector); -- cgit v1.2.3 From 6d5501d59cf659651e100fc4c5617d444c22ba74 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 10 Mar 2024 01:38:43 +0200 Subject: drm/bridge: correct DRM_BRIDGE_OP_EDID documentation While the commit d807ad80d811 ("drm/bridge: add ->edid_read hook and drm_bridge_edid_read()") and the commit 27b8f91c08d9 ("drm/bridge: remove ->get_edid callback") replaced ->get_edid() callback with the ->edid_read(), they failed to update documentation. Fix the drm_bridge docs to point to edid_read(). Fixes: 27b8f91c08d9 ("drm/bridge: remove ->get_edid callback") Reviewed-by: Jani Nikula Reviewed-by: Neil Armstrong Signed-off-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20240310-drm-bridge-fix-docs-v1-1-70d3d741cb7a@linaro.org --- include/drm/drm_bridge.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3606e1a7f965..4baca0d9107b 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -541,7 +541,7 @@ struct drm_bridge_funcs { * The @get_modes callback is mostly intended to support non-probeable * displays such as many fixed panels. Bridges that support reading * EDID shall leave @get_modes unimplemented and implement the - * &drm_bridge_funcs->get_edid callback instead. + * &drm_bridge_funcs->edid_read callback instead. * * This callback is optional. Bridges that implement it shall set the * DRM_BRIDGE_OP_MODES flag in their &drm_bridge->ops. @@ -687,7 +687,7 @@ enum drm_bridge_ops { /** * @DRM_BRIDGE_OP_EDID: The bridge can retrieve the EDID of the display * connected to its output. Bridges that set this flag shall implement - * the &drm_bridge_funcs->get_edid callback. + * the &drm_bridge_funcs->edid_read callback. */ DRM_BRIDGE_OP_EDID = BIT(1), /** -- cgit v1.2.3 From e54e09c05c00120cbe817bdb037088035be4bd79 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 12 Mar 2024 09:55:45 -0600 Subject: net: remove {revc,send}msg_copy_msghdr() from exports The only user of these was io_uring, and it's not using them anymore. Make them static and remove them from the socket header file. Signed-off-by: Jens Axboe Link: https://lore.kernel.org/r/1b6089d3-c1cf-464a-abd3-b0f0b6bb2523@kernel.dk Signed-off-by: Jakub Kicinski --- include/linux/socket.h | 7 ------- net/socket.c | 14 +++++++------- tools/perf/trace/beauty/include/linux/socket.h | 7 ------- 3 files changed, 7 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/socket.h b/include/linux/socket.h index cfcb7e2c3813..139c330ccf2c 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -422,13 +422,6 @@ extern long __sys_recvmsg_sock(struct socket *sock, struct msghdr *msg, struct user_msghdr __user *umsg, struct sockaddr __user *uaddr, unsigned int flags); -extern int sendmsg_copy_msghdr(struct msghdr *msg, - struct user_msghdr __user *umsg, unsigned flags, - struct iovec **iov); -extern int recvmsg_copy_msghdr(struct msghdr *msg, - struct user_msghdr __user *umsg, unsigned flags, - struct sockaddr __user **uaddr, - struct iovec **iov); extern int __copy_msghdr(struct msghdr *kmsg, struct user_msghdr *umsg, struct sockaddr __user **save_addr); diff --git a/net/socket.c b/net/socket.c index 7e9c8fc9a5b4..e5f3af49a8b6 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2600,9 +2600,9 @@ out: return err; } -int sendmsg_copy_msghdr(struct msghdr *msg, - struct user_msghdr __user *umsg, unsigned flags, - struct iovec **iov) +static int sendmsg_copy_msghdr(struct msghdr *msg, + struct user_msghdr __user *umsg, unsigned flags, + struct iovec **iov) { int err; @@ -2753,10 +2753,10 @@ SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg, return __sys_sendmmsg(fd, mmsg, vlen, flags, true); } -int recvmsg_copy_msghdr(struct msghdr *msg, - struct user_msghdr __user *umsg, unsigned flags, - struct sockaddr __user **uaddr, - struct iovec **iov) +static int recvmsg_copy_msghdr(struct msghdr *msg, + struct user_msghdr __user *umsg, unsigned flags, + struct sockaddr __user **uaddr, + struct iovec **iov) { ssize_t err; diff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h index cfcb7e2c3813..139c330ccf2c 100644 --- a/tools/perf/trace/beauty/include/linux/socket.h +++ b/tools/perf/trace/beauty/include/linux/socket.h @@ -422,13 +422,6 @@ extern long __sys_recvmsg_sock(struct socket *sock, struct msghdr *msg, struct user_msghdr __user *umsg, struct sockaddr __user *uaddr, unsigned int flags); -extern int sendmsg_copy_msghdr(struct msghdr *msg, - struct user_msghdr __user *umsg, unsigned flags, - struct iovec **iov); -extern int recvmsg_copy_msghdr(struct msghdr *msg, - struct user_msghdr __user *umsg, unsigned flags, - struct sockaddr __user **uaddr, - struct iovec **iov); extern int __copy_msghdr(struct msghdr *kmsg, struct user_msghdr *umsg, struct sockaddr __user **save_addr); -- cgit v1.2.3 From 152609795dbf02f004c86049b75c23f4e68071d8 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sat, 16 Mar 2024 01:10:21 +0100 Subject: fbcon: Increase maximum font width x height to 64 x 128 By using bitmaps we actually support whatever size we would want, but the console currently limits fonts to 64x128 (which gives 60x16 text on 4k screens), so we don't need more for now, and we can easily increase later. Signed-off-by: Samuel Thibault Signed-off-by: Helge Deller --- drivers/firmware/efi/earlycon.c | 2 +- drivers/video/fbdev/arkfb.c | 15 +++++++++++---- drivers/video/fbdev/core/fbcon.c | 16 +++++++++------- drivers/video/fbdev/core/fbmem.c | 12 ++++++------ drivers/video/fbdev/core/svgalib.c | 15 +++++++++++---- drivers/video/fbdev/s3fb.c | 15 +++++++++++---- drivers/video/fbdev/vga16fb.c | 6 +++++- drivers/video/fbdev/vt8623fb.c | 15 +++++++++++---- drivers/video/sticore.c | 2 +- include/linux/fb.h | 18 ++++++++++++------ include/linux/font.h | 3 ++- lib/fonts/fonts.c | 15 +++++++++------ 12 files changed, 89 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c index f80a9af3d16e..d18a1a5de144 100644 --- a/drivers/firmware/efi/earlycon.c +++ b/drivers/firmware/efi/earlycon.c @@ -252,7 +252,7 @@ static int __init efi_earlycon_setup(struct earlycon_device *device, if (si->lfb_depth != 32) return -ENODEV; - font = get_default_font(xres, yres, -1, -1); + font = get_default_font(xres, yres, NULL, NULL); if (!font) return -ENODEV; diff --git a/drivers/video/fbdev/arkfb.c b/drivers/video/fbdev/arkfb.c index dca9c0325b3f..082501feceb9 100644 --- a/drivers/video/fbdev/arkfb.c +++ b/drivers/video/fbdev/arkfb.c @@ -622,8 +622,13 @@ static int arkfb_set_par(struct fb_info *info) info->tileops = NULL; /* in 4bpp supports 8p wide tiles only, any tiles otherwise */ - info->pixmap.blit_x = (bpp == 4) ? (1 << (8 - 1)) : (~(u32)0); - info->pixmap.blit_y = ~(u32)0; + if (bpp == 4) { + bitmap_zero(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, info->pixmap.blit_x); + } else { + bitmap_fill(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + } + bitmap_fill(info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT); offset_value = (info->var.xres_virtual * bpp) / 64; screen_size = info->var.yres_virtual * info->fix.line_length; @@ -635,8 +640,10 @@ static int arkfb_set_par(struct fb_info *info) info->tileops = &arkfb_tile_ops; /* supports 8x16 tiles only */ - info->pixmap.blit_x = 1 << (8 - 1); - info->pixmap.blit_y = 1 << (16 - 1); + bitmap_zero(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, info->pixmap.blit_x); + bitmap_zero(info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT); + set_bit(16 - 1, info->pixmap.blit_y); offset_value = info->var.xres_virtual / 16; screen_size = (info->var.xres_virtual * info->var.yres_virtual) / 64; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 1183e7a871f8..4b67b32fdbc7 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2485,12 +2485,12 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres)) return -EINVAL; - if (font->width > 32 || font->height > 32) + if (font->width > FB_MAX_BLIT_WIDTH || font->height > FB_MAX_BLIT_HEIGHT) return -EINVAL; /* Make sure drawing engine can handle the font */ - if (!(info->pixmap.blit_x & BIT(font->width - 1)) || - !(info->pixmap.blit_y & BIT(font->height - 1))) + if (!test_bit(font->width - 1, info->pixmap.blit_x) || + !test_bit(font->height - 1, info->pixmap.blit_y)) return -EINVAL; /* Make sure driver can handle the font length */ @@ -3084,8 +3084,8 @@ void fbcon_get_requirement(struct fb_info *info, vc = vc_cons[i].d; if (vc && vc->vc_mode == KD_TEXT && info->node == con2fb_map[i]) { - caps->x |= 1 << (vc->vc_font.width - 1); - caps->y |= 1 << (vc->vc_font.height - 1); + set_bit(vc->vc_font.width - 1, caps->x); + set_bit(vc->vc_font.height - 1, caps->y); charcnt = vc->vc_font.charcount; if (caps->len < charcnt) caps->len = charcnt; @@ -3096,8 +3096,10 @@ void fbcon_get_requirement(struct fb_info *info, if (vc && vc->vc_mode == KD_TEXT && info->node == con2fb_map[fg_console]) { - caps->x = 1 << (vc->vc_font.width - 1); - caps->y = 1 << (vc->vc_font.height - 1); + bitmap_zero(caps->x, FB_MAX_BLIT_WIDTH); + set_bit(vc->vc_font.width - 1, caps->x); + bitmap_zero(caps->y, FB_MAX_BLIT_HEIGHT); + set_bit(vc->vc_font.height - 1, caps->y); caps->len = vc->vc_font.charcount; } } diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index fc206755f5f6..5ca18bfe11f6 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -212,8 +212,8 @@ static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var, fbcon_get_requirement(info, &caps); info->fbops->fb_get_caps(info, &fbcaps, var); - if (((fbcaps.x ^ caps.x) & caps.x) || - ((fbcaps.y ^ caps.y) & caps.y) || + if (!bitmap_subset(caps.x, fbcaps.x, FB_MAX_BLIT_WIDTH) || + !bitmap_subset(caps.y, fbcaps.y, FB_MAX_BLIT_HEIGHT) || (fbcaps.len < caps.len)) err = -EINVAL; @@ -420,11 +420,11 @@ static int do_register_framebuffer(struct fb_info *fb_info) } fb_info->pixmap.offset = 0; - if (!fb_info->pixmap.blit_x) - fb_info->pixmap.blit_x = ~(u32)0; + if (bitmap_empty(fb_info->pixmap.blit_x, FB_MAX_BLIT_WIDTH)) + bitmap_fill(fb_info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); - if (!fb_info->pixmap.blit_y) - fb_info->pixmap.blit_y = ~(u32)0; + if (bitmap_empty(fb_info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT)) + bitmap_fill(fb_info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT); if (!fb_info->modelist.prev || !fb_info->modelist.next) INIT_LIST_HEAD(&fb_info->modelist); diff --git a/drivers/video/fbdev/core/svgalib.c b/drivers/video/fbdev/core/svgalib.c index 2cba158888ea..821b89a0a645 100644 --- a/drivers/video/fbdev/core/svgalib.c +++ b/drivers/video/fbdev/core/svgalib.c @@ -354,12 +354,19 @@ void svga_get_caps(struct fb_info *info, struct fb_blit_caps *caps, { if (var->bits_per_pixel == 0) { /* can only support 256 8x16 bitmap */ - caps->x = 1 << (8 - 1); - caps->y = 1 << (16 - 1); + bitmap_zero(caps->x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, caps->x); + bitmap_zero(caps->y, FB_MAX_BLIT_HEIGHT); + set_bit(16 - 1, caps->y); caps->len = 256; } else { - caps->x = (var->bits_per_pixel == 4) ? 1 << (8 - 1) : ~(u32)0; - caps->y = ~(u32)0; + if (var->bits_per_pixel == 4) { + bitmap_zero(caps->x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, caps->x); + } else { + bitmap_fill(caps->x, FB_MAX_BLIT_WIDTH); + } + bitmap_fill(caps->y, FB_MAX_BLIT_HEIGHT); caps->len = ~(u32)0; } } diff --git a/drivers/video/fbdev/s3fb.c b/drivers/video/fbdev/s3fb.c index 07722a5ea8ef..ff84106ecf1c 100644 --- a/drivers/video/fbdev/s3fb.c +++ b/drivers/video/fbdev/s3fb.c @@ -617,8 +617,13 @@ static int s3fb_set_par(struct fb_info *info) info->tileops = NULL; /* in 4bpp supports 8p wide tiles only, any tiles otherwise */ - info->pixmap.blit_x = (bpp == 4) ? (1 << (8 - 1)) : (~(u32)0); - info->pixmap.blit_y = ~(u32)0; + if (bpp == 4) { + bitmap_zero(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, info->pixmap.blit_x); + } else { + bitmap_fill(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + } + bitmap_fill(info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT); offset_value = (info->var.xres_virtual * bpp) / 64; screen_size = info->var.yres_virtual * info->fix.line_length; @@ -630,8 +635,10 @@ static int s3fb_set_par(struct fb_info *info) info->tileops = fasttext ? &s3fb_fast_tile_ops : &s3fb_tile_ops; /* supports 8x16 tiles only */ - info->pixmap.blit_x = 1 << (8 - 1); - info->pixmap.blit_y = 1 << (16 - 1); + bitmap_zero(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, info->pixmap.blit_x); + bitmap_zero(info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT); + set_bit(16 - 1, info->pixmap.blit_y); offset_value = info->var.xres_virtual / 16; screen_size = (info->var.xres_virtual * info->var.yres_virtual) / 64; diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c index b485e9198201..a87bafbb119c 100644 --- a/drivers/video/fbdev/vga16fb.c +++ b/drivers/video/fbdev/vga16fb.c @@ -1353,7 +1353,11 @@ static int vga16fb_probe(struct platform_device *dev) info->var = vga16fb_defined; info->fix = vga16fb_fix; /* supports rectangles with widths of multiples of 8 */ - info->pixmap.blit_x = 1 << 7 | 1 << 15 | 1 << 23 | 1 << 31; + bitmap_zero(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, info->pixmap.blit_x); + set_bit(16 - 1, info->pixmap.blit_x); + set_bit(24 - 1, info->pixmap.blit_x); + set_bit(32 - 1, info->pixmap.blit_x); info->flags = FBINFO_HWACCEL_YPAN; i = (info->var.bits_per_pixel == 8) ? 256 : 16; diff --git a/drivers/video/fbdev/vt8623fb.c b/drivers/video/fbdev/vt8623fb.c index f8d022cb61e8..df984f3a7ff6 100644 --- a/drivers/video/fbdev/vt8623fb.c +++ b/drivers/video/fbdev/vt8623fb.c @@ -390,8 +390,13 @@ static int vt8623fb_set_par(struct fb_info *info) info->tileops = NULL; /* in 4bpp supports 8p wide tiles only, any tiles otherwise */ - info->pixmap.blit_x = (bpp == 4) ? (1 << (8 - 1)) : (~(u32)0); - info->pixmap.blit_y = ~(u32)0; + if (bpp == 4) { + bitmap_zero(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, info->pixmap.blit_x); + } else { + bitmap_fill(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + } + bitmap_fill(info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT); offset_value = (info->var.xres_virtual * bpp) / 64; fetch_value = ((info->var.xres * bpp) / 128) + 4; @@ -408,8 +413,10 @@ static int vt8623fb_set_par(struct fb_info *info) info->tileops = &vt8623fb_tile_ops; /* supports 8x16 tiles only */ - info->pixmap.blit_x = 1 << (8 - 1); - info->pixmap.blit_y = 1 << (16 - 1); + bitmap_zero(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH); + set_bit(8 - 1, info->pixmap.blit_x); + bitmap_zero(info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT); + set_bit(16 - 1, info->pixmap.blit_y); offset_value = info->var.xres_virtual / 16; fetch_value = (info->var.xres / 8) + 8; diff --git a/drivers/video/sticore.c b/drivers/video/sticore.c index 7115b325817f..88a1758616e0 100644 --- a/drivers/video/sticore.c +++ b/drivers/video/sticore.c @@ -529,7 +529,7 @@ sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) if (fbfont_name && strlen(fbfont_name)) fbfont = find_font(fbfont_name); if (!fbfont) - fbfont = get_default_font(1024,768, ~(u32)0, ~(u32)0); + fbfont = get_default_font(1024, 768, NULL, NULL); if (!fbfont) return NULL; diff --git a/include/linux/fb.h b/include/linux/fb.h index 05dc9624897d..7d7c7791fd26 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -143,9 +143,13 @@ struct fb_event { void *data; }; +/* Enough for the VT console needs, see its max_font_width/height */ +#define FB_MAX_BLIT_WIDTH 64 +#define FB_MAX_BLIT_HEIGHT 128 + struct fb_blit_caps { - u32 x; - u32 y; + DECLARE_BITMAP(x, FB_MAX_BLIT_WIDTH); + DECLARE_BITMAP(y, FB_MAX_BLIT_HEIGHT); u32 len; u32 flags; }; @@ -192,10 +196,12 @@ struct fb_pixmap { u32 scan_align; /* alignment per scanline */ u32 access_align; /* alignment per read/write (bits) */ u32 flags; /* see FB_PIXMAP_* */ - u32 blit_x; /* supported bit block dimensions (1-32)*/ - u32 blit_y; /* Format: blit_x = 1 << (width - 1) */ - /* blit_y = 1 << (height - 1) */ - /* if 0, will be set to 0xffffffff (all)*/ + /* supported bit block dimensions */ + /* Format: test_bit(width - 1, blit_x) */ + /* test_bit(height - 1, blit_y) */ + /* if zero, will be set to full (all) */ + DECLARE_BITMAP(blit_x, FB_MAX_BLIT_WIDTH); + DECLARE_BITMAP(blit_y, FB_MAX_BLIT_HEIGHT); /* access methods */ void (*writeio)(struct fb_info *info, void __iomem *dst, void *src, unsigned int size); void (*readio) (struct fb_info *info, void *dst, void __iomem *src, unsigned int size); diff --git a/include/linux/font.h b/include/linux/font.h index abf1442ce719..81caffd51bb4 100644 --- a/include/linux/font.h +++ b/include/linux/font.h @@ -57,7 +57,8 @@ extern const struct font_desc *find_font(const char *name); /* Get the default font for a specific screen size */ extern const struct font_desc *get_default_font(int xres, int yres, - u32 font_w, u32 font_h); + unsigned long *font_w, + unsigned long *font_h); /* Max. length for the name of a predefined font */ #define MAX_FONT_NAME 32 diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c index 973866438608..47e34950b665 100644 --- a/lib/fonts/fonts.c +++ b/lib/fonts/fonts.c @@ -96,18 +96,21 @@ EXPORT_SYMBOL(find_font); * get_default_font - get default font * @xres: screen size of X * @yres: screen size of Y - * @font_w: bit array of supported widths (1 - 32) - * @font_h: bit array of supported heights (1 - 32) + * @font_w: bit array of supported widths (1 - FB_MAX_BLIT_WIDTH) + * @font_h: bit array of supported heights (1 - FB_MAX_BLIT_HEIGHT) * * Get the default font for a specified screen size. * Dimensions are in pixels. * + * font_w or font_h being NULL means all values are supported. + * * Returns %NULL if no font is found, or a pointer to the * chosen font. * */ -const struct font_desc *get_default_font(int xres, int yres, u32 font_w, - u32 font_h) +const struct font_desc *get_default_font(int xres, int yres, + unsigned long *font_w, + unsigned long *font_h) { int i, c, cc, res; const struct font_desc *f, *g; @@ -135,8 +138,8 @@ const struct font_desc *get_default_font(int xres, int yres, u32 font_w, if (res > 20) c += 20 - res; - if ((font_w & (1U << (f->width - 1))) && - (font_h & (1U << (f->height - 1)))) + if ((!font_w || test_bit(f->width - 1, font_w)) && + (!font_h || test_bit(f->height - 1, font_h))) c += 1000; if (c > cc) { -- cgit v1.2.3 From 56a34d799bfa53064e7b8bd354aacd176aeaecc8 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Mon, 26 Feb 2024 16:00:08 +0530 Subject: kexec/kdump: make struct crash_mem available without CONFIG_CRASH_DUMP struct crash_mem defined under include/linux/crash_core.h represents a list of memory ranges. While it is used to represent memory ranges for kdump kernel, it can also be used for other kind of memory ranges. In fact, KEXEC_FILE_LOAD syscall in powerpc uses this structure to represent reserved memory ranges and exclude memory ranges needed to find the right memory regions to load kexec kernel. So, make the definition of crash_mem structure available for !CONFIG_CRASH_DUMP case too. Signed-off-by: Hari Bathini Acked-by: Baoquan He Signed-off-by: Michael Ellerman Link: https://msgid.link/20240226103010.589537-2-hbathini@linux.ibm.com --- include/linux/crash_core.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h index 23270b16e1db..d33352c2e386 100644 --- a/include/linux/crash_core.h +++ b/include/linux/crash_core.h @@ -8,6 +8,12 @@ struct kimage; +struct crash_mem { + unsigned int max_nr_ranges; + unsigned int nr_ranges; + struct range ranges[] __counted_by(max_nr_ranges); +}; + #ifdef CONFIG_CRASH_DUMP int crash_shrink_memory(unsigned long new_size); @@ -51,12 +57,6 @@ static inline unsigned int crash_get_elfcorehdr_size(void) { return 0; } /* Alignment required for elf header segment */ #define ELF_CORE_HEADER_ALIGN 4096 -struct crash_mem { - unsigned int max_nr_ranges; - unsigned int nr_ranges; - struct range ranges[] __counted_by(max_nr_ranges); -}; - extern int crash_exclude_mem_range(struct crash_mem *mem, unsigned long long mstart, unsigned long long mend); -- cgit v1.2.3 From b967df6293510b6e1f53cba56e2475b2aa50be7e Mon Sep 17 00:00:00 2001 From: Nuno Das Neves Date: Tue, 12 Mar 2024 16:21:27 -0700 Subject: hyperv-tlfs: Rename some HV_REGISTER_* defines for consistency Rename HV_REGISTER_GUEST_OSID to HV_REGISTER_GUEST_OS_ID. This matches the existing HV_X64_MSR_GUEST_OS_ID. Rename HV_REGISTER_CRASH_* to HV_REGISTER_GUEST_CRASH_*. Including GUEST_ is consistent with other #defines such as HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE. The new names also match the TLFS document more accurately, i.e. HvRegisterGuestCrash*. Signed-off-by: Nuno Das Neves Link: https://lore.kernel.org/r/1710285687-9160-1-git-send-email-nunodasneves@linux.microsoft.com Signed-off-by: Wei Liu Message-ID: <1710285687-9160-1-git-send-email-nunodasneves@linux.microsoft.com> --- arch/arm64/hyperv/hv_core.c | 14 +++++++------- arch/arm64/hyperv/mshyperv.c | 2 +- arch/arm64/include/asm/hyperv-tlfs.h | 12 ++++++------ arch/x86/kernel/cpu/mshyperv.c | 2 +- include/asm-generic/hyperv-tlfs.h | 16 ++++++++-------- 5 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/arch/arm64/hyperv/hv_core.c b/arch/arm64/hyperv/hv_core.c index b54c34793701..f1ebc025e1df 100644 --- a/arch/arm64/hyperv/hv_core.c +++ b/arch/arm64/hyperv/hv_core.c @@ -160,22 +160,22 @@ void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die) return; panic_reported = true; - guest_id = hv_get_vpreg(HV_REGISTER_GUEST_OSID); + guest_id = hv_get_vpreg(HV_REGISTER_GUEST_OS_ID); /* * Hyper-V provides the ability to store only 5 values. * Pick the passed in error value, the guest_id, the PC, * and the SP. */ - hv_set_vpreg(HV_REGISTER_CRASH_P0, err); - hv_set_vpreg(HV_REGISTER_CRASH_P1, guest_id); - hv_set_vpreg(HV_REGISTER_CRASH_P2, regs->pc); - hv_set_vpreg(HV_REGISTER_CRASH_P3, regs->sp); - hv_set_vpreg(HV_REGISTER_CRASH_P4, 0); + hv_set_vpreg(HV_REGISTER_GUEST_CRASH_P0, err); + hv_set_vpreg(HV_REGISTER_GUEST_CRASH_P1, guest_id); + hv_set_vpreg(HV_REGISTER_GUEST_CRASH_P2, regs->pc); + hv_set_vpreg(HV_REGISTER_GUEST_CRASH_P3, regs->sp); + hv_set_vpreg(HV_REGISTER_GUEST_CRASH_P4, 0); /* * Let Hyper-V know there is crash data available */ - hv_set_vpreg(HV_REGISTER_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); + hv_set_vpreg(HV_REGISTER_GUEST_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); } EXPORT_SYMBOL_GPL(hyperv_report_panic); diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c index 99362716ac87..03ac88bb9d10 100644 --- a/arch/arm64/hyperv/mshyperv.c +++ b/arch/arm64/hyperv/mshyperv.c @@ -46,7 +46,7 @@ static int __init hyperv_init(void) /* Setup the guest ID */ guest_id = hv_generate_guest_id(LINUX_VERSION_CODE); - hv_set_vpreg(HV_REGISTER_GUEST_OSID, guest_id); + hv_set_vpreg(HV_REGISTER_GUEST_OS_ID, guest_id); /* Get the features and hints from Hyper-V */ hv_get_vpreg_128(HV_REGISTER_FEATURES, &result); diff --git a/arch/arm64/include/asm/hyperv-tlfs.h b/arch/arm64/include/asm/hyperv-tlfs.h index 54846d1d29c3..bc30aadedfe9 100644 --- a/arch/arm64/include/asm/hyperv-tlfs.h +++ b/arch/arm64/include/asm/hyperv-tlfs.h @@ -37,12 +37,12 @@ * - On x86, HV_MSR_ indicates an MSR accessed via rdmsrl/wrmsrl * - On ARM, HV_MSR_ indicates a VP register accessed via hypercall */ -#define HV_MSR_CRASH_P0 (HV_REGISTER_CRASH_P0) -#define HV_MSR_CRASH_P1 (HV_REGISTER_CRASH_P1) -#define HV_MSR_CRASH_P2 (HV_REGISTER_CRASH_P2) -#define HV_MSR_CRASH_P3 (HV_REGISTER_CRASH_P3) -#define HV_MSR_CRASH_P4 (HV_REGISTER_CRASH_P4) -#define HV_MSR_CRASH_CTL (HV_REGISTER_CRASH_CTL) +#define HV_MSR_CRASH_P0 (HV_REGISTER_GUEST_CRASH_P0) +#define HV_MSR_CRASH_P1 (HV_REGISTER_GUEST_CRASH_P1) +#define HV_MSR_CRASH_P2 (HV_REGISTER_GUEST_CRASH_P2) +#define HV_MSR_CRASH_P3 (HV_REGISTER_GUEST_CRASH_P3) +#define HV_MSR_CRASH_P4 (HV_REGISTER_GUEST_CRASH_P4) +#define HV_MSR_CRASH_CTL (HV_REGISTER_GUEST_CRASH_CTL) #define HV_MSR_VP_INDEX (HV_REGISTER_VP_INDEX) #define HV_MSR_TIME_REF_COUNT (HV_REGISTER_TIME_REF_COUNT) diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 56e731d8f513..909a6236a4c0 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -450,7 +450,7 @@ static void __init ms_hyperv_init_platform(void) /* To be supported: more work is required. */ ms_hyperv.features &= ~HV_MSR_REFERENCE_TSC_AVAILABLE; - /* HV_REGISTER_CRASH_CTL is unsupported. */ + /* HV_MSR_CRASH_CTL is unsupported. */ ms_hyperv.misc_features &= ~HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE; /* Don't trust Hyper-V's TLB-flushing hypercalls. */ diff --git a/include/asm-generic/hyperv-tlfs.h b/include/asm-generic/hyperv-tlfs.h index 32514a870b98..87e3d49a4e29 100644 --- a/include/asm-generic/hyperv-tlfs.h +++ b/include/asm-generic/hyperv-tlfs.h @@ -636,14 +636,14 @@ struct hv_retarget_device_interrupt { /* * Synthetic register definitions equivalent to MSRs on x86/x64 */ -#define HV_REGISTER_CRASH_P0 0x00000210 -#define HV_REGISTER_CRASH_P1 0x00000211 -#define HV_REGISTER_CRASH_P2 0x00000212 -#define HV_REGISTER_CRASH_P3 0x00000213 -#define HV_REGISTER_CRASH_P4 0x00000214 -#define HV_REGISTER_CRASH_CTL 0x00000215 - -#define HV_REGISTER_GUEST_OSID 0x00090002 +#define HV_REGISTER_GUEST_CRASH_P0 0x00000210 +#define HV_REGISTER_GUEST_CRASH_P1 0x00000211 +#define HV_REGISTER_GUEST_CRASH_P2 0x00000212 +#define HV_REGISTER_GUEST_CRASH_P3 0x00000213 +#define HV_REGISTER_GUEST_CRASH_P4 0x00000214 +#define HV_REGISTER_GUEST_CRASH_CTL 0x00000215 + +#define HV_REGISTER_GUEST_OS_ID 0x00090002 #define HV_REGISTER_VP_INDEX 0x00090003 #define HV_REGISTER_TIME_REF_COUNT 0x00090004 #define HV_REGISTER_REFERENCE_TSC 0x00090017 -- cgit v1.2.3 From 59a55a63c24624c7ad268f12c8f82d142ef6a6d4 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 14 Mar 2024 15:24:13 +0100 Subject: fs,block: get holder during claim Now that we open block devices as files we need to deal with the realities that closing is a deferred operation. An operation on the block device such as e.g., freeze, thaw, or removal that runs concurrently with umount, tries to acquire a stable reference on the holder. The holder might already be gone though. Make that reliable by grabbing a passive reference to the holder during bdev_open() and releasing it during bdev_release(). Fixes: f3a608827d1f ("bdev: open block device as files") # mainline only Reported-by: Christoph Hellwig Link: https://lore.kernel.org/r/ZfEQQ9jZZVes0WCZ@infradead.org Reviewed-by: Jan Kara Reviewed-by: Christoph Hellwig Tested-by: Yi Zhang Reported-by: https://lore.kernel.org/r/CAHj4cs8tbDwKRwfS1=DmooP73ysM__xAb2PQc6XsAmWR+VuYmg@mail.gmail.com Link: https://lore.kernel.org/r/20240315-freibad-annehmbar-ca68c375af91@brauner Signed-off-by: Christian Brauner --- block/bdev.c | 7 +++++++ fs/super.c | 18 ++++++++++++++++++ include/linux/blkdev.h | 10 ++++++++++ 3 files changed, 35 insertions(+) (limited to 'include') diff --git a/block/bdev.c b/block/bdev.c index e7adaaf1c219..7a5f611c3d2e 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -583,6 +583,9 @@ static void bd_finish_claiming(struct block_device *bdev, void *holder, mutex_unlock(&bdev->bd_holder_lock); bd_clear_claiming(whole, holder); mutex_unlock(&bdev_lock); + + if (hops && hops->get_holder) + hops->get_holder(holder); } /** @@ -605,6 +608,7 @@ EXPORT_SYMBOL(bd_abort_claiming); static void bd_end_claim(struct block_device *bdev, void *holder) { struct block_device *whole = bdev_whole(bdev); + const struct blk_holder_ops *hops = bdev->bd_holder_ops; bool unblock = false; /* @@ -627,6 +631,9 @@ static void bd_end_claim(struct block_device *bdev, void *holder) whole->bd_holder = NULL; mutex_unlock(&bdev_lock); + if (hops && hops->put_holder) + hops->put_holder(holder); + /* * If this was the last claim, remove holder link and unblock evpoll if * it was a write holder. diff --git a/fs/super.c b/fs/super.c index ee05ab6b37e7..71d9779c42b1 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1515,11 +1515,29 @@ static int fs_bdev_thaw(struct block_device *bdev) return error; } +static void fs_bdev_super_get(void *data) +{ + struct super_block *sb = data; + + spin_lock(&sb_lock); + sb->s_count++; + spin_unlock(&sb_lock); +} + +static void fs_bdev_super_put(void *data) +{ + struct super_block *sb = data; + + put_super(sb); +} + const struct blk_holder_ops fs_holder_ops = { .mark_dead = fs_bdev_mark_dead, .sync = fs_bdev_sync, .freeze = fs_bdev_freeze, .thaw = fs_bdev_thaw, + .get_holder = fs_bdev_super_get, + .put_holder = fs_bdev_super_put, }; EXPORT_SYMBOL_GPL(fs_holder_ops); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index f9b87c39cab0..c3e8f7cf96be 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1505,6 +1505,16 @@ struct blk_holder_ops { * Thaw the file system mounted on the block device. */ int (*thaw)(struct block_device *bdev); + + /* + * If needed, get a reference to the holder. + */ + void (*get_holder)(void *holder); + + /* + * Release the holder. + */ + void (*put_holder)(void *holder); }; /* -- cgit v1.2.3 From c3198822c6cb9fb588e446540485669cc81c5d34 Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Fri, 8 Mar 2024 17:26:00 +0200 Subject: net: esp: fix bad handling of pages from page_pool When the skb is reorganized during esp_output (!esp->inline), the pages coming from the original skb fragments are supposed to be released back to the system through put_page. But if the skb fragment pages are originating from a page_pool, calling put_page on them will trigger a page_pool leak which will eventually result in a crash. This leak can be easily observed when using CONFIG_DEBUG_VM and doing ipsec + gre (non offloaded) forwarding: BUG: Bad page state in process ksoftirqd/16 pfn:1451b6 page:00000000de2b8d32 refcount:0 mapcount:0 mapping:0000000000000000 index:0x1451b6000 pfn:0x1451b6 flags: 0x200000000000000(node=0|zone=2) page_type: 0xffffffff() raw: 0200000000000000 dead000000000040 ffff88810d23c000 0000000000000000 raw: 00000001451b6000 0000000000000001 00000000ffffffff 0000000000000000 page dumped because: page_pool leak Modules linked in: ip_gre gre mlx5_ib mlx5_core xt_conntrack xt_MASQUERADE nf_conntrack_netlink nfnetlink iptable_nat nf_nat xt_addrtype br_netfilter rpcrdma rdma_ucm ib_iser libiscsi scsi_transport_iscsi ib_umad rdma_cm ib_ipoib iw_cm ib_cm ib_uverbs ib_core overlay zram zsmalloc fuse [last unloaded: mlx5_core] CPU: 16 PID: 96 Comm: ksoftirqd/16 Not tainted 6.8.0-rc4+ #22 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014 Call Trace: dump_stack_lvl+0x36/0x50 bad_page+0x70/0xf0 free_unref_page_prepare+0x27a/0x460 free_unref_page+0x38/0x120 esp_ssg_unref.isra.0+0x15f/0x200 esp_output_tail+0x66d/0x780 esp_xmit+0x2c5/0x360 validate_xmit_xfrm+0x313/0x370 ? validate_xmit_skb+0x1d/0x330 validate_xmit_skb_list+0x4c/0x70 sch_direct_xmit+0x23e/0x350 __dev_queue_xmit+0x337/0xba0 ? nf_hook_slow+0x3f/0xd0 ip_finish_output2+0x25e/0x580 iptunnel_xmit+0x19b/0x240 ip_tunnel_xmit+0x5fb/0xb60 ipgre_xmit+0x14d/0x280 [ip_gre] dev_hard_start_xmit+0xc3/0x1c0 __dev_queue_xmit+0x208/0xba0 ? nf_hook_slow+0x3f/0xd0 ip_finish_output2+0x1ca/0x580 ip_sublist_rcv_finish+0x32/0x40 ip_sublist_rcv+0x1b2/0x1f0 ? ip_rcv_finish_core.constprop.0+0x460/0x460 ip_list_rcv+0x103/0x130 __netif_receive_skb_list_core+0x181/0x1e0 netif_receive_skb_list_internal+0x1b3/0x2c0 napi_gro_receive+0xc8/0x200 gro_cell_poll+0x52/0x90 __napi_poll+0x25/0x1a0 net_rx_action+0x28e/0x300 __do_softirq+0xc3/0x276 ? sort_range+0x20/0x20 run_ksoftirqd+0x1e/0x30 smpboot_thread_fn+0xa6/0x130 kthread+0xcd/0x100 ? kthread_complete_and_exit+0x20/0x20 ret_from_fork+0x31/0x50 ? kthread_complete_and_exit+0x20/0x20 ret_from_fork_asm+0x11/0x20 The suggested fix is to introduce a new wrapper (skb_page_unref) that covers page refcounting for page_pool pages as well. Cc: stable@vger.kernel.org Fixes: 6a5bcd84e886 ("page_pool: Allow drivers to hint on SKB recycling") Reported-and-tested-by: Anatoli N.Chechelnickiy Reported-by: Ian Kumlien Link: https://lore.kernel.org/netdev/CAA85sZvvHtrpTQRqdaOx6gd55zPAVsqMYk_Lwh4Md5knTq7AyA@mail.gmail.com Signed-off-by: Dragos Tatulea Reviewed-by: Mina Almasry Reviewed-by: Jakub Kicinski Acked-by: Ilias Apalodimas Signed-off-by: Steffen Klassert --- include/linux/skbuff.h | 10 ++++++++++ net/ipv4/esp4.c | 8 ++++---- net/ipv6/esp6.c | 8 ++++---- 3 files changed, 18 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 3023bc2be6a1..b49a7d6591e8 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3523,6 +3523,16 @@ int skb_cow_data_for_xdp(struct page_pool *pool, struct sk_buff **pskb, struct bpf_prog *prog); bool napi_pp_put_page(struct page *page, bool napi_safe); +static inline void +skb_page_unref(const struct sk_buff *skb, struct page *page, bool napi_safe) +{ +#ifdef CONFIG_PAGE_POOL + if (skb->pp_recycle && napi_pp_put_page(page, napi_safe)) + return; +#endif + put_page(page); +} + static inline void napi_frag_unref(skb_frag_t *frag, bool recycle, bool napi_safe) { diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 4dd9e5040672..d33d12421814 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -95,7 +95,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, __alignof__(struct scatterlist)); } -static void esp_ssg_unref(struct xfrm_state *x, void *tmp) +static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb) { struct crypto_aead *aead = x->data; int extralen = 0; @@ -114,7 +114,7 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp) */ if (req->src != req->dst) for (sg = sg_next(req->src); sg; sg = sg_next(sg)) - put_page(sg_page(sg)); + skb_page_unref(skb, sg_page(sg), false); } #ifdef CONFIG_INET_ESPINTCP @@ -260,7 +260,7 @@ static void esp_output_done(void *data, int err) } tmp = ESP_SKB_CB(skb)->tmp; - esp_ssg_unref(x, tmp); + esp_ssg_unref(x, tmp, skb); kfree(tmp); if (xo && (xo->flags & XFRM_DEV_RESUME)) { @@ -639,7 +639,7 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * } if (sg != dsg) - esp_ssg_unref(x, tmp); + esp_ssg_unref(x, tmp, skb); if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) err = esp_output_tail_tcp(x, skb); diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 6e6efe026cdc..7371886d4f9f 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -112,7 +112,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, __alignof__(struct scatterlist)); } -static void esp_ssg_unref(struct xfrm_state *x, void *tmp) +static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb) { struct crypto_aead *aead = x->data; int extralen = 0; @@ -131,7 +131,7 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp) */ if (req->src != req->dst) for (sg = sg_next(req->src); sg; sg = sg_next(sg)) - put_page(sg_page(sg)); + skb_page_unref(skb, sg_page(sg), false); } #ifdef CONFIG_INET6_ESPINTCP @@ -294,7 +294,7 @@ static void esp_output_done(void *data, int err) } tmp = ESP_SKB_CB(skb)->tmp; - esp_ssg_unref(x, tmp); + esp_ssg_unref(x, tmp, skb); kfree(tmp); esp_output_encap_csum(skb); @@ -677,7 +677,7 @@ int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info } if (sg != dsg) - esp_ssg_unref(x, tmp); + esp_ssg_unref(x, tmp, skb); if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) err = esp_output_tail_tcp(x, skb); -- cgit v1.2.3 From 35c3e27917568192927c785fc380f139255468b4 Mon Sep 17 00:00:00 2001 From: Abhishek Chauhan Date: Thu, 14 Mar 2024 12:24:04 -0700 Subject: Revert "net: Re-use and set mono_delivery_time bit for userspace tstamp packets" This reverts commit 885c36e59f46375c138de18ff1692f18eff67b7f. The patch currently broke the bpf selftest test_tc_dtime because uapi field __sk_buff->tstamp_type depends on skb->mono_delivery_time which does not necessarily mean mono with the original fix as the bit was re-used for userspace timestamp as well to avoid tstamp reset in the forwarding path. To solve this we need to keep mono_delivery_time as is and introduce another bit called user_delivery_time and fall back to the initial proposal of setting the user_delivery_time bit based on sk_clockid set from userspace. Fixes: 885c36e59f46 ("net: Re-use and set mono_delivery_time bit for userspace tstamp packets") Link: https://lore.kernel.org/netdev/bc037db4-58bb-4861-ac31-a361a93841d3@linux.dev/ Signed-off-by: Abhishek Chauhan Acked-by: Daniel Borkmann Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- include/linux/skbuff.h | 6 +++--- net/ipv4/ip_output.c | 1 - net/ipv4/raw.c | 1 - net/ipv6/ip6_output.c | 2 +- net/ipv6/raw.c | 2 +- net/packet/af_packet.c | 4 +--- 6 files changed, 6 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 3023bc2be6a1..7d56ce195120 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -822,9 +822,9 @@ typedef unsigned char *sk_buff_data_t; * @decrypted: Decrypted SKB * @slow_gro: state present at GRO time, slower prepare step required * @mono_delivery_time: When set, skb->tstamp has the - * delivery_time in mono clock base (i.e., EDT) or a clock base chosen - * by SO_TXTIME. If zero, skb->tstamp has the (rcv) timestamp at - * ingress. + * delivery_time in mono clock base (i.e. EDT). Otherwise, the + * skb->tstamp has the (rcv) timestamp at ingress and + * delivery_time at egress. * @napi_id: id of the NAPI struct this skb came from * @sender_cpu: (aka @napi_id) source CPU in XPS * @alloc_cpu: CPU which did the skb allocation. diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 33f93dc730a3..1fe794967211 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1458,7 +1458,6 @@ struct sk_buff *__ip_make_skb(struct sock *sk, skb->priority = (cork->tos != -1) ? cork->priority: READ_ONCE(sk->sk_priority); skb->mark = cork->mark; skb->tstamp = cork->transmit_time; - skb->mono_delivery_time = !!skb->tstamp; /* * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec * on dst refcount diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 42ac434cfcfa..12b3740393ba 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -360,7 +360,6 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, skb->priority = READ_ONCE(sk->sk_priority); skb->mark = sockc->mark; skb->tstamp = sockc->transmit_time; - skb->mono_delivery_time = !!skb->tstamp; skb_dst_set(skb, &rt->dst); *rtp = NULL; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 02eeca5492cd..b9dd3a66e423 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1925,7 +1925,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, skb->priority = READ_ONCE(sk->sk_priority); skb->mark = cork->base.mark; skb->tstamp = cork->base.transmit_time; - skb->mono_delivery_time = !!skb->tstamp; + ip6_cork_steal_dst(skb, cork); IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS); if (proto == IPPROTO_ICMPV6) { diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index ca49e6617afa..0d896ca7b589 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -622,7 +622,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, skb->priority = READ_ONCE(sk->sk_priority); skb->mark = sockc->mark; skb->tstamp = sockc->transmit_time; - skb->mono_delivery_time = !!skb->tstamp; + skb_put(skb, length); skb_reset_network_header(skb); iph = ipv6_hdr(skb); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 7cfc7d301508..18f616f487ea 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2057,7 +2057,7 @@ retry: skb->priority = READ_ONCE(sk->sk_priority); skb->mark = READ_ONCE(sk->sk_mark); skb->tstamp = sockc.transmit_time; - skb->mono_delivery_time = !!skb->tstamp; + skb_setup_tx_timestamp(skb, sockc.tsflags); if (unlikely(extra_len == 4)) @@ -2586,7 +2586,6 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, skb->priority = READ_ONCE(po->sk.sk_priority); skb->mark = READ_ONCE(po->sk.sk_mark); skb->tstamp = sockc->transmit_time; - skb->mono_delivery_time = !!skb->tstamp; skb_setup_tx_timestamp(skb, sockc->tsflags); skb_zcopy_set_nouarg(skb, ph.raw); @@ -3065,7 +3064,6 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) skb->priority = READ_ONCE(sk->sk_priority); skb->mark = sockc.mark; skb->tstamp = sockc.transmit_time; - skb->mono_delivery_time = !!skb->tstamp; if (unlikely(extra_len == 4)) skb->no_fcs = 1; -- cgit v1.2.3 From 64805e4039f1687b9857034123a9ec10bb9abddd Mon Sep 17 00:00:00 2001 From: Beau Belgrave Date: Thu, 22 Feb 2024 00:18:05 +0000 Subject: tracing/user_events: Introduce multi-format events Currently user_events supports 1 event with the same name and must have the exact same format when referenced by multiple programs. This opens an opportunity for malicious or poorly thought through programs to create events that others use with different formats. Another scenario is user programs wishing to use the same event name but add more fields later when the software updates. Various versions of a program may be running side-by-side, which is prevented by the current single format requirement. Add a new register flag (USER_EVENT_REG_MULTI_FORMAT) which indicates the user program wishes to use the same user_event name, but may have several different formats of the event. When this flag is used, create the underlying tracepoint backing the user_event with a unique name per-version of the format. It's important that existing ABI users do not get this logic automatically, even if one of the multi format events matches the format. This ensures existing programs that create events and assume the tracepoint name will match exactly continue to work as expected. Add logic to only check multi-format events with other multi-format events and single-format events to only check single-format events during find. Change system name of the multi-format event tracepoint to ensure that multi-format events are isolated completely from single-format events. This prevents single-format names from conflicting with multi-format events if they end with the same suffix as the multi-format events. Add a register_name (reg_name) to the user_event struct which allows for split naming of events. We now have the name that was used to register within user_events as well as the unique name for the tracepoint. Upon registering events ensure matches based on first the reg_name, followed by the fields and format of the event. This allows for multiple events with the same registered name to have different formats. The underlying tracepoint will have a unique name in the format of {reg_name}.{unique_id}. For example, if both "test u32 value" and "test u64 value" are used with the USER_EVENT_REG_MULTI_FORMAT the system would have 2 unique tracepoints. The dynamic_events file would then show the following: u:test u64 count u:test u32 count The actual tracepoint names look like this: test.0 test.1 Both would be under the new user_events_multi system name to prevent the older ABI from being used to squat on multi-formatted events and block their use. Deleting events via "!u:test u64 count" would only delete the first tracepoint that matched that format. When the delete ABI is used all events with the same name will be attempted to be deleted. If per-version deletion is required, user programs should either not use persistent events or delete them via dynamic_events. Link: https://lore.kernel.org/linux-trace-kernel/20240222001807.1463-3-beaub@linux.microsoft.com Signed-off-by: Beau Belgrave Signed-off-by: Steven Rostedt (Google) --- include/uapi/linux/user_events.h | 6 ++- kernel/trace/trace_events_user.c | 102 ++++++++++++++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/user_events.h b/include/uapi/linux/user_events.h index f74f3aedd49c..a03de03dccbc 100644 --- a/include/uapi/linux/user_events.h +++ b/include/uapi/linux/user_events.h @@ -12,6 +12,7 @@ #include #define USER_EVENTS_SYSTEM "user_events" +#define USER_EVENTS_MULTI_SYSTEM "user_events_multi" #define USER_EVENTS_PREFIX "u:" /* Create dynamic location entry within a 32-bit value */ @@ -22,8 +23,11 @@ enum user_reg_flag { /* Event will not delete upon last reference closing */ USER_EVENT_REG_PERSIST = 1U << 0, + /* Event will be allowed to have multiple formats */ + USER_EVENT_REG_MULTI_FORMAT = 1U << 1, + /* This value or above is currently non-ABI */ - USER_EVENT_REG_MAX = 1U << 1, + USER_EVENT_REG_MAX = 1U << 2, }; /* diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index fce5ed5fec50..70d428c394b6 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -34,7 +34,8 @@ /* Limit how long of an event name plus args within the subsystem. */ #define MAX_EVENT_DESC 512 -#define EVENT_NAME(user_event) ((user_event)->tracepoint.name) +#define EVENT_NAME(user_event) ((user_event)->reg_name) +#define EVENT_TP_NAME(user_event) ((user_event)->tracepoint.name) #define MAX_FIELD_ARRAY_SIZE 1024 /* @@ -54,10 +55,13 @@ * allows isolation for events by various means. */ struct user_event_group { - char *system_name; - struct hlist_node node; - struct mutex reg_mutex; + char *system_name; + char *system_multi_name; + struct hlist_node node; + struct mutex reg_mutex; DECLARE_HASHTABLE(register_table, 8); + /* ID that moves forward within the group for multi-event names */ + u64 multi_id; }; /* Group for init_user_ns mapping, top-most group */ @@ -78,6 +82,7 @@ static unsigned int current_user_events; */ struct user_event { struct user_event_group *group; + char *reg_name; struct tracepoint tracepoint; struct trace_event_call call; struct trace_event_class class; @@ -127,6 +132,8 @@ struct user_event_enabler { #define ENABLE_BIT(e) ((int)((e)->values & ENABLE_VAL_BIT_MASK)) +#define EVENT_MULTI_FORMAT(f) ((f) & USER_EVENT_REG_MULTI_FORMAT) + /* Used for asynchronous faulting in of pages */ struct user_event_enabler_fault { struct work_struct work; @@ -330,6 +337,7 @@ out: static void user_event_group_destroy(struct user_event_group *group) { kfree(group->system_name); + kfree(group->system_multi_name); kfree(group); } @@ -348,6 +356,11 @@ static char *user_event_group_system_name(void) return system_name; } +static char *user_event_group_system_multi_name(void) +{ + return kstrdup(USER_EVENTS_MULTI_SYSTEM, GFP_KERNEL); +} + static struct user_event_group *current_user_event_group(void) { return init_group; @@ -367,6 +380,11 @@ static struct user_event_group *user_event_group_create(void) if (!group->system_name) goto error; + group->system_multi_name = user_event_group_system_multi_name(); + + if (!group->system_multi_name) + goto error; + mutex_init(&group->reg_mutex); hash_init(group->register_table); @@ -1482,6 +1500,11 @@ static int destroy_user_event(struct user_event *user) hash_del(&user->node); user_event_destroy_validators(user); + + /* If we have different names, both must be freed */ + if (EVENT_NAME(user) != EVENT_TP_NAME(user)) + kfree(EVENT_TP_NAME(user)); + kfree(user->call.print_fmt); kfree(EVENT_NAME(user)); kfree(user); @@ -1504,12 +1527,24 @@ static struct user_event *find_user_event(struct user_event_group *group, *outkey = key; hash_for_each_possible(group->register_table, user, node, key) { + /* + * Single-format events shouldn't return multi-format + * events. Callers expect the underlying tracepoint to match + * the name exactly in these cases. Only check like-formats. + */ + if (EVENT_MULTI_FORMAT(flags) != EVENT_MULTI_FORMAT(user->reg_flags)) + continue; + if (strcmp(EVENT_NAME(user), name)) continue; if (user_fields_match(user, argc, argv)) return user_event_get(user); + /* Scan others if this is a multi-format event */ + if (EVENT_MULTI_FORMAT(flags)) + continue; + return ERR_PTR(-EADDRINUSE); } @@ -1889,8 +1924,12 @@ static bool user_event_match(const char *system, const char *event, struct user_event *user = container_of(ev, struct user_event, devent); bool match; - match = strcmp(EVENT_NAME(user), event) == 0 && - (!system || strcmp(system, USER_EVENTS_SYSTEM) == 0); + match = strcmp(EVENT_NAME(user), event) == 0; + + if (match && system) { + match = strcmp(system, user->group->system_name) == 0 || + strcmp(system, user->group->system_multi_name) == 0; + } if (match) match = user_fields_match(user, argc, argv); @@ -1923,6 +1962,33 @@ static int user_event_trace_register(struct user_event *user) return ret; } +static int user_event_set_tp_name(struct user_event *user) +{ + lockdep_assert_held(&user->group->reg_mutex); + + if (EVENT_MULTI_FORMAT(user->reg_flags)) { + char *multi_name; + + multi_name = kasprintf(GFP_KERNEL_ACCOUNT, "%s.%llx", + user->reg_name, user->group->multi_id); + + if (!multi_name) + return -ENOMEM; + + user->call.name = multi_name; + user->tracepoint.name = multi_name; + + /* Inc to ensure unique multi-event name next time */ + user->group->multi_id++; + } else { + /* Non Multi-format uses register name */ + user->call.name = user->reg_name; + user->tracepoint.name = user->reg_name; + } + + return 0; +} + /* * Parses the event name, arguments and flags then registers if successful. * The name buffer lifetime is owned by this method for success cases only. @@ -1985,7 +2051,13 @@ static int user_event_parse(struct user_event_group *group, char *name, INIT_LIST_HEAD(&user->validators); user->group = group; - user->tracepoint.name = name; + user->reg_name = name; + user->reg_flags = reg_flags; + + ret = user_event_set_tp_name(user); + + if (ret) + goto put_user; ret = user_event_parse_fields(user, args); @@ -1999,11 +2071,14 @@ static int user_event_parse(struct user_event_group *group, char *name, user->call.data = user; user->call.class = &user->class; - user->call.name = name; user->call.flags = TRACE_EVENT_FL_TRACEPOINT; user->call.tp = &user->tracepoint; user->call.event.funcs = &user_event_funcs; - user->class.system = group->system_name; + + if (EVENT_MULTI_FORMAT(user->reg_flags)) + user->class.system = group->system_multi_name; + else + user->class.system = group->system_name; user->class.fields_array = user_event_fields_array; user->class.get_fields = user_event_get_fields; @@ -2025,8 +2100,6 @@ static int user_event_parse(struct user_event_group *group, char *name, if (ret) goto put_user_lock; - user->reg_flags = reg_flags; - if (user->reg_flags & USER_EVENT_REG_PERSIST) { /* Ensure we track self ref and caller ref (2) */ refcount_set(&user->refcnt, 2); @@ -2050,6 +2123,11 @@ put_user: user_event_destroy_fields(user); user_event_destroy_validators(user); kfree(user->call.print_fmt); + + /* Caller frees reg_name on error, but not multi-name */ + if (EVENT_NAME(user) != EVENT_TP_NAME(user)) + kfree(EVENT_TP_NAME(user)); + kfree(user); return ret; } @@ -2639,7 +2717,7 @@ static int user_seq_show(struct seq_file *m, void *p) hash_for_each(group->register_table, i, user, node) { status = user->status; - seq_printf(m, "%s", EVENT_NAME(user)); + seq_printf(m, "%s", EVENT_TP_NAME(user)); if (status != 0) seq_puts(m, " #"); -- cgit v1.2.3 From c1fa617caeb005e7e3db60826cff6dddebb0363f Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 22 Feb 2024 16:14:16 -0500 Subject: tracing: Rework __assign_str() and __string() to not duplicate getting the string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TRACE_EVENT() macro handles dynamic strings by having: TP_PROTO(struct some_struct *s), TP_ARGS(s), TP_STRUCT__entry( __string(my_string, s->string) ), TP_fast_assign( __assign_str(my_string, s->string); ) TP_printk("%s", __get_str(my_string)) There's even some code that may call a function helper to find the s->string value. The problem with the above is that the work to get the s->string is done twice. Once at the __string() and again in the __assign_str(). But the __string() uses dynamic_array() which has a helper structure that is created holding the offsets and length of the string fields. Instead of finding the string twice, just save it off in another field from that helper structure, and have __assign_str() use that instead. Note, this also means that the second parameter of __assign_str() isn't even used anymore, and may be removed in the future. Link: https://lore.kernel.org/linux-trace-kernel/20240222211442.634192653@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Ville Syrjälä Cc: Rodrigo Vivi Cc: Chuck Lever Signed-off-by: Steven Rostedt (Google) --- include/trace/stages/stage2_data_offsets.h | 4 ++-- include/trace/stages/stage5_get_offsets.h | 15 ++++++++++----- include/trace/stages/stage6_event_callback.h | 12 ++++++++---- 3 files changed, 20 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/trace/stages/stage2_data_offsets.h b/include/trace/stages/stage2_data_offsets.h index 469b6a64293d..8b0cff06d346 100644 --- a/include/trace/stages/stage2_data_offsets.h +++ b/include/trace/stages/stage2_data_offsets.h @@ -24,7 +24,7 @@ #define __array(type, item, len) #undef __dynamic_array -#define __dynamic_array(type, item, len) u32 item; +#define __dynamic_array(type, item, len) u32 item; const void *item##_ptr_; #undef __string #define __string(item, src) __dynamic_array(char, item, -1) @@ -45,7 +45,7 @@ #define __sockaddr(field, len) __dynamic_array(u8, field, len) #undef __rel_dynamic_array -#define __rel_dynamic_array(type, item, len) u32 item; +#define __rel_dynamic_array(type, item, len) u32 item; const void *item##_ptr_; #undef __rel_string #define __rel_string(item, src) __rel_dynamic_array(char, item, -1) diff --git a/include/trace/stages/stage5_get_offsets.h b/include/trace/stages/stage5_get_offsets.h index e30a13be46ba..45f8151cf622 100644 --- a/include/trace/stages/stage5_get_offsets.h +++ b/include/trace/stages/stage5_get_offsets.h @@ -47,10 +47,12 @@ #undef __string #define __string(item, src) __dynamic_array(char, item, \ - strlen((src) ? (const char *)(src) : "(null)") + 1) + strlen((src) ? (const char *)(src) : "(null)") + 1) \ + __data_offsets->item##_ptr_ = src; #undef __string_len -#define __string_len(item, src, len) __dynamic_array(char, item, (len) + 1) +#define __string_len(item, src, len) __dynamic_array(char, item, (len) + 1)\ + __data_offsets->item##_ptr_ = src; #undef __vstring #define __vstring(item, fmt, ap) __dynamic_array(char, item, \ @@ -67,11 +69,14 @@ __data_size += __item_length; #undef __rel_string -#define __rel_string(item, src) __rel_dynamic_array(char, item, \ - strlen((src) ? (const char *)(src) : "(null)") + 1) +#define __rel_string(item, src) __rel_dynamic_array(char, item, \ + strlen((src) ? (const char *)(src) : "(null)") + 1) \ + __data_offsets->item##_ptr_ = src; #undef __rel_string_len -#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, (len) + 1) +#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, (len) + 1)\ + __data_offsets->item##_ptr_ = src; + /* * __bitmask_size_in_bytes_raw is the number of bytes needed to hold * num_possible_cpus(). diff --git a/include/trace/stages/stage6_event_callback.h b/include/trace/stages/stage6_event_callback.h index 919b1a4da980..b3e2f321e787 100644 --- a/include/trace/stages/stage6_event_callback.h +++ b/include/trace/stages/stage6_event_callback.h @@ -32,12 +32,14 @@ #undef __assign_str #define __assign_str(dst, src) \ - strcpy(__get_str(dst), (src) ? (const char *)(src) : "(null)"); + strcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? \ + __data_offsets.dst##_ptr_ : "(null)") #undef __assign_str_len #define __assign_str_len(dst, src, len) \ do { \ - memcpy(__get_str(dst), (src), (len)); \ + memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? \ + __data_offsets.dst##_ptr_ : "(null)", len); \ __get_str(dst)[len] = '\0'; \ } while(0) @@ -92,12 +94,14 @@ #undef __assign_rel_str #define __assign_rel_str(dst, src) \ - strcpy(__get_rel_str(dst), (src) ? (const char *)(src) : "(null)"); + strcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? \ + __data_offsets.dst##_ptr_ : "(null)") #undef __assign_rel_str_len #define __assign_rel_str_len(dst, src, len) \ do { \ - memcpy(__get_rel_str(dst), (src), (len)); \ + memcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? \ + __data_offsets.dst##_ptr_ : "(null)", len); \ __get_rel_str(dst)[len] = '\0'; \ } while (0) -- cgit v1.2.3 From e8b737bfb16a0d540413173e8d1574e3bf8cc0e9 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 22 Feb 2024 16:14:17 -0500 Subject: tracing: Do not calculate strlen() twice for __string() fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TRACE_EVENT() macro handles dynamic strings by having: TP_PROTO(struct some_struct *s), TP_ARGS(s), TP_STRUCT__entry( __string(my_string, s->string) ), TP_fast_assign( __assign_str(my_string, s->string); ) TP_printk("%s", __get_str(my_string)) There's even some code that may call a function helper to find the s->string value. The problem with the above is that the work to get the s->string is done twice. Once at the __string() and again in the __assign_str(). The length of the string is calculated via a strlen(), not once, but twice. Once during the __string() macro and again in __assign_str(). But the length is actually already recorded in the data location and here's no reason to call strlen() again. Just use the saved length that was saved in the __string() code for the __assign_str() code. Link: https://lore.kernel.org/linux-trace-kernel/20240222211442.793074999@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Ville Syrjälä Cc: Rodrigo Vivi Cc: Chuck Lever Signed-off-by: Steven Rostedt (Google) --- include/trace/stages/stage6_event_callback.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/trace/stages/stage6_event_callback.h b/include/trace/stages/stage6_event_callback.h index b3e2f321e787..c0e5d097324e 100644 --- a/include/trace/stages/stage6_event_callback.h +++ b/include/trace/stages/stage6_event_callback.h @@ -32,8 +32,9 @@ #undef __assign_str #define __assign_str(dst, src) \ - strcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? \ - __data_offsets.dst##_ptr_ : "(null)") + memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? \ + __data_offsets.dst##_ptr_ : "(null)", \ + __get_dynamic_array_len(dst)) #undef __assign_str_len #define __assign_str_len(dst, src, len) \ @@ -94,8 +95,9 @@ #undef __assign_rel_str #define __assign_rel_str(dst, src) \ - strcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? \ - __data_offsets.dst##_ptr_ : "(null)") + memcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? \ + __data_offsets.dst##_ptr_ : "(null)", \ + __get_rel_dynamic_array_len(dst)) #undef __assign_rel_str_len #define __assign_rel_str_len(dst, src, len) \ -- cgit v1.2.3 From 916849860fa9c7d3caeb144cb5dec8831cf23bfc Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 22 Feb 2024 16:14:18 -0500 Subject: tracing: Use ? : shortcut in trace macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having: #define __assign_str(dst, src) \ memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? \ __data_offsets.dst##_ptr_ : "(null)", \ __get_dynamic_array_len(dst)) Use the ? : shortcut and compact it down to: #define __assign_str(dst, src) \ memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? : "(null)", \ __get_dynamic_array_len(dst)) Link: https://lore.kernel.org/linux-trace-kernel/20240222211442.949327725@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Ville Syrjälä Cc: Rodrigo Vivi Cc: Chuck Lever Suggested-by: Mathieu Desnoyers Signed-off-by: Steven Rostedt (Google) --- include/trace/stages/stage5_get_offsets.h | 4 ++-- include/trace/stages/stage6_event_callback.h | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/trace/stages/stage5_get_offsets.h b/include/trace/stages/stage5_get_offsets.h index 45f8151cf622..20b801ed3fd4 100644 --- a/include/trace/stages/stage5_get_offsets.h +++ b/include/trace/stages/stage5_get_offsets.h @@ -47,7 +47,7 @@ #undef __string #define __string(item, src) __dynamic_array(char, item, \ - strlen((src) ? (const char *)(src) : "(null)") + 1) \ + strlen((const char *)(src) ? : "(null)") + 1) \ __data_offsets->item##_ptr_ = src; #undef __string_len @@ -70,7 +70,7 @@ #undef __rel_string #define __rel_string(item, src) __rel_dynamic_array(char, item, \ - strlen((src) ? (const char *)(src) : "(null)") + 1) \ + strlen((const char *)(src) ? : "(null)") + 1) \ __data_offsets->item##_ptr_ = src; #undef __rel_string_len diff --git a/include/trace/stages/stage6_event_callback.h b/include/trace/stages/stage6_event_callback.h index c0e5d097324e..38732855eadb 100644 --- a/include/trace/stages/stage6_event_callback.h +++ b/include/trace/stages/stage6_event_callback.h @@ -32,15 +32,14 @@ #undef __assign_str #define __assign_str(dst, src) \ - memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? \ - __data_offsets.dst##_ptr_ : "(null)", \ + memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? : "(null)", \ __get_dynamic_array_len(dst)) #undef __assign_str_len #define __assign_str_len(dst, src, len) \ do { \ - memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? \ - __data_offsets.dst##_ptr_ : "(null)", len); \ + memcpy(__get_str(dst), \ + __data_offsets.dst##_ptr_ ? : "(null)", len); \ __get_str(dst)[len] = '\0'; \ } while(0) @@ -95,15 +94,14 @@ #undef __assign_rel_str #define __assign_rel_str(dst, src) \ - memcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? \ - __data_offsets.dst##_ptr_ : "(null)", \ + memcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? : "(null)", \ __get_rel_dynamic_array_len(dst)) #undef __assign_rel_str_len #define __assign_rel_str_len(dst, src, len) \ do { \ - memcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? \ - __data_offsets.dst##_ptr_ : "(null)", len); \ + memcpy(__get_rel_str(dst), \ + __data_offsets.dst##_ptr_ ? : "(null)", len); \ __get_rel_str(dst)[len] = '\0'; \ } while (0) -- cgit v1.2.3 From 70a6ed553f7d3504febac467cb4a0bae621ba3c6 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 22 Feb 2024 16:14:19 -0500 Subject: tracing: Use EVENT_NULL_STR macro instead of open coding "(null)" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TRACE_EVENT macros has some dependency if a __string() field is NULL, where it will save "(null)" as the string. This string is also used by __assign_str(). It's better to create a single macro instead of having something that will not be caught by the compiler if there is an unfortunate typo. Link: https://lore.kernel.org/linux-trace-kernel/20240222211443.106216915@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Ville Syrjälä Cc: Rodrigo Vivi Cc: Chuck Lever Suggested-by: Mathieu Desnoyers Signed-off-by: Steven Rostedt (Google) --- include/linux/trace_events.h | 3 +++ include/trace/events/sunrpc.h | 12 ++++++------ include/trace/stages/stage5_get_offsets.h | 4 ++-- include/trace/stages/stage6_event_callback.h | 8 ++++---- 4 files changed, 15 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index fc6d0af56bb1..6f9bdfb09d1d 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -17,6 +17,9 @@ struct dentry; struct bpf_prog; union bpf_attr; +/* Used for event string fields when they are NULL */ +#define EVENT_NULL_STR "(null)" + const char *trace_print_flags_seq(struct trace_seq *p, const char *delim, unsigned long flags, const struct trace_print_flags *flag_array); diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index cdd3a45e6003..ce6a85b82afa 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -1327,18 +1327,18 @@ TRACE_EVENT(xs_stream_read_data, __field(ssize_t, err) __field(size_t, total) __string(addr, xprt ? xprt->address_strings[RPC_DISPLAY_ADDR] : - "(null)") + EVENT_NULL_STR) __string(port, xprt ? xprt->address_strings[RPC_DISPLAY_PORT] : - "(null)") + EVENT_NULL_STR) ), TP_fast_assign( __entry->err = err; __entry->total = total; __assign_str(addr, xprt ? - xprt->address_strings[RPC_DISPLAY_ADDR] : "(null)"); + xprt->address_strings[RPC_DISPLAY_ADDR] : EVENT_NULL_STR); __assign_str(port, xprt ? - xprt->address_strings[RPC_DISPLAY_PORT] : "(null)"); + xprt->address_strings[RPC_DISPLAY_PORT] : EVENT_NULL_STR); ), TP_printk("peer=[%s]:%s err=%zd total=%zu", __get_str(addr), @@ -1783,7 +1783,7 @@ TRACE_EVENT(svc_process, __string(service, name) __string(procedure, svc_proc_name(rqst)) __string(addr, rqst->rq_xprt ? - rqst->rq_xprt->xpt_remotebuf : "(null)") + rqst->rq_xprt->xpt_remotebuf : EVENT_NULL_STR) ), TP_fast_assign( @@ -1793,7 +1793,7 @@ TRACE_EVENT(svc_process, __assign_str(service, name); __assign_str(procedure, svc_proc_name(rqst)); __assign_str(addr, rqst->rq_xprt ? - rqst->rq_xprt->xpt_remotebuf : "(null)"); + rqst->rq_xprt->xpt_remotebuf : EVENT_NULL_STR); ), TP_printk("addr=%s xid=0x%08x service=%s vers=%u proc=%s", diff --git a/include/trace/stages/stage5_get_offsets.h b/include/trace/stages/stage5_get_offsets.h index 20b801ed3fd4..e6b96608f452 100644 --- a/include/trace/stages/stage5_get_offsets.h +++ b/include/trace/stages/stage5_get_offsets.h @@ -47,7 +47,7 @@ #undef __string #define __string(item, src) __dynamic_array(char, item, \ - strlen((const char *)(src) ? : "(null)") + 1) \ + strlen((const char *)(src) ? : EVENT_NULL_STR) + 1) \ __data_offsets->item##_ptr_ = src; #undef __string_len @@ -70,7 +70,7 @@ #undef __rel_string #define __rel_string(item, src) __rel_dynamic_array(char, item, \ - strlen((const char *)(src) ? : "(null)") + 1) \ + strlen((const char *)(src) ? : EVENT_NULL_STR) + 1) \ __data_offsets->item##_ptr_ = src; #undef __rel_string_len diff --git a/include/trace/stages/stage6_event_callback.h b/include/trace/stages/stage6_event_callback.h index 38732855eadb..2bfd49713b42 100644 --- a/include/trace/stages/stage6_event_callback.h +++ b/include/trace/stages/stage6_event_callback.h @@ -32,14 +32,14 @@ #undef __assign_str #define __assign_str(dst, src) \ - memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? : "(null)", \ + memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? : EVENT_NULL_STR, \ __get_dynamic_array_len(dst)) #undef __assign_str_len #define __assign_str_len(dst, src, len) \ do { \ memcpy(__get_str(dst), \ - __data_offsets.dst##_ptr_ ? : "(null)", len); \ + __data_offsets.dst##_ptr_ ? : EVENT_NULL_STR, len); \ __get_str(dst)[len] = '\0'; \ } while(0) @@ -94,14 +94,14 @@ #undef __assign_rel_str #define __assign_rel_str(dst, src) \ - memcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? : "(null)", \ + memcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? : EVENT_NULL_STR, \ __get_rel_dynamic_array_len(dst)) #undef __assign_rel_str_len #define __assign_rel_str_len(dst, src, len) \ do { \ memcpy(__get_rel_str(dst), \ - __data_offsets.dst##_ptr_ ? : "(null)", len); \ + __data_offsets.dst##_ptr_ ? : EVENT_NULL_STR, len); \ __get_rel_str(dst)[len] = '\0'; \ } while (0) -- cgit v1.2.3 From c759e609030ca37e59866cbc849fdc611cc56292 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Fri, 23 Feb 2024 15:22:06 -0500 Subject: tracing: Remove __assign_str_len() Now that __assign_str() gets the length from the __string() (and __string_len()) macros, there's no reason to have a separate __assign_str_len() macro as __assign_str() can get the length of the string needed. Also remove __assign_rel_str() although it had no users anyway. Link: https://lore.kernel.org/linux-trace-kernel/20240223152206.0b650659@gandalf.local.home Cc: Jeff Layton Acked-by: Chuck Lever Signed-off-by: Steven Rostedt (Google) --- fs/nfsd/trace.h | 8 ++++---- include/trace/stages/stage6_event_callback.h | 28 +++++++++++----------------- samples/trace_events/trace-events-sample.h | 9 +++++---- 3 files changed, 20 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index 288a796b2878..1cd2076210b1 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -104,7 +104,7 @@ TRACE_EVENT(nfsd_compound, TP_fast_assign( __entry->xid = be32_to_cpu(rqst->rq_xid); __entry->opcnt = opcnt; - __assign_str_len(tag, tag, taglen); + __assign_str(tag, tag); ), TP_printk("xid=0x%08x opcnt=%u tag=%s", __entry->xid, __entry->opcnt, __get_str(tag) @@ -485,7 +485,7 @@ TRACE_EVENT(nfsd_dirent, TP_fast_assign( __entry->fh_hash = fhp ? knfsd_fh_hash(&fhp->fh_handle) : 0; __entry->ino = ino; - __assign_str_len(name, name, namlen) + __assign_str(name, name); ), TP_printk("fh_hash=0x%08x ino=%llu name=%s", __entry->fh_hash, __entry->ino, __get_str(name) @@ -906,7 +906,7 @@ DECLARE_EVENT_CLASS(nfsd_clid_class, __entry->flavor = clp->cl_cred.cr_flavor; memcpy(__entry->verifier, (void *)&clp->cl_verifier, NFS4_VERIFIER_SIZE); - __assign_str_len(name, clp->cl_name.data, clp->cl_name.len); + __assign_str(name, clp->cl_name.data); ), TP_printk("addr=%pISpc name='%s' verifier=0x%s flavor=%s client=%08x:%08x", __entry->addr, __get_str(name), @@ -1976,7 +1976,7 @@ TRACE_EVENT(nfsd_ctl_time, TP_fast_assign( __entry->netns_ino = net->ns.inum; __entry->time = time; - __assign_str_len(name, name, namelen); + __assign_str(name, name); ), TP_printk("file=%s time=%d\n", __get_str(name), __entry->time diff --git a/include/trace/stages/stage6_event_callback.h b/include/trace/stages/stage6_event_callback.h index 2bfd49713b42..0c0f50bcdc56 100644 --- a/include/trace/stages/stage6_event_callback.h +++ b/include/trace/stages/stage6_event_callback.h @@ -32,16 +32,13 @@ #undef __assign_str #define __assign_str(dst, src) \ - memcpy(__get_str(dst), __data_offsets.dst##_ptr_ ? : EVENT_NULL_STR, \ - __get_dynamic_array_len(dst)) - -#undef __assign_str_len -#define __assign_str_len(dst, src, len) \ do { \ - memcpy(__get_str(dst), \ - __data_offsets.dst##_ptr_ ? : EVENT_NULL_STR, len); \ - __get_str(dst)[len] = '\0'; \ - } while(0) + char *__str__ = __get_str(dst); \ + int __len__ = __get_dynamic_array_len(dst) - 1; \ + memcpy(__str__, __data_offsets.dst##_ptr_ ? : \ + EVENT_NULL_STR, __len__); \ + __str__[__len__] = '\0'; \ + } while (0) #undef __assign_vstr #define __assign_vstr(dst, fmt, va) \ @@ -94,15 +91,12 @@ #undef __assign_rel_str #define __assign_rel_str(dst, src) \ - memcpy(__get_rel_str(dst), __data_offsets.dst##_ptr_ ? : EVENT_NULL_STR, \ - __get_rel_dynamic_array_len(dst)) - -#undef __assign_rel_str_len -#define __assign_rel_str_len(dst, src, len) \ do { \ - memcpy(__get_rel_str(dst), \ - __data_offsets.dst##_ptr_ ? : EVENT_NULL_STR, len); \ - __get_rel_str(dst)[len] = '\0'; \ + char *__str__ = __get_rel_str(dst); \ + int __len__ = __get_rel_dynamic_array_len(dst) - 1; \ + memcpy(__str__, __data_offsets.dst##_ptr_ ? : \ + EVENT_NULL_STR, __len__); \ + __str__[__len__] = '\0'; \ } while (0) #undef __rel_bitmask diff --git a/samples/trace_events/trace-events-sample.h b/samples/trace_events/trace-events-sample.h index 23f923ccd529..f2d2d56ce8e2 100644 --- a/samples/trace_events/trace-events-sample.h +++ b/samples/trace_events/trace-events-sample.h @@ -163,8 +163,7 @@ * __string(). * * __string_len: This is a helper to a __dynamic_array, but it understands - * that the array has characters in it, and with the combined - * use of __assign_str_len(), it will allocate 'len' + 1 bytes + * that the array has characters in it, it will allocate 'len' + 1 bytes * in the ring buffer and add a '\0' to the string. This is * useful if the string being saved has no terminating '\0' byte. * It requires that the length of the string is known as it acts @@ -174,9 +173,11 @@ * * __string_len(foo, bar, len) * - * To assign this string, use the helper macro __assign_str_len(). + * To assign this string, use the helper macro __assign_str(). + * The length is saved via the __string_len() and is retrieved in + * __assign_str(). * - * __assign_str_len(foo, bar, len); + * __assign_str(foo, bar); * * Then len + 1 is allocated to the ring buffer, and a nul terminating * byte is added. This is similar to: -- cgit v1.2.3 From cf986e57d606849288eecf572800f2bd476fb1ab Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Fri, 23 Feb 2024 16:13:56 -0500 Subject: tracing: Add warning if string in __assign_str() does not match __string() In preparation to remove the second parameter of __assign_str(), make sure it is really a duplicate of __string() by adding a WARN_ON_ONCE(). Link: https://lore.kernel.org/linux-trace-kernel/20240223161356.63b72403@gandalf.local.home Cc: Mathieu Desnoyers Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt (Google) --- include/trace/stages/stage6_event_callback.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/trace/stages/stage6_event_callback.h b/include/trace/stages/stage6_event_callback.h index 0c0f50bcdc56..935608971899 100644 --- a/include/trace/stages/stage6_event_callback.h +++ b/include/trace/stages/stage6_event_callback.h @@ -35,6 +35,7 @@ do { \ char *__str__ = __get_str(dst); \ int __len__ = __get_dynamic_array_len(dst) - 1; \ + WARN_ON_ONCE((src) != __data_offsets.dst##_ptr_); \ memcpy(__str__, __data_offsets.dst##_ptr_ ? : \ EVENT_NULL_STR, __len__); \ __str__[__len__] = '\0'; \ -- cgit v1.2.3 From 0bdfb68c845edd7e2dbe815266bb09641576e22f Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Fri, 23 Feb 2024 16:25:19 -0500 Subject: tracing: Remove second parameter to __assign_rel_str() The second parameter of __assign_rel_str() is no longer used. It can be removed. Note, the only real users of rel_string is user events. This code is just in the sample code for testing purposes. This makes __assign_rel_str() different than __assign_str() but that's fine. __assign_str() is used over 700 places and has a larger impact. That change will come later. Link: https://lore.kernel.org/linux-trace-kernel/20240223162519.2beb8112@gandalf.local.home Cc: Mathieu Desnoyers Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt (Google) --- include/trace/stages/stage6_event_callback.h | 2 +- samples/trace_events/trace-events-sample.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/trace/stages/stage6_event_callback.h b/include/trace/stages/stage6_event_callback.h index 935608971899..a0c15f67eabe 100644 --- a/include/trace/stages/stage6_event_callback.h +++ b/include/trace/stages/stage6_event_callback.h @@ -91,7 +91,7 @@ #define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, -1) #undef __assign_rel_str -#define __assign_rel_str(dst, src) \ +#define __assign_rel_str(dst) \ do { \ char *__str__ = __get_rel_str(dst); \ int __len__ = __get_rel_dynamic_array_len(dst) - 1; \ diff --git a/samples/trace_events/trace-events-sample.h b/samples/trace_events/trace-events-sample.h index 2dfaf7fc7bfa..500981eca74d 100644 --- a/samples/trace_events/trace-events-sample.h +++ b/samples/trace_events/trace-events-sample.h @@ -574,7 +574,7 @@ TRACE_EVENT(foo_rel_loc, ), TP_fast_assign( - __assign_rel_str(foo, foo); + __assign_rel_str(foo); __entry->bar = bar; __assign_rel_bitmask(bitmask, mask, BITS_PER_BYTE * sizeof(unsigned long)); -- cgit v1.2.3 From 19f0423fd55c301c8edaea286e568ec657f42750 Mon Sep 17 00:00:00 2001 From: Huang Yiwei Date: Fri, 23 Feb 2024 16:31:26 +0800 Subject: tracing: Support to dump instance traces by ftrace_dump_on_oops Currently ftrace only dumps the global trace buffer on an OOPs. For debugging a production usecase, instance trace will be helpful to check specific problems since global trace buffer may be used for other purposes. This patch extend the ftrace_dump_on_oops parameter to dump a specific or multiple trace instances: - ftrace_dump_on_oops=0: as before -- don't dump - ftrace_dump_on_oops[=1]: as before -- dump the global trace buffer on all CPUs - ftrace_dump_on_oops=2 or =orig_cpu: as before -- dump the global trace buffer on CPU that triggered the oops - ftrace_dump_on_oops=: new behavior -- dump the tracing instance matching - ftrace_dump_on_oops[=2/orig_cpu],[=2/orig_cpu], [=2/orig_cpu]: new behavior -- dump the global trace buffer and multiple instance buffer on all CPUs, or only dump on CPU that triggered the oops if =2 or =orig_cpu is given Also, the sysctl node can handle the input accordingly. Link: https://lore.kernel.org/linux-trace-kernel/20240223083126.1817731-1-quic_hyiwei@quicinc.com Cc: Ross Zwisler Cc: Cc: Cc: Cc: Cc: Cc: Cc: Signed-off-by: Huang Yiwei Signed-off-by: Steven Rostedt (Google) --- Documentation/admin-guide/kernel-parameters.txt | 26 +++- Documentation/admin-guide/sysctl/kernel.rst | 30 ++++- include/linux/ftrace.h | 4 +- include/linux/kernel.h | 1 + kernel/sysctl.c | 4 +- kernel/trace/trace.c | 156 ++++++++++++++++++------ kernel/trace/trace_selftest.c | 2 +- 7 files changed, 168 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 479ff1737c2f..fa871d53641c 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1572,12 +1572,28 @@ The above will cause the "foo" tracing instance to trigger a snapshot at the end of boot up. - ftrace_dump_on_oops[=orig_cpu] + ftrace_dump_on_oops[=2(orig_cpu) | =][, | + ,=2(orig_cpu)] [FTRACE] will dump the trace buffers on oops. - If no parameter is passed, ftrace will dump - buffers of all CPUs, but if you pass orig_cpu, it will - dump only the buffer of the CPU that triggered the - oops. + If no parameter is passed, ftrace will dump global + buffers of all CPUs, if you pass 2 or orig_cpu, it + will dump only the buffer of the CPU that triggered + the oops, or the specific instance will be dumped if + its name is passed. Multiple instance dump is also + supported, and instances are separated by commas. Each + instance supports only dump on CPU that triggered the + oops by passing 2 or orig_cpu to it. + + ftrace_dump_on_oops=foo=orig_cpu + + The above will dump only the buffer of "foo" instance + on CPU that triggered the oops. + + ftrace_dump_on_oops,foo,bar=orig_cpu + + The above will dump global buffer on all CPUs, the + buffer of "foo" instance on all CPUs and the buffer + of "bar" instance on CPU that triggered the oops. ftrace_filter=[function-list] [FTRACE] Limit the functions traced by the function diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 6584a1f9bfe3..ea8e5f152edc 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -296,12 +296,30 @@ kernel panic). This will output the contents of the ftrace buffers to the console. This is very useful for capturing traces that lead to crashes and outputting them to a serial console. -= =================================================== -0 Disabled (default). -1 Dump buffers of all CPUs. -2 Dump the buffer of the CPU that triggered the oops. -= =================================================== - +======================= =========================================== +0 Disabled (default). +1 Dump buffers of all CPUs. +2(orig_cpu) Dump the buffer of the CPU that triggered the + oops. + Dump the specific instance buffer on all CPUs. +=2(orig_cpu) Dump the specific instance buffer on the CPU + that triggered the oops. +======================= =========================================== + +Multiple instance dump is also supported, and instances are separated +by commas. If global buffer also needs to be dumped, please specify +the dump mode (1/2/orig_cpu) first for global buffer. + +So for example to dump "foo" and "bar" instance buffer on all CPUs, +user can:: + + echo "foo,bar" > /proc/sys/kernel/ftrace_dump_on_oops + +To dump global buffer and "foo" instance buffer on all +CPUs along with the "bar" instance buffer on CPU that triggered the +oops, user can:: + + echo "1,foo,bar=2" > /proc/sys/kernel/ftrace_dump_on_oops ftrace_enabled, stack_tracer_enabled ==================================== diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index e8921871ef9a..54d53f345d14 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -1151,7 +1151,9 @@ static inline void unpause_graph_tracing(void) { } #ifdef CONFIG_TRACING enum ftrace_dump_mode; -extern enum ftrace_dump_mode ftrace_dump_on_oops; +#define MAX_TRACER_SIZE 100 +extern char ftrace_dump_on_oops[]; +extern int ftrace_dump_on_oops_enabled(void); extern int tracepoint_printk; extern void disable_trace_on_warning(void); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index d718fbec72dd..be2e8c0a187e 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -215,6 +215,7 @@ enum ftrace_dump_mode { DUMP_NONE, DUMP_ALL, DUMP_ORIG, + DUMP_PARAM, }; #ifdef CONFIG_TRACING diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 157f7ce2942d..81cc974913bb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1710,9 +1710,9 @@ static struct ctl_table kern_table[] = { { .procname = "ftrace_dump_on_oops", .data = &ftrace_dump_on_oops, - .maxlen = sizeof(int), + .maxlen = MAX_TRACER_SIZE, .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dostring, }, { .procname = "traceoff_on_warning", diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 1e1fd377a1cf..233d1af39fff 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -130,9 +130,12 @@ cpumask_var_t __read_mostly tracing_buffer_mask; * /proc/sys/kernel/ftrace_dump_on_oops * Set 1 if you want to dump buffers of all CPUs * Set 2 if you want to dump the buffer of the CPU that triggered oops + * Set instance name if you want to dump the specific trace instance + * Multiple instance dump is also supported, and instances are seperated + * by commas. */ - -enum ftrace_dump_mode ftrace_dump_on_oops; +/* Set to string format zero to disable by default */ +char ftrace_dump_on_oops[MAX_TRACER_SIZE] = "0"; /* When set, tracing will stop when a WARN*() is hit */ int __disable_trace_on_warning; @@ -178,7 +181,6 @@ static void ftrace_trace_userstack(struct trace_array *tr, struct trace_buffer *buffer, unsigned int trace_ctx); -#define MAX_TRACER_SIZE 100 static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata; static char *default_bootup_tracer; @@ -201,19 +203,33 @@ static int __init set_cmdline_ftrace(char *str) } __setup("ftrace=", set_cmdline_ftrace); +int ftrace_dump_on_oops_enabled(void) +{ + if (!strcmp("0", ftrace_dump_on_oops)) + return 0; + else + return 1; +} + static int __init set_ftrace_dump_on_oops(char *str) { - if (*str++ != '=' || !*str || !strcmp("1", str)) { - ftrace_dump_on_oops = DUMP_ALL; + if (!*str) { + strscpy(ftrace_dump_on_oops, "1", MAX_TRACER_SIZE); return 1; } - if (!strcmp("orig_cpu", str) || !strcmp("2", str)) { - ftrace_dump_on_oops = DUMP_ORIG; - return 1; - } + if (*str == ',') { + strscpy(ftrace_dump_on_oops, "1", MAX_TRACER_SIZE); + strscpy(ftrace_dump_on_oops + 1, str, MAX_TRACER_SIZE - 1); + return 1; + } + + if (*str++ == '=') { + strscpy(ftrace_dump_on_oops, str, MAX_TRACER_SIZE); + return 1; + } - return 0; + return 0; } __setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops); @@ -9832,14 +9848,14 @@ static struct notifier_block trace_die_notifier = { static int trace_die_panic_handler(struct notifier_block *self, unsigned long ev, void *unused) { - if (!ftrace_dump_on_oops) + if (!ftrace_dump_on_oops_enabled()) return NOTIFY_DONE; /* The die notifier requires DIE_OOPS to trigger */ if (self == &trace_die_notifier && ev != DIE_OOPS) return NOTIFY_DONE; - ftrace_dump(ftrace_dump_on_oops); + ftrace_dump(DUMP_PARAM); return NOTIFY_DONE; } @@ -9880,12 +9896,12 @@ trace_printk_seq(struct trace_seq *s) trace_seq_init(s); } -void trace_init_global_iter(struct trace_iterator *iter) +static void trace_init_iter(struct trace_iterator *iter, struct trace_array *tr) { - iter->tr = &global_trace; + iter->tr = tr; iter->trace = iter->tr->current_trace; iter->cpu_file = RING_BUFFER_ALL_CPUS; - iter->array_buffer = &global_trace.array_buffer; + iter->array_buffer = &tr->array_buffer; if (iter->trace && iter->trace->open) iter->trace->open(iter); @@ -9905,22 +9921,19 @@ void trace_init_global_iter(struct trace_iterator *iter) iter->fmt_size = STATIC_FMT_BUF_SIZE; } -void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) +void trace_init_global_iter(struct trace_iterator *iter) +{ + trace_init_iter(iter, &global_trace); +} + +static void ftrace_dump_one(struct trace_array *tr, enum ftrace_dump_mode dump_mode) { /* use static because iter can be a bit big for the stack */ static struct trace_iterator iter; - static atomic_t dump_running; - struct trace_array *tr = &global_trace; unsigned int old_userobj; unsigned long flags; int cnt = 0, cpu; - /* Only allow one dump user at a time. */ - if (atomic_inc_return(&dump_running) != 1) { - atomic_dec(&dump_running); - return; - } - /* * Always turn off tracing when we dump. * We don't need to show trace output of what happens @@ -9929,12 +9942,12 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) * If the user does a sysrq-z, then they can re-enable * tracing with echo 1 > tracing_on. */ - tracing_off(); + tracer_tracing_off(tr); local_irq_save(flags); /* Simulate the iterator */ - trace_init_global_iter(&iter); + trace_init_iter(&iter, tr); for_each_tracing_cpu(cpu) { atomic_inc(&per_cpu_ptr(iter.array_buffer->data, cpu)->disabled); @@ -9945,21 +9958,15 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) /* don't look at user memory in panic mode */ tr->trace_flags &= ~TRACE_ITER_SYM_USEROBJ; - switch (oops_dump_mode) { - case DUMP_ALL: - iter.cpu_file = RING_BUFFER_ALL_CPUS; - break; - case DUMP_ORIG: + if (dump_mode == DUMP_ORIG) iter.cpu_file = raw_smp_processor_id(); - break; - case DUMP_NONE: - goto out_enable; - default: - printk(KERN_TRACE "Bad dumping mode, switching to all CPUs dump\n"); + else iter.cpu_file = RING_BUFFER_ALL_CPUS; - } - printk(KERN_TRACE "Dumping ftrace buffer:\n"); + if (tr == &global_trace) + printk(KERN_TRACE "Dumping ftrace buffer:\n"); + else + printk(KERN_TRACE "Dumping ftrace instance %s buffer:\n", tr->name); /* Did function tracer already get disabled? */ if (ftrace_is_dead()) { @@ -10001,15 +10008,84 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) else printk(KERN_TRACE "---------------------------------\n"); - out_enable: tr->trace_flags |= old_userobj; for_each_tracing_cpu(cpu) { atomic_dec(&per_cpu_ptr(iter.array_buffer->data, cpu)->disabled); } - atomic_dec(&dump_running); local_irq_restore(flags); } + +static void ftrace_dump_by_param(void) +{ + bool first_param = true; + char dump_param[MAX_TRACER_SIZE]; + char *buf, *token, *inst_name; + struct trace_array *tr; + + strscpy(dump_param, ftrace_dump_on_oops, MAX_TRACER_SIZE); + buf = dump_param; + + while ((token = strsep(&buf, ",")) != NULL) { + if (first_param) { + first_param = false; + if (!strcmp("0", token)) + continue; + else if (!strcmp("1", token)) { + ftrace_dump_one(&global_trace, DUMP_ALL); + continue; + } + else if (!strcmp("2", token) || + !strcmp("orig_cpu", token)) { + ftrace_dump_one(&global_trace, DUMP_ORIG); + continue; + } + } + + inst_name = strsep(&token, "="); + tr = trace_array_find(inst_name); + if (!tr) { + printk(KERN_TRACE "Instance %s not found\n", inst_name); + continue; + } + + if (token && (!strcmp("2", token) || + !strcmp("orig_cpu", token))) + ftrace_dump_one(tr, DUMP_ORIG); + else + ftrace_dump_one(tr, DUMP_ALL); + } +} + +void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) +{ + static atomic_t dump_running; + + /* Only allow one dump user at a time. */ + if (atomic_inc_return(&dump_running) != 1) { + atomic_dec(&dump_running); + return; + } + + switch (oops_dump_mode) { + case DUMP_ALL: + ftrace_dump_one(&global_trace, DUMP_ALL); + break; + case DUMP_ORIG: + ftrace_dump_one(&global_trace, DUMP_ORIG); + break; + case DUMP_PARAM: + ftrace_dump_by_param(); + break; + case DUMP_NONE: + break; + default: + printk(KERN_TRACE "Bad dumping mode, switching to all CPUs dump\n"); + ftrace_dump_one(&global_trace, DUMP_ALL); + } + + atomic_dec(&dump_running); +} EXPORT_SYMBOL_GPL(ftrace_dump); #define WRITE_BUFSIZE 4096 diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 529590499b1f..e9c5058a8efd 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -768,7 +768,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace) if (unlikely(++graph_hang_thresh > GRAPH_MAX_FUNC_TEST)) { ftrace_graph_stop(); printk(KERN_WARNING "BUG: Function graph tracer hang!\n"); - if (ftrace_dump_on_oops) { + if (ftrace_dump_on_oops_enabled()) { ftrace_dump(DUMP_ALL); /* ftrace_dump() disables tracing */ tracing_on(); -- cgit v1.2.3 From 1b273124107cc8b9dd52228eba701efa516a3d92 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 28 Feb 2024 13:31:12 -0500 Subject: tracepoints: Use WARN() and not WARN_ON() for warnings There are two WARN_ON*() warnings in tracepoint.h that deal with RCU usage. But when they trigger, especially from using a TRACE_EVENT() macro, the information is not very helpful and is confusing: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at include/trace/events/lock.h:24 lock_acquire+0x2b2/0x2d0 Where the above warning takes you to: TRACE_EVENT(lock_acquire, <<<--- line 24 in lock.h TP_PROTO(struct lockdep_map *lock, unsigned int subclass, int trylock, int read, int check, struct lockdep_map *next_lock, unsigned long ip), [..] Change the WARN_ON_ONCE() to WARN_ONCE() and add a string that allows someone to search for exactly where the bug happened. Link: https://lore.kernel.org/linux-trace-kernel/20240228133112.0d64fb1b@gandalf.local.home Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Cc: Thomas Gleixner Reported-by: Borislav Petkov Tested-by: Borislav Petkov (AMD) Reviewed-by: Paul E. McKenney Signed-off-by: Steven Rostedt (Google) --- include/linux/tracepoint.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 88c0ba623ee6..689b6d71590e 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -199,7 +199,8 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) if (!(cond)) \ return; \ \ - if (WARN_ON_ONCE(RCUIDLE_COND(rcuidle))) \ + if (WARN_ONCE(RCUIDLE_COND(rcuidle), \ + "Bad RCU usage for tracepoint")) \ return; \ \ /* keep srcu and sched-rcu usage consistent */ \ @@ -259,7 +260,8 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) TP_ARGS(args), \ TP_CONDITION(cond), 0); \ if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) { \ - WARN_ON_ONCE(!rcu_is_watching()); \ + WARN_ONCE(!rcu_is_watching(), \ + "RCU not watching for tracepoint"); \ } \ } \ __DECLARE_TRACE_RCU(name, PARAMS(proto), PARAMS(args), \ -- cgit v1.2.3 From b1afefa62ca9d77c8b1d386c3512fb4f21cb7db1 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Tue, 12 Mar 2024 11:30:02 -0400 Subject: tracing: Use strcmp() in __assign_str() WARN_ON() check The WARN_ON() check in __assign_str() to catch where the source variable to the macro doesn't match the source variable to __string() gives an error in clang: >> include/trace/events/sunrpc.h:703:4: warning: result of comparison against a string literal is unspecified (use an explicit string comparison function instead) [-Wstring-compare] 670 | __assign_str(progname, "unknown"); That's because the __assign_str() macro has: WARN_ON_ONCE((src) != __data_offsets.dst##_ptr_); Where "src" is a string literal. Clang warns when comparing a string literal directly as it is undefined to what the value of the literal is. Since this is still to make sure the same string that goes to __string() is the same as __assign_str(), for string literals do a test for that and then use strcmp() in those cases Note that this depends on commit 51270d573a8d ("tracing/net_sched: Fix tracepoints that save qdisc_dev() as a string") being applied, as this was what found that bug. Link: https://lore.kernel.org/linux-trace-kernel/20240312113002.00031668@gandalf.local.home Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Cc: Nathan Chancellor Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202402292111.KIdExylU-lkp@intel.com/ Fixes: 433e1d88a3be ("tracing: Add warning if string in __assign_str() does not match __string()") Signed-off-by: Steven Rostedt (Google) --- include/trace/stages/stage6_event_callback.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/stages/stage6_event_callback.h b/include/trace/stages/stage6_event_callback.h index a0c15f67eabe..83da83a0c14f 100644 --- a/include/trace/stages/stage6_event_callback.h +++ b/include/trace/stages/stage6_event_callback.h @@ -35,7 +35,9 @@ do { \ char *__str__ = __get_str(dst); \ int __len__ = __get_dynamic_array_len(dst) - 1; \ - WARN_ON_ONCE((src) != __data_offsets.dst##_ptr_); \ + WARN_ON_ONCE(__builtin_constant_p(src) ? \ + strcmp((src), __data_offsets.dst##_ptr_) : \ + (src) != __data_offsets.dst##_ptr_); \ memcpy(__str__, __data_offsets.dst##_ptr_ ? : \ EVENT_NULL_STR, __len__); \ __str__[__len__] = '\0'; \ -- cgit v1.2.3 From 7604256cecef34a82333d9f78262d3180f4eb525 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 14 Mar 2024 23:27:54 -0400 Subject: tracing: Add __string_src() helper to help compilers not to get confused The __string() helper macro of the TRACE_EVENT() macro is used to determine how much of the ring buffer needs to be allocated to fit the given source string. Some trace events have a string that is dependent on another variable that could be NULL, and in those cases the string is passed in to be NULL. The __string() macro can handle being passed in a NULL pointer for which it will turn it into "(null)". It does that with: strlen((src) ? (const char *)(src) : "(null)") + 1 But if src itself has the same conditional type it can confuse the compiler. That is: __string(r ? dev(r)->name : NULL) Would turn into: strlen((r ? dev(r)->name : NULL) ? (r ? dev(r)->name : NULL) : "(null)" + 1 For which the compiler thinks that NULL is being passed to strlen() and gives this kind of warning: ./include/trace/stages/stage5_get_offsets.h:50:21: warning: argument 1 null where non-null expected [-Wnonnull] 50 | strlen((src) ? (const char *)(src) : "(null)") + 1) Instead, create a static inline function that takes the src string and will return the string if it is not NULL and will return "(null)" if it is. This will then make the strlen() line: strlen(__string_src(src)) + 1 Where the compiler can see that strlen() will not end up with NULL and does not warn about it. Note that this depends on commit 51270d573a8d ("tracing/net_sched: Fix tracepoints that save qdisc_dev() as a string") being applied, as passing the qdisc_dev() into __string_src() will give an error. Link: https://lore.kernel.org/all/ZfNmfCmgCs4Nc+EH@aschofie-mobl2/ Link: https://lore.kernel.org/linux-trace-kernel/20240314232754.345cea82@rorschach.local.home Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Reported-by: Alison Schofield Signed-off-by: Steven Rostedt (Google) --- include/trace/stages/stage5_get_offsets.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/trace/stages/stage5_get_offsets.h b/include/trace/stages/stage5_get_offsets.h index e6b96608f452..c6a62dfb18ef 100644 --- a/include/trace/stages/stage5_get_offsets.h +++ b/include/trace/stages/stage5_get_offsets.h @@ -9,6 +9,16 @@ #undef __entry #define __entry entry +#ifndef __STAGE5_STRING_SRC_H +#define __STAGE5_STRING_SRC_H +static inline const char *__string_src(const char *str) +{ + if (!str) + return EVENT_NULL_STR; + return str; +} +#endif /* __STAGE5_STRING_SRC_H */ + /* * Fields should never declare an array: i.e. __field(int, arr[5]) * If they do, it will cause issues in parsing and possibly corrupt the @@ -47,7 +57,7 @@ #undef __string #define __string(item, src) __dynamic_array(char, item, \ - strlen((const char *)(src) ? : EVENT_NULL_STR) + 1) \ + strlen(__string_src(src)) + 1) \ __data_offsets->item##_ptr_ = src; #undef __string_len @@ -70,7 +80,7 @@ #undef __rel_string #define __rel_string(item, src) __rel_dynamic_array(char, item, \ - strlen((const char *)(src) ? : EVENT_NULL_STR) + 1) \ + strlen(__string_src(src)) + 1) \ __data_offsets->item##_ptr_ = src; #undef __rel_string_len -- cgit v1.2.3 From f2580a907e5c0e8fc9354fd095b011301c64f949 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Mon, 18 Mar 2024 08:54:08 -0700 Subject: x86/hyperv: Use Hyper-V entropy to seed guest random number generator A Hyper-V host provides its guest VMs with entropy in a custom ACPI table named "OEM0". The entropy bits are updated each time Hyper-V boots the VM, and are suitable for seeding the Linux guest random number generator (rng). See a brief description of OEM0 in [1]. Generation 2 VMs on Hyper-V use UEFI to boot. Existing EFI code in Linux seeds the rng with entropy bits from the EFI_RNG_PROTOCOL. Via this path, the rng is seeded very early during boot with good entropy. The ACPI OEM0 table provided in such VMs is an additional source of entropy. Generation 1 VMs on Hyper-V boot from BIOS. For these VMs, Linux doesn't currently get any entropy from the Hyper-V host. While this is not fundamentally broken because Linux can generate its own entropy, using the Hyper-V host provided entropy would get the rng off to a better start and would do so earlier in the boot process. Improve the rng seeding for Generation 1 VMs by having Hyper-V specific code in Linux take advantage of the OEM0 table to seed the rng. For Generation 2 VMs, use the OEM0 table to provide additional entropy beyond the EFI_RNG_PROTOCOL. Because the OEM0 table is custom to Hyper-V, parse it directly in the Hyper-V code in the Linux kernel and use add_bootloader_randomness() to add it to the rng. Once the entropy bits are read from OEM0, zero them out in the table so they don't appear in /sys/firmware/acpi/tables/OEM0 in the running VM. The zero'ing is done out of an abundance of caution to avoid potential security risks to the rng. Also set the OEM0 data length to zero so a kexec or other subsequent use of the table won't try to use the zero'ed bits. [1] https://download.microsoft.com/download/1/c/9/1c9813b8-089c-4fef-b2ad-ad80e79403ba/Whitepaper%20-%20The%20Windows%2010%20random%20number%20generation%20infrastructure.pdf Signed-off-by: Michael Kelley Reviewed-by: Jason A. Donenfeld Link: https://lore.kernel.org/r/20240318155408.216851-1-mhklinux@outlook.com Signed-off-by: Wei Liu Message-ID: <20240318155408.216851-1-mhklinux@outlook.com> --- arch/arm64/hyperv/mshyperv.c | 2 ++ arch/x86/kernel/cpu/mshyperv.c | 1 + drivers/hv/hv_common.c | 69 ++++++++++++++++++++++++++++++++++++++++++ include/asm-generic/mshyperv.h | 2 ++ 4 files changed, 74 insertions(+) (limited to 'include') diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c index 03ac88bb9d10..b1a4de4eee29 100644 --- a/arch/arm64/hyperv/mshyperv.c +++ b/arch/arm64/hyperv/mshyperv.c @@ -72,6 +72,8 @@ static int __init hyperv_init(void) return ret; } + ms_hyperv_late_init(); + hyperv_initialized = true; return 0; } diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 909a6236a4c0..faf438dce9db 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -639,6 +639,7 @@ const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = { .init.x2apic_available = ms_hyperv_x2apic_available, .init.msi_ext_dest_id = ms_hyperv_msi_ext_dest_id, .init.init_platform = ms_hyperv_init_platform, + .init.guest_late_init = ms_hyperv_late_init, #ifdef CONFIG_AMD_MEM_ENCRYPT .runtime.sev_es_hcall_prepare = hv_sev_es_hcall_prepare, .runtime.sev_es_hcall_finish = hv_sev_es_hcall_finish, diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 5d64cb0a709d..dde3f9b6871a 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -20,8 +20,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -355,6 +358,72 @@ int __init hv_common_init(void) return 0; } +void __init ms_hyperv_late_init(void) +{ + struct acpi_table_header *header; + acpi_status status; + u8 *randomdata; + u32 length, i; + + /* + * Seed the Linux random number generator with entropy provided by + * the Hyper-V host in ACPI table OEM0. + */ + if (!IS_ENABLED(CONFIG_ACPI)) + return; + + status = acpi_get_table("OEM0", 0, &header); + if (ACPI_FAILURE(status) || !header) + return; + + /* + * Since the "OEM0" table name is for OEM specific usage, verify + * that what we're seeing purports to be from Microsoft. + */ + if (strncmp(header->oem_table_id, "MICROSFT", 8)) + goto error; + + /* + * Ensure the length is reasonable. Requiring at least 8 bytes and + * no more than 4K bytes is somewhat arbitrary and just protects + * against a malformed table. Hyper-V currently provides 64 bytes, + * but allow for a change in a later version. + */ + if (header->length < sizeof(*header) + 8 || + header->length > sizeof(*header) + SZ_4K) + goto error; + + length = header->length - sizeof(*header); + randomdata = (u8 *)(header + 1); + + pr_debug("Hyper-V: Seeding rng with %d random bytes from ACPI table OEM0\n", + length); + + add_bootloader_randomness(randomdata, length); + + /* + * To prevent the seed data from being visible in /sys/firmware/acpi, + * zero out the random data in the ACPI table and fixup the checksum. + * The zero'ing is done out of an abundance of caution in avoiding + * potential security risks to the rng. Similarly, reset the table + * length to just the header size so that a subsequent kexec doesn't + * try to use the zero'ed out random data. + */ + for (i = 0; i < length; i++) { + header->checksum += randomdata[i]; + randomdata[i] = 0; + } + + for (i = 0; i < sizeof(header->length); i++) + header->checksum += ((u8 *)&header->length)[i]; + header->length = sizeof(*header); + for (i = 0; i < sizeof(header->length); i++) + header->checksum -= ((u8 *)&header->length)[i]; + +error: + acpi_put_table(header); +} + /* * Hyper-V specific initialization and die code for * individual CPUs that is common across all architectures. diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 452b7c089b71..99935779682d 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -195,6 +195,7 @@ extern u64 (*hv_read_reference_counter)(void); int __init hv_common_init(void); void __init hv_common_free(void); +void __init ms_hyperv_late_init(void); int hv_common_cpu_init(unsigned int cpu); int hv_common_cpu_die(unsigned int cpu); @@ -292,6 +293,7 @@ void hv_setup_dma_ops(struct device *dev, bool coherent); static inline bool hv_is_hyperv_initialized(void) { return false; } static inline bool hv_is_hibernation_supported(void) { return false; } static inline void hyperv_cleanup(void) {} +static inline void ms_hyperv_late_init(void) {} static inline bool hv_is_isolation_supported(void) { return false; } static inline enum hv_isolation_type hv_get_isolation_type(void) { -- cgit v1.2.3 From 807f96abdf14c80f534c78f2d854c2590963345c Mon Sep 17 00:00:00 2001 From: Arthur Grillo Date: Sat, 16 Mar 2024 13:25:20 -0300 Subject: drm: Fix drm_fixp2int_round() making it add 0.5 As well noted by Pekka[1], the rounding of drm_fixp2int_round is wrong. To round a number, you need to add 0.5 to the number and floor that, drm_fixp2int_round() is adding 0.0000076. Make it add 0.5. [1]: https://lore.kernel.org/all/20240301135327.22efe0dd.pekka.paalanen@collabora.com/ Fixes: 8b25320887d7 ("drm: Add fixed-point helper to get rounded integer values") Suggested-by: Pekka Paalanen Reviewed-by: Harry Wentland Reviewed-by: Melissa Wen Signed-off-by: Arthur Grillo Signed-off-by: Melissa Wen Link: https://patchwork.freedesktop.org/patch/msgid/20240316-drm_fixed-v2-1-c1bc2665b5ed@riseup.net --- include/drm/drm_fixed.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h index 0c9f917a4d4b..81572d32db0c 100644 --- a/include/drm/drm_fixed.h +++ b/include/drm/drm_fixed.h @@ -71,7 +71,6 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B) } #define DRM_FIXED_POINT 32 -#define DRM_FIXED_POINT_HALF 16 #define DRM_FIXED_ONE (1ULL << DRM_FIXED_POINT) #define DRM_FIXED_DECIMAL_MASK (DRM_FIXED_ONE - 1) #define DRM_FIXED_DIGITS_MASK (~DRM_FIXED_DECIMAL_MASK) @@ -90,7 +89,7 @@ static inline int drm_fixp2int(s64 a) static inline int drm_fixp2int_round(s64 a) { - return drm_fixp2int(a + (1 << (DRM_FIXED_POINT_HALF - 1))); + return drm_fixp2int(a + DRM_FIXED_ONE / 2); } static inline int drm_fixp2int_ceil(s64 a) -- cgit v1.2.3 From ec6ecb844d14d38b7dae8beb74e3d65db9c7b3e6 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 25 Jan 2024 23:20:39 +0000 Subject: virtio: uapi: Drop __packed attribute in linux/virtio_pci.h Commit 92792ac752aa ("virtio-pci: Introduce admin command sending function") added "__packed" structures to UAPI header linux/virtio_pci.h. This triggers build failures in the consumer userspace applications without proper "definition" of __packed (e.g., kvmtool build fails). Moreover, the structures are already packed well, and doesn't need explicit packing, similar to the rest of the structures in all virtio_* headers. Remove the __packed attribute. Fixes: 92792ac752aa ("virtio-pci: Introduce admin command sending function") Cc: Feng Liu Cc: Michael S. Tsirkin Cc: Yishai Hadas Cc: Alex Williamson Cc: Jean-Philippe Brucker Reviewed-by: Jean-Philippe Brucker Acked-by: Michael S. Tsirkin Signed-off-by: Suzuki K Poulose Message-Id: <20240125232039.913606-1-suzuki.poulose@arm.com> Signed-off-by: Michael S. Tsirkin --- include/uapi/linux/virtio_pci.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index ef3810dee7ef..a8208492e822 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -240,7 +240,7 @@ struct virtio_pci_cfg_cap { #define VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ 0x5 #define VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO 0x6 -struct __packed virtio_admin_cmd_hdr { +struct virtio_admin_cmd_hdr { __le16 opcode; /* * 1 - SR-IOV @@ -252,20 +252,20 @@ struct __packed virtio_admin_cmd_hdr { __le64 group_member_id; }; -struct __packed virtio_admin_cmd_status { +struct virtio_admin_cmd_status { __le16 status; __le16 status_qualifier; /* Unused, reserved for future extensions. */ __u8 reserved2[4]; }; -struct __packed virtio_admin_cmd_legacy_wr_data { +struct virtio_admin_cmd_legacy_wr_data { __u8 offset; /* Starting offset of the register(s) to write. */ __u8 reserved[7]; __u8 registers[]; }; -struct __packed virtio_admin_cmd_legacy_rd_data { +struct virtio_admin_cmd_legacy_rd_data { __u8 offset; /* Starting offset of the register(s) to read. */ }; @@ -275,7 +275,7 @@ struct __packed virtio_admin_cmd_legacy_rd_data { #define VIRTIO_ADMIN_CMD_MAX_NOTIFY_INFO 4 -struct __packed virtio_admin_cmd_notify_info_data { +struct virtio_admin_cmd_notify_info_data { __u8 flags; /* 0 = end of list, 1 = owner device, 2 = member device */ __u8 bar; /* BAR of the member or the owner device */ __u8 padding[6]; -- cgit v1.2.3 From 1496c47065f9f8413296780c63374daee9bdae20 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Sat, 3 Feb 2024 00:38:56 +0800 Subject: vhost-vdpa: uapi to support reporting per vq size The size of a virtqueue is a per vq configuration. This commit introduce a new ioctl uAPI to support this flexibility. Signed-off-by: Zhu Lingshan Message-Id: <20240202163905.8834-2-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- include/uapi/linux/vhost.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/vhost.h b/include/uapi/linux/vhost.h index 649560c685f1..bea697390613 100644 --- a/include/uapi/linux/vhost.h +++ b/include/uapi/linux/vhost.h @@ -227,4 +227,11 @@ */ #define VHOST_VDPA_GET_VRING_DESC_GROUP _IOWR(VHOST_VIRTIO, 0x7F, \ struct vhost_vring_state) + +/* Get the queue size of a specific virtqueue. + * userspace set the vring index in vhost_vring_state.index + * kernel set the queue size in vhost_vring_state.num + */ +#define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x80, \ + struct vhost_vring_state) #endif -- cgit v1.2.3 From 0a926fc972532788719fd03c4a44724ec23c1875 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Sat, 3 Feb 2024 00:38:57 +0800 Subject: vDPA: introduce get_vq_size to vdpa_config_ops This commit introduces a new interface get_vq_size to vDPA config ops, this new interface intends to report the size of a specific virtqueue Signed-off-by: Zhu Lingshan Message-Id: <20240202163905.8834-3-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vhost/vdpa.c | 8 ++++++++ include/linux/vdpa.h | 5 +++++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index aef92a7c57f3..ba52d128aeb7 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -687,6 +687,14 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, if (!ops->set_group_asid) return -EOPNOTSUPP; return ops->set_group_asid(vdpa, idx, s.num); + case VHOST_VDPA_GET_VRING_SIZE: + if (!ops->get_vq_size) + return -EOPNOTSUPP; + s.index = idx; + s.num = ops->get_vq_size(vdpa, idx); + if (copy_to_user(argp, &s, sizeof(s))) + return -EFAULT; + return 0; case VHOST_GET_VRING_BASE: r = ops->get_vq_state(v->vdpa, idx, &vq_state); if (r) diff --git a/include/linux/vdpa.h b/include/linux/vdpa.h index db15ac07f8a6..4097e8e92860 100644 --- a/include/linux/vdpa.h +++ b/include/linux/vdpa.h @@ -195,6 +195,10 @@ struct vdpa_map_file { * @idx: virtqueue index * Returns int: irq number of a virtqueue, * negative number if no irq assigned. + * @get_vq_size: Get the size of a specific virtqueue (optional) + * @vdev: vdpa device + * @idx: virtqueue index + * Return u16: the size of the virtqueue * @get_vq_align: Get the virtqueue align requirement * for the device * @vdev: vdpa device @@ -386,6 +390,7 @@ struct vdpa_config_ops { (*get_vq_notification)(struct vdpa_device *vdev, u16 idx); /* vq irq is not expected to be changed once DRIVER_OK is set */ int (*get_vq_irq)(struct vdpa_device *vdev, u16 idx); + u16 (*get_vq_size)(struct vdpa_device *vdev, u16 idx); /* Device ops */ u32 (*get_vq_align)(struct vdpa_device *vdev); -- cgit v1.2.3 From c2475a9a789721bfdcc1b16aaf61ccfecb891914 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:55:57 +0800 Subject: vDPA: report virtio-block capacity to user space This commit allows userspace to query capacity of a virtio-block device. Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-2-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/vdpa.h | 1 + include/uapi/linux/vdpa.h | 2 ++ 3 files changed, 38 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index c73c06102e7e..613bec31f59e 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -944,6 +944,38 @@ static int vdpa_dev_net_config_fill(struct vdpa_device *vdev, struct sk_buff *ms return vdpa_dev_net_mq_config_fill(msg, features_device, &config); } +static int +vdpa_dev_blk_capacity_config_fill(struct sk_buff *msg, + const struct virtio_blk_config *config) +{ + u64 val_u64; + + val_u64 = __virtio64_to_cpu(true, config->capacity); + + return nla_put_u64_64bit(msg, VDPA_ATTR_DEV_BLK_CFG_CAPACITY, + val_u64, VDPA_ATTR_PAD); +} + +static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, + struct sk_buff *msg) +{ + struct virtio_blk_config config = {}; + u64 features_device; + + vdev->config->get_config(vdev, 0, &config, sizeof(config)); + + features_device = vdev->config->get_device_features(vdev); + + if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_FEATURES, features_device, + VDPA_ATTR_PAD)) + return -EMSGSIZE; + + if (vdpa_dev_blk_capacity_config_fill(msg, &config)) + return -EMSGSIZE; + + return 0; +} + static int vdpa_dev_config_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, u32 seq, int flags, struct netlink_ext_ack *extack) @@ -988,6 +1020,9 @@ vdpa_dev_config_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, case VIRTIO_ID_NET: err = vdpa_dev_net_config_fill(vdev, msg); break; + case VIRTIO_ID_BLOCK: + err = vdpa_dev_blk_config_fill(vdev, msg); + break; default: err = -EOPNOTSUPP; break; diff --git a/include/linux/vdpa.h b/include/linux/vdpa.h index 4097e8e92860..7977ca03ac7a 100644 --- a/include/linux/vdpa.h +++ b/include/linux/vdpa.h @@ -7,6 +7,7 @@ #include #include #include +#include #include /** diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 54b649ab0f22..1bf69226cb96 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -56,6 +56,8 @@ enum vdpa_attr { /* virtio features that are provisioned to the vDPA device */ VDPA_ATTR_DEV_FEATURES, /* u64 */ + VDPA_ATTR_DEV_BLK_CFG_CAPACITY, /* u64 */ + /* new attributes must be added above here */ VDPA_ATTR_MAX, }; -- cgit v1.2.3 From 330b8aea692410fbe8f0d058e01aea2986613ff7 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:55:58 +0800 Subject: vDPA: report virtio-block max segment size to user space This commit allows reporting the max size of any single segment of virtio-block devices to user space. Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-3-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 17 +++++++++++++++++ include/uapi/linux/vdpa.h | 1 + 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index 613bec31f59e..db355d4683ba 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -956,6 +956,20 @@ vdpa_dev_blk_capacity_config_fill(struct sk_buff *msg, val_u64, VDPA_ATTR_PAD); } +static int +vdpa_dev_blk_seg_size_config_fill(struct sk_buff *msg, u64 features, + const struct virtio_blk_config *config) +{ + u32 val_u32; + + if ((features & BIT_ULL(VIRTIO_BLK_F_SIZE_MAX)) == 0) + return 0; + + val_u32 = __virtio32_to_cpu(true, config->size_max); + + return nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_SEG_SIZE, val_u32); +} + static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, struct sk_buff *msg) { @@ -973,6 +987,9 @@ static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, if (vdpa_dev_blk_capacity_config_fill(msg, &config)) return -EMSGSIZE; + if (vdpa_dev_blk_seg_size_config_fill(msg, features_device, &config)) + return -EMSGSIZE; + return 0; } diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 1bf69226cb96..586bce3c906a 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -57,6 +57,7 @@ enum vdpa_attr { VDPA_ATTR_DEV_FEATURES, /* u64 */ VDPA_ATTR_DEV_BLK_CFG_CAPACITY, /* u64 */ + VDPA_ATTR_DEV_BLK_CFG_SEG_SIZE, /* u32 */ /* new attributes must be added above here */ VDPA_ATTR_MAX, -- cgit v1.2.3 From 3a1d33fb7e5f96471aa7bfe97dd09afc2aa03b39 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:55:59 +0800 Subject: vDPA: report virtio-block block-size to user space This commit allows reporting the block size of a virtio-block device to user space. Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-4-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 18 ++++++++++++++++++ include/uapi/linux/vdpa.h | 1 + 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index db355d4683ba..d96dc2ad48aa 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -970,6 +970,21 @@ vdpa_dev_blk_seg_size_config_fill(struct sk_buff *msg, u64 features, return nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_SEG_SIZE, val_u32); } +/* fill the block size*/ +static int +vdpa_dev_blk_block_size_config_fill(struct sk_buff *msg, u64 features, + const struct virtio_blk_config *config) +{ + u32 val_u32; + + if ((features & BIT_ULL(VIRTIO_BLK_F_BLK_SIZE)) == 0) + return 0; + + val_u32 = __virtio32_to_cpu(true, config->blk_size); + + return nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_BLK_SIZE, val_u32); +} + static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, struct sk_buff *msg) { @@ -990,6 +1005,9 @@ static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, if (vdpa_dev_blk_seg_size_config_fill(msg, features_device, &config)) return -EMSGSIZE; + if (vdpa_dev_blk_block_size_config_fill(msg, features_device, &config)) + return -EMSGSIZE; + return 0; } diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 586bce3c906a..f3baf7ea7d6d 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -58,6 +58,7 @@ enum vdpa_attr { VDPA_ATTR_DEV_BLK_CFG_CAPACITY, /* u64 */ VDPA_ATTR_DEV_BLK_CFG_SEG_SIZE, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_BLK_SIZE, /* u32 */ /* new attributes must be added above here */ VDPA_ATTR_MAX, -- cgit v1.2.3 From 81f64e1d911550832b8065631c28c87c96ffca36 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:56:00 +0800 Subject: vDPA: report virtio-block max segments in a request to user space This commit allows vDPA reporting the maximum number of segments in a request of virtio-block devices to user space. Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-5-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 17 +++++++++++++++++ include/uapi/linux/vdpa.h | 1 + 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index d96dc2ad48aa..3e3c84bd0337 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -985,6 +985,20 @@ vdpa_dev_blk_block_size_config_fill(struct sk_buff *msg, u64 features, return nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_BLK_SIZE, val_u32); } +static int +vdpa_dev_blk_seg_max_config_fill(struct sk_buff *msg, u64 features, + const struct virtio_blk_config *config) +{ + u32 val_u32; + + if ((features & BIT_ULL(VIRTIO_BLK_F_SEG_MAX)) == 0) + return 0; + + val_u32 = __virtio32_to_cpu(true, config->seg_max); + + return nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_SEG_MAX, val_u32); +} + static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, struct sk_buff *msg) { @@ -1008,6 +1022,9 @@ static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, if (vdpa_dev_blk_block_size_config_fill(msg, features_device, &config)) return -EMSGSIZE; + if (vdpa_dev_blk_seg_max_config_fill(msg, features_device, &config)) + return -EMSGSIZE; + return 0; } diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index f3baf7ea7d6d..2dc92fca2052 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -59,6 +59,7 @@ enum vdpa_attr { VDPA_ATTR_DEV_BLK_CFG_CAPACITY, /* u64 */ VDPA_ATTR_DEV_BLK_CFG_SEG_SIZE, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_BLK_SIZE, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_SEG_MAX, /* u32 */ /* new attributes must be added above here */ VDPA_ATTR_MAX, -- cgit v1.2.3 From 54fb04b02efd8f56e388443d5f9e03e68eef48e6 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:56:01 +0800 Subject: vDPA: report virtio-block MQ info to user space This commits allows vDPA reporting virtio-block multi-queue configuration to user sapce. Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-6-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 16 ++++++++++++++++ include/uapi/linux/vdpa.h | 1 + 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index 3e3c84bd0337..4c348da1f2f7 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -999,6 +999,19 @@ vdpa_dev_blk_seg_max_config_fill(struct sk_buff *msg, u64 features, return nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_SEG_MAX, val_u32); } +static int vdpa_dev_blk_mq_config_fill(struct sk_buff *msg, u64 features, + const struct virtio_blk_config *config) +{ + u16 val_u16; + + if ((features & BIT_ULL(VIRTIO_BLK_F_MQ)) == 0) + return 0; + + val_u16 = __virtio16_to_cpu(true, config->num_queues); + + return nla_put_u16(msg, VDPA_ATTR_DEV_BLK_CFG_NUM_QUEUES, val_u16); +} + static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, struct sk_buff *msg) { @@ -1025,6 +1038,9 @@ static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, if (vdpa_dev_blk_seg_max_config_fill(msg, features_device, &config)) return -EMSGSIZE; + if (vdpa_dev_blk_mq_config_fill(msg, features_device, &config)) + return -EMSGSIZE; + return 0; } diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 2dc92fca2052..3c8e3c87a864 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -60,6 +60,7 @@ enum vdpa_attr { VDPA_ATTR_DEV_BLK_CFG_SEG_SIZE, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_BLK_SIZE, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_SEG_MAX, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_NUM_QUEUES, /* u16 */ /* new attributes must be added above here */ VDPA_ATTR_MAX, -- cgit v1.2.3 From c9d989b4ababe3ec7291b204f365eaadec0afa26 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:56:02 +0800 Subject: vDPA: report virtio-block topology info to user space This commit allows vDPA reporting topology information of virtio-blk devices to user space, includes: 1) the number of logical blocks per physical block 2) offset of first aligned logical block 3) suggested minimum I/O size in blocks 4) optimal (suggested maximum) I/O size in blocks Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-7-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 32 ++++++++++++++++++++++++++++++++ include/uapi/linux/vdpa.h | 4 ++++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index 4c348da1f2f7..76b2e31d6229 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -1012,6 +1012,35 @@ static int vdpa_dev_blk_mq_config_fill(struct sk_buff *msg, u64 features, return nla_put_u16(msg, VDPA_ATTR_DEV_BLK_CFG_NUM_QUEUES, val_u16); } +static int vdpa_dev_blk_topology_config_fill(struct sk_buff *msg, u64 features, + const struct virtio_blk_config *config) +{ + u16 min_io_size; + u32 opt_io_size; + + if ((features & BIT_ULL(VIRTIO_BLK_F_TOPOLOGY)) == 0) + return 0; + + min_io_size = __virtio16_to_cpu(true, config->min_io_size); + opt_io_size = __virtio32_to_cpu(true, config->opt_io_size); + + if (nla_put_u8(msg, VDPA_ATTR_DEV_BLK_CFG_PHY_BLK_EXP, + config->physical_block_exp)) + return -EMSGSIZE; + + if (nla_put_u8(msg, VDPA_ATTR_DEV_BLK_CFG_ALIGN_OFFSET, + config->alignment_offset)) + return -EMSGSIZE; + + if (nla_put_u16(msg, VDPA_ATTR_DEV_BLK_CFG_MIN_IO_SIZE, min_io_size)) + return -EMSGSIZE; + + if (nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_OPT_IO_SIZE, opt_io_size)) + return -EMSGSIZE; + + return 0; +} + static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, struct sk_buff *msg) { @@ -1041,6 +1070,9 @@ static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, if (vdpa_dev_blk_mq_config_fill(msg, features_device, &config)) return -EMSGSIZE; + if (vdpa_dev_blk_topology_config_fill(msg, features_device, &config)) + return -EMSGSIZE; + return 0; } diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 3c8e3c87a864..5cc70614c97a 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -61,6 +61,10 @@ enum vdpa_attr { VDPA_ATTR_DEV_BLK_CFG_BLK_SIZE, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_SEG_MAX, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_NUM_QUEUES, /* u16 */ + VDPA_ATTR_DEV_BLK_CFG_PHY_BLK_EXP, /* u8 */ + VDPA_ATTR_DEV_BLK_CFG_ALIGN_OFFSET, /* u8 */ + VDPA_ATTR_DEV_BLK_CFG_MIN_IO_SIZE, /* u16 */ + VDPA_ATTR_DEV_BLK_CFG_OPT_IO_SIZE, /* u32 */ /* new attributes must be added above here */ VDPA_ATTR_MAX, -- cgit v1.2.3 From 65848f46e1d6c01fbd0d79a1c051d978a6e6002e Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:56:03 +0800 Subject: vDPA: report virtio-block discarding configuration to user space This commit reports virtio-blk discarding configuration to user space,includes: 1) the maximum discard sectors 2) maximum number of discard segments for the block driver to use 3) the alignment for splitting a discarding request Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-8-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 26 ++++++++++++++++++++++++++ include/uapi/linux/vdpa.h | 3 +++ 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index 76b2e31d6229..9348818b8bf9 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -1041,6 +1041,29 @@ static int vdpa_dev_blk_topology_config_fill(struct sk_buff *msg, u64 features, return 0; } +static int vdpa_dev_blk_discard_config_fill(struct sk_buff *msg, u64 features, + const struct virtio_blk_config *config) +{ + u32 val_u32; + + if ((features & BIT_ULL(VIRTIO_BLK_F_DISCARD)) == 0) + return 0; + + val_u32 = __virtio32_to_cpu(true, config->max_discard_sectors); + if (nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_MAX_DISCARD_SEC, val_u32)) + return -EMSGSIZE; + + val_u32 = __virtio32_to_cpu(true, config->max_discard_seg); + if (nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_MAX_DISCARD_SEG, val_u32)) + return -EMSGSIZE; + + val_u32 = __virtio32_to_cpu(true, config->discard_sector_alignment); + if (nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_DISCARD_SEC_ALIGN, val_u32)) + return -EMSGSIZE; + + return 0; +} + static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, struct sk_buff *msg) { @@ -1073,6 +1096,9 @@ static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, if (vdpa_dev_blk_topology_config_fill(msg, features_device, &config)) return -EMSGSIZE; + if (vdpa_dev_blk_discard_config_fill(msg, features_device, &config)) + return -EMSGSIZE; + return 0; } diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 5cc70614c97a..30887931a220 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -65,6 +65,9 @@ enum vdpa_attr { VDPA_ATTR_DEV_BLK_CFG_ALIGN_OFFSET, /* u8 */ VDPA_ATTR_DEV_BLK_CFG_MIN_IO_SIZE, /* u16 */ VDPA_ATTR_DEV_BLK_CFG_OPT_IO_SIZE, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_MAX_DISCARD_SEC, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_MAX_DISCARD_SEG, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_DISCARD_SEC_ALIGN,/* u32 */ /* new attributes must be added above here */ VDPA_ATTR_MAX, -- cgit v1.2.3 From 6bdc7846e627ec21fc79a6d2554f69546a7b4985 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:56:04 +0800 Subject: vDPA: report virtio-block write zeroes configuration to user space This commits reports write zeroes configuration of virtio-block devices to user space, includes: 1)maximum write zeroes sectors size 2)maximum write zeroes segment number Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-9-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 23 +++++++++++++++++++++++ include/uapi/linux/vdpa.h | 2 ++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index 9348818b8bf9..bcea9f880042 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -1064,6 +1064,26 @@ static int vdpa_dev_blk_discard_config_fill(struct sk_buff *msg, u64 features, return 0; } +static int +vdpa_dev_blk_write_zeroes_config_fill(struct sk_buff *msg, u64 features, + const struct virtio_blk_config *config) +{ + u32 val_u32; + + if ((features & BIT_ULL(VIRTIO_BLK_F_WRITE_ZEROES)) == 0) + return 0; + + val_u32 = __virtio32_to_cpu(true, config->max_write_zeroes_sectors); + if (nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEC, val_u32)) + return -EMSGSIZE; + + val_u32 = __virtio32_to_cpu(true, config->max_write_zeroes_seg); + if (nla_put_u32(msg, VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEG, val_u32)) + return -EMSGSIZE; + + return 0; +} + static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, struct sk_buff *msg) { @@ -1099,6 +1119,9 @@ static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, if (vdpa_dev_blk_discard_config_fill(msg, features_device, &config)) return -EMSGSIZE; + if (vdpa_dev_blk_write_zeroes_config_fill(msg, features_device, &config)) + return -EMSGSIZE; + return 0; } diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 30887931a220..797d5708492f 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -68,6 +68,8 @@ enum vdpa_attr { VDPA_ATTR_DEV_BLK_CFG_MAX_DISCARD_SEC, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_MAX_DISCARD_SEG, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_DISCARD_SEC_ALIGN,/* u32 */ + VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEC, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEG, /* u32 */ /* new attributes must be added above here */ VDPA_ATTR_MAX, -- cgit v1.2.3 From ae1374b7f72c973631ce881a654664fd2b40c4b9 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:56:05 +0800 Subject: vDPA: report virtio-block read-only info to user space This commit report read-only information of virtio-blk devices to user space. Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-10-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 14 ++++++++++++++ include/uapi/linux/vdpa.h | 1 + 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index bcea9f880042..2d02ea60af0c 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -1084,6 +1084,17 @@ vdpa_dev_blk_write_zeroes_config_fill(struct sk_buff *msg, u64 features, return 0; } +static int vdpa_dev_blk_ro_config_fill(struct sk_buff *msg, u64 features) +{ + u8 ro; + + ro = ((features & BIT_ULL(VIRTIO_BLK_F_RO)) == 0) ? 0 : 1; + if (nla_put_u8(msg, VDPA_ATTR_DEV_BLK_CFG_READ_ONLY, ro)) + return -EMSGSIZE; + + return 0; +} + static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, struct sk_buff *msg) { @@ -1122,6 +1133,9 @@ static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, if (vdpa_dev_blk_write_zeroes_config_fill(msg, features_device, &config)) return -EMSGSIZE; + if (vdpa_dev_blk_ro_config_fill(msg, features_device)) + return -EMSGSIZE; + return 0; } diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 797d5708492f..4be8e3a15874 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -70,6 +70,7 @@ enum vdpa_attr { VDPA_ATTR_DEV_BLK_CFG_DISCARD_SEC_ALIGN,/* u32 */ VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEC, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEG, /* u32 */ + VDPA_ATTR_DEV_BLK_CFG_READ_ONLY, /* u8 */ /* new attributes must be added above here */ VDPA_ATTR_MAX, -- cgit v1.2.3 From 1ac61ddfee93278f87370f6c43ed669b845f9037 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Mon, 19 Feb 2024 02:56:06 +0800 Subject: vDPA: report virtio-blk flush info to user space This commit reports whether a virtio-blk device support cache flush command to user space Signed-off-by: Zhu Lingshan Message-Id: <20240218185606.13509-11-lingshan.zhu@intel.com> Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa.c | 14 ++++++++++++++ include/uapi/linux/vdpa.h | 1 + 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index 2d02ea60af0c..b246067e074b 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -1095,6 +1095,17 @@ static int vdpa_dev_blk_ro_config_fill(struct sk_buff *msg, u64 features) return 0; } +static int vdpa_dev_blk_flush_config_fill(struct sk_buff *msg, u64 features) +{ + u8 flush; + + flush = ((features & BIT_ULL(VIRTIO_BLK_F_FLUSH)) == 0) ? 0 : 1; + if (nla_put_u8(msg, VDPA_ATTR_DEV_BLK_CFG_FLUSH, flush)) + return -EMSGSIZE; + + return 0; +} + static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, struct sk_buff *msg) { @@ -1136,6 +1147,9 @@ static int vdpa_dev_blk_config_fill(struct vdpa_device *vdev, if (vdpa_dev_blk_ro_config_fill(msg, features_device)) return -EMSGSIZE; + if (vdpa_dev_blk_flush_config_fill(msg, features_device)) + return -EMSGSIZE; + return 0; } diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 4be8e3a15874..43c51698195c 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -71,6 +71,7 @@ enum vdpa_attr { VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEC, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_MAX_WRITE_ZEROES_SEG, /* u32 */ VDPA_ATTR_DEV_BLK_CFG_READ_ONLY, /* u8 */ + VDPA_ATTR_DEV_BLK_CFG_FLUSH, /* u8 */ /* new attributes must be added above here */ VDPA_ATTR_MAX, -- cgit v1.2.3 From f6e0a4984c2e7244689ea87b62b433bed9d07e94 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 14 Mar 2024 20:08:45 +0000 Subject: net: move dev->state into net_device_read_txrx group dev->state can be read in rx and tx fast paths. netif_running() which needs dev->state is called from - enqueue_to_backlog() [RX path] - __dev_direct_xmit() [TX path] Fixes: 43a71cd66b9c ("net-device: reorganize net_device fast path variables") Signed-off-by: Eric Dumazet Cc: Coco Li Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240314200845.3050179-1-edumazet@google.com Signed-off-by: Paolo Abeni --- Documentation/networking/net_cachelines/net_device.rst | 2 +- include/linux/netdevice.h | 2 +- net/core/dev.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst index dceb49d56a91..70c4fb9d4e5c 100644 --- a/Documentation/networking/net_cachelines/net_device.rst +++ b/Documentation/networking/net_cachelines/net_device.rst @@ -13,7 +13,7 @@ struct_dev_ifalias* ifalias unsigned_long mem_end unsigned_long mem_start unsigned_long base_addr -unsigned_long state +unsigned_long state read_mostly read_mostly netif_running(dev) struct_list_head dev_list struct_list_head napi_list struct_list_head unreg_list diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c6f6ac779b34..cb37817d6382 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2072,6 +2072,7 @@ struct net_device { struct pcpu_sw_netstats __percpu *tstats; struct pcpu_dstats __percpu *dstats; }; + unsigned long state; unsigned int flags; unsigned short hard_header_len; netdev_features_t features; @@ -2117,7 +2118,6 @@ struct net_device { * part of the usual set specified in Space.c. */ - unsigned long state; struct list_head dev_list; struct list_head napi_list; diff --git a/net/core/dev.c b/net/core/dev.c index 722787c32755..303a6ff46e4e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -11665,11 +11665,12 @@ static void __init net_dev_struct_check(void) /* TXRX read-mostly hotpath */ CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, lstats); + CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, state); CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, flags); CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, hard_header_len); CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, features); CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, ip6_ptr); - CACHELINE_ASSERT_GROUP_SIZE(struct net_device, net_device_read_txrx, 38); + CACHELINE_ASSERT_GROUP_SIZE(struct net_device, net_device_read_txrx, 46); /* RX read-mostly hotpath */ CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_rx, ptype_specific); -- cgit v1.2.3 From f6e922365faf4cd576bd1cf3e64b58c8a32e1856 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 18 Mar 2024 09:54:27 -0700 Subject: xsk: Don't assume metadata is always requested in TX completion `compl->tx_timestam != NULL` means that the user has explicitly requested the metadata via XDP_TX_METADATA+XDP_TX_METADATA_TIMESTAMP. Fixes: 48eb03dd2630 ("xsk: Add TX timestamp and TX checksum offload support") Reported-by: Daniele Salvatore Albano Signed-off-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann Tested-by: Daniele Salvatore Albano Link: https://lore.kernel.org/bpf/20240318165427.1403313-1-sdf@google.com --- include/net/xdp_sock.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 3cb4dc9bd70e..3d54de168a6d 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -188,6 +188,8 @@ static inline void xsk_tx_metadata_complete(struct xsk_tx_metadata_compl *compl, { if (!compl) return; + if (!compl->tx_timestamp) + return; *compl->tx_timestamp = ops->tmo_fill_timestamp(priv); } -- cgit v1.2.3 From 24f5bb9f24ad80da6c6f83ba6124b5188c5384b2 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Tue, 19 Mar 2024 13:39:59 -0400 Subject: tracing: Just use strcmp() for testing __string() and __assign_str() match As __assign_str() no longer uses its "src" parameter, there's a check to make sure nothing depends on it being different than what was passed to __string(). It originally just compared the pointer passed to __string() with the pointer passed into __assign_str() via the "src" parameter. But there's a couple of outliers that just pass in a quoted string constant, where comparing the pointers is UB to the compiler, as the compiler is free to create multiple copies of the same string constant. Instead, just use strcmp(). It may slow down the trace event, but this will eventually be removed. Also, fix the issue of passing NULL to strcmp() by adding a WARN_ON() to make sure that both "src" and the pointer saved in __string() are either both NULL or have content, and then checking if "src" is not NULL before performing the strcmp(). Link: https://lore.kernel.org/all/CAHk-=wjxX16kWd=uxG5wzqt=aXoYDf1BgWOKk+qVmAO0zh7sjA@mail.gmail.com/ Fixes: b1afefa62ca9 ("tracing: Use strcmp() in __assign_str() WARN_ON() check") Reported-by: Linus Torvalds Signed-off-by: Steven Rostedt (Google) Signed-off-by: Linus Torvalds --- include/trace/stages/stage6_event_callback.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/trace/stages/stage6_event_callback.h b/include/trace/stages/stage6_event_callback.h index 83da83a0c14f..3690e677263f 100644 --- a/include/trace/stages/stage6_event_callback.h +++ b/include/trace/stages/stage6_event_callback.h @@ -35,9 +35,8 @@ do { \ char *__str__ = __get_str(dst); \ int __len__ = __get_dynamic_array_len(dst) - 1; \ - WARN_ON_ONCE(__builtin_constant_p(src) ? \ - strcmp((src), __data_offsets.dst##_ptr_) : \ - (src) != __data_offsets.dst##_ptr_); \ + WARN_ON_ONCE(!(void *)(src) != !(void *)__data_offsets.dst##_ptr_); \ + WARN_ON_ONCE((src) && strcmp((src), __data_offsets.dst##_ptr_)); \ memcpy(__str__, __data_offsets.dst##_ptr_ ? : \ EVENT_NULL_STR, __len__); \ __str__[__len__] = '\0'; \ -- cgit v1.2.3 From 956c0d6191075f0592367512bf07aede458f0151 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Fri, 15 Mar 2024 15:47:10 -0700 Subject: tcp: Clear req->syncookie in reqsk_alloc(). syzkaller reported a read of uninit req->syncookie. [0] Originally, req->syncookie was used only in tcp_conn_request() to indicate if we need to encode SYN cookie in SYN+ACK, so the field remains uninitialised in other places. The commit 695751e31a63 ("bpf: tcp: Handle BPF SYN Cookie in cookie_v[46]_check().") added another meaning in ACK path; req->syncookie is set true if SYN cookie is validated by BPF kfunc. After the change, cookie_v[46]_check() always read req->syncookie, but it is not initialised in the normal SYN cookie case as reported by KMSAN. Let's make sure we always initialise req->syncookie in reqsk_alloc(). [0]: BUG: KMSAN: uninit-value in cookie_v4_check+0x22b7/0x29e0 net/ipv4/syncookies.c:477 cookie_v4_check+0x22b7/0x29e0 net/ipv4/syncookies.c:477 tcp_v4_cookie_check net/ipv4/tcp_ipv4.c:1855 [inline] tcp_v4_do_rcv+0xb17/0x10b0 net/ipv4/tcp_ipv4.c:1914 tcp_v4_rcv+0x4ce4/0x5420 net/ipv4/tcp_ipv4.c:2322 ip_protocol_deliver_rcu+0x2a3/0x13d0 net/ipv4/ip_input.c:205 ip_local_deliver_finish+0x332/0x500 net/ipv4/ip_input.c:233 NF_HOOK include/linux/netfilter.h:314 [inline] ip_local_deliver+0x21f/0x490 net/ipv4/ip_input.c:254 dst_input include/net/dst.h:460 [inline] ip_rcv_finish+0x4a2/0x520 net/ipv4/ip_input.c:449 NF_HOOK include/linux/netfilter.h:314 [inline] ip_rcv+0xcd/0x380 net/ipv4/ip_input.c:569 __netif_receive_skb_one_core net/core/dev.c:5538 [inline] __netif_receive_skb+0x319/0x9e0 net/core/dev.c:5652 process_backlog+0x480/0x8b0 net/core/dev.c:5981 __napi_poll+0xe7/0x980 net/core/dev.c:6632 napi_poll net/core/dev.c:6701 [inline] net_rx_action+0x89d/0x1820 net/core/dev.c:6813 __do_softirq+0x1c0/0x7d7 kernel/softirq.c:554 do_softirq+0x9a/0x100 kernel/softirq.c:455 __local_bh_enable_ip+0x9f/0xb0 kernel/softirq.c:382 local_bh_enable include/linux/bottom_half.h:33 [inline] rcu_read_unlock_bh include/linux/rcupdate.h:820 [inline] __dev_queue_xmit+0x2776/0x52c0 net/core/dev.c:4362 dev_queue_xmit include/linux/netdevice.h:3091 [inline] neigh_hh_output include/net/neighbour.h:526 [inline] neigh_output include/net/neighbour.h:540 [inline] ip_finish_output2+0x187a/0x1b70 net/ipv4/ip_output.c:235 __ip_finish_output+0x287/0x810 ip_finish_output+0x4b/0x550 net/ipv4/ip_output.c:323 NF_HOOK_COND include/linux/netfilter.h:303 [inline] ip_output+0x15f/0x3f0 net/ipv4/ip_output.c:433 dst_output include/net/dst.h:450 [inline] ip_local_out net/ipv4/ip_output.c:129 [inline] __ip_queue_xmit+0x1e93/0x2030 net/ipv4/ip_output.c:535 ip_queue_xmit+0x60/0x80 net/ipv4/ip_output.c:549 __tcp_transmit_skb+0x3c70/0x4890 net/ipv4/tcp_output.c:1462 tcp_transmit_skb net/ipv4/tcp_output.c:1480 [inline] tcp_write_xmit+0x3ee1/0x8900 net/ipv4/tcp_output.c:2792 __tcp_push_pending_frames net/ipv4/tcp_output.c:2977 [inline] tcp_send_fin+0xa90/0x12e0 net/ipv4/tcp_output.c:3578 tcp_shutdown+0x198/0x1f0 net/ipv4/tcp.c:2716 inet_shutdown+0x33f/0x5b0 net/ipv4/af_inet.c:923 __sys_shutdown_sock net/socket.c:2425 [inline] __sys_shutdown net/socket.c:2437 [inline] __do_sys_shutdown net/socket.c:2445 [inline] __se_sys_shutdown+0x2a4/0x440 net/socket.c:2443 __x64_sys_shutdown+0x6c/0xa0 net/socket.c:2443 do_syscall_64+0xd5/0x1f0 entry_SYSCALL_64_after_hwframe+0x6d/0x75 Uninit was stored to memory at: reqsk_alloc include/net/request_sock.h:148 [inline] inet_reqsk_alloc+0x651/0x7a0 net/ipv4/tcp_input.c:6978 cookie_tcp_reqsk_alloc+0xd4/0x900 net/ipv4/syncookies.c:328 cookie_tcp_check net/ipv4/syncookies.c:388 [inline] cookie_v4_check+0x289f/0x29e0 net/ipv4/syncookies.c:420 tcp_v4_cookie_check net/ipv4/tcp_ipv4.c:1855 [inline] tcp_v4_do_rcv+0xb17/0x10b0 net/ipv4/tcp_ipv4.c:1914 tcp_v4_rcv+0x4ce4/0x5420 net/ipv4/tcp_ipv4.c:2322 ip_protocol_deliver_rcu+0x2a3/0x13d0 net/ipv4/ip_input.c:205 ip_local_deliver_finish+0x332/0x500 net/ipv4/ip_input.c:233 NF_HOOK include/linux/netfilter.h:314 [inline] ip_local_deliver+0x21f/0x490 net/ipv4/ip_input.c:254 dst_input include/net/dst.h:460 [inline] ip_rcv_finish+0x4a2/0x520 net/ipv4/ip_input.c:449 NF_HOOK include/linux/netfilter.h:314 [inline] ip_rcv+0xcd/0x380 net/ipv4/ip_input.c:569 __netif_receive_skb_one_core net/core/dev.c:5538 [inline] __netif_receive_skb+0x319/0x9e0 net/core/dev.c:5652 process_backlog+0x480/0x8b0 net/core/dev.c:5981 __napi_poll+0xe7/0x980 net/core/dev.c:6632 napi_poll net/core/dev.c:6701 [inline] net_rx_action+0x89d/0x1820 net/core/dev.c:6813 __do_softirq+0x1c0/0x7d7 kernel/softirq.c:554 Uninit was created at: __alloc_pages+0x9a7/0xe00 mm/page_alloc.c:4592 __alloc_pages_node include/linux/gfp.h:238 [inline] alloc_pages_node include/linux/gfp.h:261 [inline] alloc_slab_page mm/slub.c:2175 [inline] allocate_slab mm/slub.c:2338 [inline] new_slab+0x2de/0x1400 mm/slub.c:2391 ___slab_alloc+0x1184/0x33d0 mm/slub.c:3525 __slab_alloc mm/slub.c:3610 [inline] __slab_alloc_node mm/slub.c:3663 [inline] slab_alloc_node mm/slub.c:3835 [inline] kmem_cache_alloc+0x6d3/0xbe0 mm/slub.c:3852 reqsk_alloc include/net/request_sock.h:131 [inline] inet_reqsk_alloc+0x66/0x7a0 net/ipv4/tcp_input.c:6978 tcp_conn_request+0x484/0x44e0 net/ipv4/tcp_input.c:7135 tcp_v4_conn_request+0x16f/0x1d0 net/ipv4/tcp_ipv4.c:1716 tcp_rcv_state_process+0x2e5/0x4bb0 net/ipv4/tcp_input.c:6655 tcp_v4_do_rcv+0xbfd/0x10b0 net/ipv4/tcp_ipv4.c:1929 tcp_v4_rcv+0x4ce4/0x5420 net/ipv4/tcp_ipv4.c:2322 ip_protocol_deliver_rcu+0x2a3/0x13d0 net/ipv4/ip_input.c:205 ip_local_deliver_finish+0x332/0x500 net/ipv4/ip_input.c:233 NF_HOOK include/linux/netfilter.h:314 [inline] ip_local_deliver+0x21f/0x490 net/ipv4/ip_input.c:254 dst_input include/net/dst.h:460 [inline] ip_sublist_rcv_finish net/ipv4/ip_input.c:580 [inline] ip_list_rcv_finish net/ipv4/ip_input.c:631 [inline] ip_sublist_rcv+0x15f3/0x17f0 net/ipv4/ip_input.c:639 ip_list_rcv+0x9ef/0xa40 net/ipv4/ip_input.c:674 __netif_receive_skb_list_ptype net/core/dev.c:5581 [inline] __netif_receive_skb_list_core+0x15c5/0x1670 net/core/dev.c:5629 __netif_receive_skb_list net/core/dev.c:5681 [inline] netif_receive_skb_list_internal+0x106c/0x16f0 net/core/dev.c:5773 gro_normal_list include/net/gro.h:438 [inline] napi_complete_done+0x425/0x880 net/core/dev.c:6113 virtqueue_napi_complete drivers/net/virtio_net.c:465 [inline] virtnet_poll+0x149d/0x2240 drivers/net/virtio_net.c:2211 __napi_poll+0xe7/0x980 net/core/dev.c:6632 napi_poll net/core/dev.c:6701 [inline] net_rx_action+0x89d/0x1820 net/core/dev.c:6813 __do_softirq+0x1c0/0x7d7 kernel/softirq.c:554 CPU: 0 PID: 16792 Comm: syz-executor.2 Not tainted 6.8.0-syzkaller-05562-g61387b8dcf1d #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/29/2024 Fixes: 695751e31a63 ("bpf: tcp: Handle BPF SYN Cookie in cookie_v[46]_check().") Reported-by: syzkaller Reported-by: Eric Dumazet Closes: https://lore.kernel.org/bpf/CANn89iKdN9c+C_2JAUbc+VY3DDQjAQukMtiBbormAmAk9CdvQA@mail.gmail.com/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20240315224710.55209-1-kuniyu@amazon.com Signed-off-by: Jakub Kicinski --- include/net/request_sock.h | 7 ++++++- net/ipv4/syncookies.c | 3 +++ net/ipv6/syncookies.c | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 8839133d6f6b..004e651e6067 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -61,7 +61,11 @@ struct request_sock { struct request_sock *dl_next; u16 mss; u8 num_retrans; /* number of retransmits */ - u8 syncookie:1; /* syncookie: encode tcpopts in timestamp */ + u8 syncookie:1; /* True if + * 1) tcpopts needs to be encoded in + * TS of SYN+ACK + * 2) ACK is validated by BPF kfunc. + */ u8 num_timeout:7; /* number of timeouts */ u32 ts_recent; struct timer_list rsk_timer; @@ -144,6 +148,7 @@ reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener, sk_node_init(&req_to_sk(req)->sk_node); sk_tx_queue_clear(req_to_sk(req)); req->saved_syn = NULL; + req->syncookie = 0; req->timeout = 0; req->num_timeout = 0; req->num_retrans = 0; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 7972ad3d7c73..500f665f98cb 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -474,6 +474,9 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ireq->wscale_ok, &rcv_wscale, dst_metric(&rt->dst, RTAX_INITRWND)); + /* req->syncookie is set true only if ACK is validated + * by BPF kfunc, then, rcv_wscale is already configured. + */ if (!req->syncookie) ireq->rcv_wscale = rcv_wscale; ireq->ecn_ok &= cookie_ecn_ok(net, &rt->dst); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 8bad0a44a0a6..6d8286c299c9 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -258,6 +258,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ireq->wscale_ok, &rcv_wscale, dst_metric(dst, RTAX_INITRWND)); + /* req->syncookie is set true only if ACK is validated + * by BPF kfunc, then, rcv_wscale is already configured. + */ if (!req->syncookie) ireq->rcv_wscale = rcv_wscale; ireq->ecn_ok &= cookie_ecn_ok(net, dst); -- cgit v1.2.3 From 2d9d9f256c8c85049306df3131ec7c81f9d8317c Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Thu, 14 Mar 2024 13:00:06 +0100 Subject: lib/bitmap: Fix bitmap_scatter() and bitmap_gather() kernel doc The make htmldoc command failed with the following error ... include/linux/bitmap.h:524: ERROR: Unexpected indentation. ... include/linux/bitmap.h:524: CRITICAL: Unexpected section title or transition. Move the visual representation to a literal block. Fixes: de5f84338970 ("lib/bitmap: Introduce bitmap_scatter() and bitmap_gather() helpers") Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/linux-kernel/20240312153059.3ffde1b7@canb.auug.org.au/ Signed-off-by: Herve Codina Reviewed-by: Andy Shevchenko Reviewed-by: Bagas Sanjaya Acked-by: Yury Norov Link: https://lore.kernel.org/r/20240314120006.458580-1-herve.codina@bootlin.com Signed-off-by: Jakub Kicinski --- include/linux/bitmap.h | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index fb3a9c93ac86..aa4096126553 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -522,17 +522,18 @@ static inline void bitmap_replace(unsigned long *dst, * * (Bits 0, 1, 2, 3, 4, 5 are copied to the bits 0, 1, 4, 8, 9, 12) * - * A more 'visual' description of the operation: - * src: 0000000001011010 - * |||||| - * +------+||||| - * | +----+|||| - * | |+----+||| - * | || +-+|| - * | || | || - * mask: ...v..vv...v..vv - * ...0..11...0..10 - * dst: 0000001100000010 + * A more 'visual' description of the operation:: + * + * src: 0000000001011010 + * |||||| + * +------+||||| + * | +----+|||| + * | |+----+||| + * | || +-+|| + * | || | || + * mask: ...v..vv...v..vv + * ...0..11...0..10 + * dst: 0000001100000010 * * A relationship exists between bitmap_scatter() and bitmap_gather(). * bitmap_gather() can be seen as the 'reverse' bitmap_scatter() operation. @@ -568,16 +569,17 @@ static inline void bitmap_scatter(unsigned long *dst, const unsigned long *src, * * (Bits 0, 1, 4, 8, 9, 12 are copied to the bits 0, 1, 2, 3, 4, 5) * - * A more 'visual' description of the operation: - * mask: ...v..vv...v..vv - * src: 0000001100000010 - * ^ ^^ ^ 0 - * | || | 10 - * | || > 010 - * | |+--> 1010 - * | +--> 11010 - * +----> 011010 - * dst: 0000000000011010 + * A more 'visual' description of the operation:: + * + * mask: ...v..vv...v..vv + * src: 0000001100000010 + * ^ ^^ ^ 0 + * | || | 10 + * | || > 010 + * | |+--> 1010 + * | +--> 11010 + * +----> 011010 + * dst: 0000000000011010 * * A relationship exists between bitmap_gather() and bitmap_scatter(). See * bitmap_scatter() for the bitmap scatter detailed operations. -- cgit v1.2.3 From 1a77557d48cff187a169c2aec01c0dd78a5e7e50 Mon Sep 17 00:00:00 2001 From: Yan Zhai Date: Tue, 19 Mar 2024 13:44:34 -0700 Subject: rcu: add a helper to report consolidated flavor QS When under heavy load, network processing can run CPU-bound for many tens of seconds. Even in preemptible kernels (non-RT kernel), this can block RCU Tasks grace periods, which can cause trace-event removal to take more than a minute, which is unacceptably long. This commit therefore creates a new helper function that passes through both RCU and RCU-Tasks quiescent states every 100 milliseconds. This hard-coded value suffices for current workloads. Suggested-by: Paul E. McKenney Reviewed-by: Jesper Dangaard Brouer Signed-off-by: Yan Zhai Reviewed-by: Paul E. McKenney Acked-by: Jesper Dangaard Brouer Link: https://lore.kernel.org/r/90431d46ee112d2b0af04dbfe936faaca11810a5.1710877680.git.yan@cloudflare.com Signed-off-by: Jakub Kicinski --- include/linux/rcupdate.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 16f519914415..17d7ed5f3ae6 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -247,6 +247,37 @@ do { \ cond_resched(); \ } while (0) +/** + * rcu_softirq_qs_periodic - Report RCU and RCU-Tasks quiescent states + * @old_ts: jiffies at start of processing. + * + * This helper is for long-running softirq handlers, such as NAPI threads in + * networking. The caller should initialize the variable passed in as @old_ts + * at the beginning of the softirq handler. When invoked frequently, this macro + * will invoke rcu_softirq_qs() every 100 milliseconds thereafter, which will + * provide both RCU and RCU-Tasks quiescent states. Note that this macro + * modifies its old_ts argument. + * + * Because regions of code that have disabled softirq act as RCU read-side + * critical sections, this macro should be invoked with softirq (and + * preemption) enabled. + * + * The macro is not needed when CONFIG_PREEMPT_RT is defined. RT kernels would + * have more chance to invoke schedule() calls and provide necessary quiescent + * states. As a contrast, calling cond_resched() only won't achieve the same + * effect because cond_resched() does not provide RCU-Tasks quiescent states. + */ +#define rcu_softirq_qs_periodic(old_ts) \ +do { \ + if (!IS_ENABLED(CONFIG_PREEMPT_RT) && \ + time_after(jiffies, (old_ts) + HZ / 10)) { \ + preempt_disable(); \ + rcu_softirq_qs(); \ + preempt_enable(); \ + (old_ts) = jiffies; \ + } \ +} while (0) + /* * Infrastructure to implement the synchronize_() primitives in * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. -- cgit v1.2.3 From 02545bc57512b7660625e454e60e3fb0d07f660d Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 27 Nov 2023 14:06:56 +0200 Subject: ALSA: hda: intel-nhlt: add intel_nhlt_ssp_device_type() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper function intel_nhlt_ssp_device_type() to detect the type of specific SSP port. The result is nhlt_device_type enum type which could be NHLT_DEVICE_BT or NHLT_DEVICE_I2S. Signed-off-by: Brent Lu Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Signed-off-by: Takashi Iwai Message-ID: <20231127120657.19764-2-peter.ujfalusi@linux.intel.com> --- include/sound/intel-nhlt.h | 10 ++++++++++ sound/hda/intel-nhlt.c | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/include/sound/intel-nhlt.h b/include/sound/intel-nhlt.h index 53470d6a28d6..24dbe16684ae 100644 --- a/include/sound/intel-nhlt.h +++ b/include/sound/intel-nhlt.h @@ -143,6 +143,9 @@ intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, u32 bus_id, u8 link_type, u8 vbps, u8 bps, u8 num_ch, u32 rate, u8 dir, u8 dev_type); +int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt, + u8 virtual_bus_id); + #else static inline struct nhlt_acpi_table *intel_nhlt_init(struct device *dev) @@ -184,6 +187,13 @@ intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, return NULL; } +static inline int intel_nhlt_ssp_device_type(struct device *dev, + struct nhlt_acpi_table *nhlt, + u8 virtual_bus_id) +{ + return -EINVAL; +} + #endif #endif diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c index 696a958d93e9..088cff799e0b 100644 --- a/sound/hda/intel-nhlt.c +++ b/sound/hda/intel-nhlt.c @@ -343,3 +343,29 @@ intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, return NULL; } EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob); + +int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt, + u8 virtual_bus_id) +{ + struct nhlt_endpoint *epnt; + int i; + + if (!nhlt) + return -EINVAL; + + epnt = (struct nhlt_endpoint *)nhlt->desc; + for (i = 0; i < nhlt->endpoint_count; i++) { + /* for SSP link the virtual bus id is the SSP port number */ + if (epnt->linktype == NHLT_LINK_SSP && + epnt->virtual_bus_id == virtual_bus_id) { + dev_dbg(dev, "SSP%d: dev_type=%d\n", virtual_bus_id, + epnt->device_type); + return epnt->device_type; + } + + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + + return -EINVAL; +} +EXPORT_SYMBOL(intel_nhlt_ssp_device_type); -- cgit v1.2.3 From 203a6763ab699da0568fd2b76303d03bb121abd4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 13 Mar 2024 16:32:27 -0700 Subject: Revert "crypto: pkcs7 - remove sha1 support" This reverts commit 16ab7cb5825fc3425c16ad2c6e53d827f382d7c6 because it broke iwd. iwd uses the KEYCTL_PKEY_* UAPIs via its dependency libell, and apparently it is relying on SHA-1 signature support. These UAPIs are fairly obscure, and their documentation does not mention which algorithms they support. iwd really should be using a properly supported userspace crypto library instead. Regardless, since something broke we have to revert the change. It may be possible that some parts of this commit can be reinstated without breaking iwd (e.g. probably the removal of MODULE_SIG_SHA1), but for now this just does a full revert to get things working again. Reported-by: Karel Balej Closes: https://lore.kernel.org/r/CZSHRUIJ4RKL.34T4EASV5DNJM@matfyz.cz Cc: Dimitri John Ledkov Signed-off-by: Eric Biggers Tested-by: Karel Balej Signed-off-by: Herbert Xu --- crypto/asymmetric_keys/mscode_parser.c | 3 ++ crypto/asymmetric_keys/pkcs7_parser.c | 4 ++ crypto/asymmetric_keys/public_key.c | 3 +- crypto/asymmetric_keys/signature.c | 2 +- crypto/asymmetric_keys/x509_cert_parser.c | 8 ++++ crypto/testmgr.h | 80 +++++++++++++++++++++++++++++++ include/linux/oid_registry.h | 4 ++ kernel/module/Kconfig | 5 ++ 8 files changed, 107 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c index 05402ef8964e..8aecbe4637f3 100644 --- a/crypto/asymmetric_keys/mscode_parser.c +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -75,6 +75,9 @@ int mscode_note_digest_algo(void *context, size_t hdrlen, oid = look_up_OID(value, vlen); switch (oid) { + case OID_sha1: + ctx->digest_algo = "sha1"; + break; case OID_sha256: ctx->digest_algo = "sha256"; break; diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 5b08c50722d0..231ad7b3789d 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -227,6 +227,9 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, struct pkcs7_parse_context *ctx = context; switch (ctx->last_oid) { + case OID_sha1: + ctx->sinfo->sig->hash_algo = "sha1"; + break; case OID_sha256: ctx->sinfo->sig->hash_algo = "sha256"; break; @@ -278,6 +281,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, ctx->sinfo->sig->pkey_algo = "rsa"; ctx->sinfo->sig->encoding = "pkcs1"; break; + case OID_id_ecdsa_with_sha1: case OID_id_ecdsa_with_sha224: case OID_id_ecdsa_with_sha256: case OID_id_ecdsa_with_sha384: diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index e5f22691febd..e314fd57e6f8 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -115,7 +115,8 @@ software_key_determine_akcipher(const struct public_key *pkey, */ if (!hash_algo) return -EINVAL; - if (strcmp(hash_algo, "sha224") != 0 && + if (strcmp(hash_algo, "sha1") != 0 && + strcmp(hash_algo, "sha224") != 0 && strcmp(hash_algo, "sha256") != 0 && strcmp(hash_algo, "sha384") != 0 && strcmp(hash_algo, "sha512") != 0 && diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 398983be77e8..2deff81f8af5 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -115,7 +115,7 @@ EXPORT_SYMBOL_GPL(decrypt_blob); * Sign the specified data blob using the private key specified by params->key. * The signature is wrapped in an encoding if params->encoding is specified * (eg. "pkcs1"). If the encoding needs to know the digest type, this can be - * passed through params->hash_algo (eg. "sha512"). + * passed through params->hash_algo (eg. "sha1"). * * Returns the length of the data placed in the signature buffer or an error. */ diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 487204d39426..bb0bffa271b5 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -198,6 +198,10 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag, default: return -ENOPKG; /* Unsupported combination */ + case OID_sha1WithRSAEncryption: + ctx->cert->sig->hash_algo = "sha1"; + goto rsa_pkcs1; + case OID_sha256WithRSAEncryption: ctx->cert->sig->hash_algo = "sha256"; goto rsa_pkcs1; @@ -214,6 +218,10 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag, ctx->cert->sig->hash_algo = "sha224"; goto rsa_pkcs1; + case OID_id_ecdsa_with_sha1: + ctx->cert->sig->hash_algo = "sha1"; + goto ecdsa; + case OID_id_rsassa_pkcs1_v1_5_with_sha3_256: ctx->cert->sig->hash_algo = "sha3-256"; goto rsa_pkcs1; diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 986f331a5fc2..12e1c892f366 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -653,6 +653,30 @@ static const struct akcipher_testvec rsa_tv_template[] = { static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { { .key = + "\x04\xf7\x46\xf8\x2f\x15\xf6\x22\x8e\xd7\x57\x4f\xcc\xe7\xbb\xc1" + "\xd4\x09\x73\xcf\xea\xd0\x15\x07\x3d\xa5\x8a\x8a\x95\x43\xe4\x68" + "\xea\xc6\x25\xc1\xc1\x01\x25\x4c\x7e\xc3\x3c\xa6\x04\x0a\xe7\x08" + "\x98", + .key_len = 49, + .params = + "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" + "\xce\x3d\x03\x01\x01", + .param_len = 21, + .m = + "\xcd\xb9\xd2\x1c\xb7\x6f\xcd\x44\xb3\xfd\x63\xea\xa3\x66\x7f\xae" + "\x63\x85\xe7\x82", + .m_size = 20, + .algo = OID_id_ecdsa_with_sha1, + .c = + "\x30\x35\x02\x19\x00\xba\xe5\x93\x83\x6e\xb6\x3b\x63\xa0\x27\x91" + "\xc6\xf6\x7f\xc3\x09\xad\x59\xad\x88\x27\xd6\x92\x6b\x02\x18\x10" + "\x68\x01\x9d\xba\xce\x83\x08\xef\x95\x52\x7b\xa0\x0f\xe4\x18\x86" + "\x80\x6f\xa5\x79\x77\xda\xd0", + .c_size = 55, + .public_key_vec = true, + .siggen_sigver_test = true, + }, { + .key = "\x04\xb6\x4b\xb1\xd1\xac\xba\x24\x8f\x65\xb2\x60\x00\x90\xbf\xbd" "\x78\x05\x73\xe9\x79\x1d\x6f\x7c\x0b\xd2\xc3\x93\xa7\x28\xe1\x75" "\xf7\xd5\x95\x1d\x28\x10\xc0\x75\x50\x5c\x1a\x4f\x3f\x8f\xa5\xee" @@ -756,6 +780,32 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { { .key = + "\x04\xb9\x7b\xbb\xd7\x17\x64\xd2\x7e\xfc\x81\x5d\x87\x06\x83\x41" + "\x22\xd6\x9a\xaa\x87\x17\xec\x4f\x63\x55\x2f\x94\xba\xdd\x83\xe9" + "\x34\x4b\xf3\xe9\x91\x13\x50\xb6\xcb\xca\x62\x08\xe7\x3b\x09\xdc" + "\xc3\x63\x4b\x2d\xb9\x73\x53\xe4\x45\xe6\x7c\xad\xe7\x6b\xb0\xe8" + "\xaf", + .key_len = 65, + .params = + "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" + "\xce\x3d\x03\x01\x07", + .param_len = 21, + .m = + "\xc2\x2b\x5f\x91\x78\x34\x26\x09\x42\x8d\x6f\x51\xb2\xc5\xaf\x4c" + "\x0b\xde\x6a\x42", + .m_size = 20, + .algo = OID_id_ecdsa_with_sha1, + .c = + "\x30\x46\x02\x21\x00\xf9\x25\xce\x9f\x3a\xa6\x35\x81\xcf\xd4\xe7" + "\xb7\xf0\x82\x56\x41\xf7\xd4\xad\x8d\x94\x5a\x69\x89\xee\xca\x6a" + "\x52\x0e\x48\x4d\xcc\x02\x21\x00\xd7\xe4\xef\x52\x66\xd3\x5b\x9d" + "\x8a\xfa\x54\x93\x29\xa7\x70\x86\xf1\x03\x03\xf3\x3b\xe2\x73\xf7" + "\xfb\x9d\x8b\xde\xd4\x8d\x6f\xad", + .c_size = 72, + .public_key_vec = true, + .siggen_sigver_test = true, + }, { + .key = "\x04\x8b\x6d\xc0\x33\x8e\x2d\x8b\x67\xf5\xeb\xc4\x7f\xa0\xf5\xd9" "\x7b\x03\xa5\x78\x9a\xb5\xea\x14\xe4\x23\xd0\xaf\xd7\x0e\x2e\xa0" "\xc9\x8b\xdb\x95\xf8\xb3\xaf\xac\x00\x2c\x2c\x1f\x7a\xfd\x95\x88" @@ -866,6 +916,36 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { { + .key = /* secp384r1(sha1) */ + "\x04\x89\x25\xf3\x97\x88\xcb\xb0\x78\xc5\x72\x9a\x14\x6e\x7a\xb1" + "\x5a\xa5\x24\xf1\x95\x06\x9e\x28\xfb\xc4\xb9\xbe\x5a\x0d\xd9\x9f" + "\xf3\xd1\x4d\x2d\x07\x99\xbd\xda\xa7\x66\xec\xbb\xea\xba\x79\x42" + "\xc9\x34\x89\x6a\xe7\x0b\xc3\xf2\xfe\x32\x30\xbe\xba\xf9\xdf\x7e" + "\x4b\x6a\x07\x8e\x26\x66\x3f\x1d\xec\xa2\x57\x91\x51\xdd\x17\x0e" + "\x0b\x25\xd6\x80\x5c\x3b\xe6\x1a\x98\x48\x91\x45\x7a\x73\xb0\xc3" + "\xf1", + .key_len = 97, + .params = + "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" + "\x00\x22", + .param_len = 18, + .m = + "\x12\x55\x28\xf0\x77\xd5\xb6\x21\x71\x32\x48\xcd\x28\xa8\x25\x22" + "\x3a\x69\xc1\x93", + .m_size = 20, + .algo = OID_id_ecdsa_with_sha1, + .c = + "\x30\x66\x02\x31\x00\xf5\x0f\x24\x4c\x07\x93\x6f\x21\x57\x55\x07" + "\x20\x43\x30\xde\xa0\x8d\x26\x8e\xae\x63\x3f\xbc\x20\x3a\xc6\xf1" + "\x32\x3c\xce\x70\x2b\x78\xf1\x4c\x26\xe6\x5b\x86\xcf\xec\x7c\x7e" + "\xd0\x87\xd7\xd7\x6e\x02\x31\x00\xcd\xbb\x7e\x81\x5d\x8f\x63\xc0" + "\x5f\x63\xb1\xbe\x5e\x4c\x0e\xa1\xdf\x28\x8c\x1b\xfa\xf9\x95\x88" + "\x74\xa0\x0f\xbf\xaf\xc3\x36\x76\x4a\xa1\x59\xf1\x1c\xa4\x58\x26" + "\x79\x12\x2a\xb7\xc5\x15\x92\xc5", + .c_size = 104, + .public_key_vec = true, + .siggen_sigver_test = true, + }, { .key = /* secp384r1(sha224) */ "\x04\x69\x6c\xcf\x62\xee\xd0\x0d\xe5\xb5\x2f\x70\x54\xcf\x26\xa0" "\xd9\x98\x8d\x92\x2a\xab\x9b\x11\xcb\x48\x18\xa1\xa9\x0d\xd5\x18" diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 3921fbed0b28..51421fdbb0ba 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -17,10 +17,12 @@ * build_OID_registry.pl to generate the data for look_up_OID(). */ enum OID { + OID_id_dsa_with_sha1, /* 1.2.840.10030.4.3 */ OID_id_dsa, /* 1.2.840.10040.4.1 */ OID_id_ecPublicKey, /* 1.2.840.10045.2.1 */ OID_id_prime192v1, /* 1.2.840.10045.3.1.1 */ OID_id_prime256v1, /* 1.2.840.10045.3.1.7 */ + OID_id_ecdsa_with_sha1, /* 1.2.840.10045.4.1 */ OID_id_ecdsa_with_sha224, /* 1.2.840.10045.4.3.1 */ OID_id_ecdsa_with_sha256, /* 1.2.840.10045.4.3.2 */ OID_id_ecdsa_with_sha384, /* 1.2.840.10045.4.3.3 */ @@ -28,6 +30,7 @@ enum OID { /* PKCS#1 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)} */ OID_rsaEncryption, /* 1.2.840.113549.1.1.1 */ + OID_sha1WithRSAEncryption, /* 1.2.840.113549.1.1.5 */ OID_sha256WithRSAEncryption, /* 1.2.840.113549.1.1.11 */ OID_sha384WithRSAEncryption, /* 1.2.840.113549.1.1.12 */ OID_sha512WithRSAEncryption, /* 1.2.840.113549.1.1.13 */ @@ -64,6 +67,7 @@ enum OID { OID_PKU2U, /* 1.3.5.1.5.2.7 */ OID_Scram, /* 1.3.6.1.5.5.14 */ OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ + OID_sha1, /* 1.3.14.3.2.26 */ OID_id_ansip384r1, /* 1.3.132.0.34 */ OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ OID_sha384, /* 2.16.840.1.101.3.4.2.2 */ diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig index 0ea1b2970a23..28db5b7589eb 100644 --- a/kernel/module/Kconfig +++ b/kernel/module/Kconfig @@ -236,6 +236,10 @@ choice possible to load a signed module containing the algorithm to check the signature on that module. +config MODULE_SIG_SHA1 + bool "Sign modules with SHA-1" + select CRYPTO_SHA1 + config MODULE_SIG_SHA256 bool "Sign modules with SHA-256" select CRYPTO_SHA256 @@ -265,6 +269,7 @@ endchoice config MODULE_SIG_HASH string depends on MODULE_SIG || IMA_APPRAISE_MODSIG + default "sha1" if MODULE_SIG_SHA1 default "sha256" if MODULE_SIG_SHA256 default "sha384" if MODULE_SIG_SHA384 default "sha512" if MODULE_SIG_SHA512 -- cgit v1.2.3 From d8e45f2929b94099913eb66c3ebb18b5063e9421 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 6 Mar 2024 15:51:36 -0800 Subject: overflow: Change DEFINE_FLEX to take __counted_by member The norm should be flexible array structures with __counted_by annotations, so DEFINE_FLEX() is updated to expect that. Rename the non-annotated version to DEFINE_RAW_FLEX(), and update the few existing users. Additionally add selftests for the macros. Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20240306235128.it.933-kees@kernel.org Reviewed-by: Przemek Kitszel Signed-off-by: Kees Cook --- drivers/net/ethernet/intel/ice/ice_base.c | 4 ++-- drivers/net/ethernet/intel/ice/ice_common.c | 4 ++-- drivers/net/ethernet/intel/ice/ice_ddp.c | 8 ++++---- drivers/net/ethernet/intel/ice/ice_lag.c | 6 +++--- drivers/net/ethernet/intel/ice/ice_sched.c | 4 ++-- drivers/net/ethernet/intel/ice/ice_switch.c | 10 +++++----- include/linux/overflow.h | 25 +++++++++++++++++++++---- lib/overflow_kunit.c | 19 +++++++++++++++++++ 8 files changed, 58 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c index d2fd315556a3..a545a7917e4f 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.c +++ b/drivers/net/ethernet/intel/ice/ice_base.c @@ -956,7 +956,7 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, int ice_vsi_cfg_single_txq(struct ice_vsi *vsi, struct ice_tx_ring **tx_rings, u16 q_idx) { - DEFINE_FLEX(struct ice_aqc_add_tx_qgrp, qg_buf, txqs, 1); + DEFINE_RAW_FLEX(struct ice_aqc_add_tx_qgrp, qg_buf, txqs, 1); if (q_idx >= vsi->alloc_txq || !tx_rings || !tx_rings[q_idx]) return -EINVAL; @@ -978,7 +978,7 @@ int ice_vsi_cfg_single_txq(struct ice_vsi *vsi, struct ice_tx_ring **tx_rings, static int ice_vsi_cfg_txqs(struct ice_vsi *vsi, struct ice_tx_ring **rings, u16 count) { - DEFINE_FLEX(struct ice_aqc_add_tx_qgrp, qg_buf, txqs, 1); + DEFINE_RAW_FLEX(struct ice_aqc_add_tx_qgrp, qg_buf, txqs, 1); int err = 0; u16 q_idx; diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index 4d8111aeb0ff..db4b2844e1f7 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -4695,7 +4695,7 @@ ice_dis_vsi_txq(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u8 num_queues, enum ice_disq_rst_src rst_src, u16 vmvf_num, struct ice_sq_cd *cd) { - DEFINE_FLEX(struct ice_aqc_dis_txq_item, qg_list, q_id, 1); + DEFINE_RAW_FLEX(struct ice_aqc_dis_txq_item, qg_list, q_id, 1); u16 i, buf_size = __struct_size(qg_list); struct ice_q_ctx *q_ctx; int status = -ENOENT; @@ -4917,7 +4917,7 @@ int ice_dis_vsi_rdma_qset(struct ice_port_info *pi, u16 count, u32 *qset_teid, u16 *q_id) { - DEFINE_FLEX(struct ice_aqc_dis_txq_item, qg_list, q_id, 1); + DEFINE_RAW_FLEX(struct ice_aqc_dis_txq_item, qg_list, q_id, 1); u16 qg_size = __struct_size(qg_list); struct ice_hw *hw; int status = 0; diff --git a/drivers/net/ethernet/intel/ice/ice_ddp.c b/drivers/net/ethernet/intel/ice/ice_ddp.c index 7532d11ad7f3..fc91c4d41186 100644 --- a/drivers/net/ethernet/intel/ice/ice_ddp.c +++ b/drivers/net/ethernet/intel/ice/ice_ddp.c @@ -1938,8 +1938,8 @@ static enum ice_ddp_state ice_init_pkg_info(struct ice_hw *hw, */ static enum ice_ddp_state ice_get_pkg_info(struct ice_hw *hw) { - DEFINE_FLEX(struct ice_aqc_get_pkg_info_resp, pkg_info, pkg_info, - ICE_PKG_CNT); + DEFINE_RAW_FLEX(struct ice_aqc_get_pkg_info_resp, pkg_info, pkg_info, + ICE_PKG_CNT); u16 size = __struct_size(pkg_info); u32 i; @@ -1990,8 +1990,8 @@ static enum ice_ddp_state ice_chk_pkg_compat(struct ice_hw *hw, struct ice_pkg_hdr *ospkg, struct ice_seg **seg) { - DEFINE_FLEX(struct ice_aqc_get_pkg_info_resp, pkg, pkg_info, - ICE_PKG_CNT); + DEFINE_RAW_FLEX(struct ice_aqc_get_pkg_info_resp, pkg, pkg_info, + ICE_PKG_CNT); u16 size = __struct_size(pkg); enum ice_ddp_state state; u32 i; diff --git a/drivers/net/ethernet/intel/ice/ice_lag.c b/drivers/net/ethernet/intel/ice/ice_lag.c index 467372d541d2..f97128b69f87 100644 --- a/drivers/net/ethernet/intel/ice/ice_lag.c +++ b/drivers/net/ethernet/intel/ice/ice_lag.c @@ -491,7 +491,7 @@ static void ice_lag_move_vf_node_tc(struct ice_lag *lag, u8 oldport, u8 newport, u16 vsi_num, u8 tc) { - DEFINE_FLEX(struct ice_aqc_move_elem, buf, teid, 1); + DEFINE_RAW_FLEX(struct ice_aqc_move_elem, buf, teid, 1); struct device *dev = ice_pf_to_dev(lag->pf); u16 numq, valq, num_moved, qbuf_size; u16 buf_size = __struct_size(buf); @@ -849,7 +849,7 @@ static void ice_lag_reclaim_vf_tc(struct ice_lag *lag, struct ice_hw *src_hw, u16 vsi_num, u8 tc) { - DEFINE_FLEX(struct ice_aqc_move_elem, buf, teid, 1); + DEFINE_RAW_FLEX(struct ice_aqc_move_elem, buf, teid, 1); struct device *dev = ice_pf_to_dev(lag->pf); u16 numq, valq, num_moved, qbuf_size; u16 buf_size = __struct_size(buf); @@ -1873,7 +1873,7 @@ static void ice_lag_move_vf_nodes_tc_sync(struct ice_lag *lag, struct ice_hw *dest_hw, u16 vsi_num, u8 tc) { - DEFINE_FLEX(struct ice_aqc_move_elem, buf, teid, 1); + DEFINE_RAW_FLEX(struct ice_aqc_move_elem, buf, teid, 1); struct device *dev = ice_pf_to_dev(lag->pf); u16 numq, valq, num_moved, qbuf_size; u16 buf_size = __struct_size(buf); diff --git a/drivers/net/ethernet/intel/ice/ice_sched.c b/drivers/net/ethernet/intel/ice/ice_sched.c index d174a4eeb899..a1525992d14b 100644 --- a/drivers/net/ethernet/intel/ice/ice_sched.c +++ b/drivers/net/ethernet/intel/ice/ice_sched.c @@ -237,7 +237,7 @@ static int ice_sched_remove_elems(struct ice_hw *hw, struct ice_sched_node *parent, u32 node_teid) { - DEFINE_FLEX(struct ice_aqc_delete_elem, buf, teid, 1); + DEFINE_RAW_FLEX(struct ice_aqc_delete_elem, buf, teid, 1); u16 buf_size = __struct_size(buf); u16 num_groups_removed = 0; int status; @@ -2219,7 +2219,7 @@ int ice_sched_move_nodes(struct ice_port_info *pi, struct ice_sched_node *parent, u16 num_items, u32 *list) { - DEFINE_FLEX(struct ice_aqc_move_elem, buf, teid, 1); + DEFINE_RAW_FLEX(struct ice_aqc_move_elem, buf, teid, 1); u16 buf_len = __struct_size(buf); struct ice_sched_node *node; u16 i, grps_movd = 0; diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c index f84bab80ca42..d4baae8c3b72 100644 --- a/drivers/net/ethernet/intel/ice/ice_switch.c +++ b/drivers/net/ethernet/intel/ice/ice_switch.c @@ -1812,7 +1812,7 @@ ice_aq_alloc_free_vsi_list(struct ice_hw *hw, u16 *vsi_list_id, enum ice_sw_lkup_type lkup_type, enum ice_adminq_opc opc) { - DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1); + DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1); u16 buf_len = __struct_size(sw_buf); struct ice_aqc_res_elem *vsi_ele; int status; @@ -2081,7 +2081,7 @@ ice_aq_get_recipe_to_profile(struct ice_hw *hw, u32 profile_id, u8 *r_bitmap, */ int ice_alloc_recipe(struct ice_hw *hw, u16 *rid) { - DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1); + DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1); u16 buf_len = __struct_size(sw_buf); int status; @@ -4418,7 +4418,7 @@ int ice_alloc_res_cntr(struct ice_hw *hw, u8 type, u8 alloc_shared, u16 num_items, u16 *counter_id) { - DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1); + DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1); u16 buf_len = __struct_size(buf); int status; @@ -4446,7 +4446,7 @@ int ice_free_res_cntr(struct ice_hw *hw, u8 type, u8 alloc_shared, u16 num_items, u16 counter_id) { - DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1); + DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1); u16 buf_len = __struct_size(buf); int status; @@ -4476,7 +4476,7 @@ ice_free_res_cntr(struct ice_hw *hw, u8 type, u8 alloc_shared, u16 num_items, */ int ice_share_res(struct ice_hw *hw, u16 type, u8 shared, u16 res_id) { - DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1); + DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1); u16 buf_len = __struct_size(buf); u16 res_type; int status; diff --git a/include/linux/overflow.h b/include/linux/overflow.h index aa691f2119b0..0c7e3dcfe867 100644 --- a/include/linux/overflow.h +++ b/include/linux/overflow.h @@ -398,7 +398,7 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend) * @count: Number of elements in the array; must be compile-time const. * @initializer: initializer expression (could be empty for no init). */ -#define _DEFINE_FLEX(type, name, member, count, initializer) \ +#define _DEFINE_FLEX(type, name, member, count, initializer...) \ _Static_assert(__builtin_constant_p(count), \ "onstack flex array members require compile-time const count"); \ union { \ @@ -408,8 +408,8 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend) type *name = (type *)&name##_u /** - * DEFINE_FLEX() - Define an on-stack instance of structure with a trailing - * flexible array member. + * DEFINE_RAW_FLEX() - Define an on-stack instance of structure with a trailing + * flexible array member, when it does not have a __counted_by annotation. * * @type: structure type name, including "struct" keyword. * @name: Name for a variable to define. @@ -420,7 +420,24 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend) * flexible array member. * Use __struct_size(@name) to get compile-time size of it afterwards. */ -#define DEFINE_FLEX(type, name, member, count) \ +#define DEFINE_RAW_FLEX(type, name, member, count) \ _DEFINE_FLEX(type, name, member, count, = {}) +/** + * DEFINE_FLEX() - Define an on-stack instance of structure with a trailing + * flexible array member. + * + * @TYPE: structure type name, including "struct" keyword. + * @NAME: Name for a variable to define. + * @MEMBER: Name of the array member. + * @COUNTER: Name of the __counted_by member. + * @COUNT: Number of elements in the array; must be compile-time const. + * + * Define a zeroed, on-stack, instance of @TYPE structure with a trailing + * flexible array member. + * Use __struct_size(@NAME) to get compile-time size of it afterwards. + */ +#define DEFINE_FLEX(TYPE, NAME, MEMBER, COUNTER, COUNT) \ + _DEFINE_FLEX(TYPE, NAME, MEMBER, COUNT, = { .obj.COUNTER = COUNT, }) + #endif /* __LINUX_OVERFLOW_H */ diff --git a/lib/overflow_kunit.c b/lib/overflow_kunit.c index 65e8a72a83bf..4ef31b0bb74d 100644 --- a/lib/overflow_kunit.c +++ b/lib/overflow_kunit.c @@ -1172,6 +1172,24 @@ static void castable_to_type_test(struct kunit *test) #undef TEST_CASTABLE_TO_TYPE } +struct foo { + int a; + u32 counter; + s16 array[] __counted_by(counter); +}; + +static void DEFINE_FLEX_test(struct kunit *test) +{ + DEFINE_RAW_FLEX(struct foo, two, array, 2); + DEFINE_FLEX(struct foo, eight, array, counter, 8); + DEFINE_FLEX(struct foo, empty, array, counter, 0); + + KUNIT_EXPECT_EQ(test, __struct_size(two), + sizeof(struct foo) + sizeof(s16) + sizeof(s16)); + KUNIT_EXPECT_EQ(test, __struct_size(eight), 24); + KUNIT_EXPECT_EQ(test, __struct_size(empty), sizeof(struct foo)); +} + static struct kunit_case overflow_test_cases[] = { KUNIT_CASE(u8_u8__u8_overflow_test), KUNIT_CASE(s8_s8__s8_overflow_test), @@ -1194,6 +1212,7 @@ static struct kunit_case overflow_test_cases[] = { KUNIT_CASE(overflows_type_test), KUNIT_CASE(same_type_test), KUNIT_CASE(castable_to_type_test), + KUNIT_CASE(DEFINE_FLEX_test), {} }; -- cgit v1.2.3 From be23b2d7c3b7c8bf57b1cf0bf890bd65df9d0186 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Mar 2024 11:09:51 +0100 Subject: wifi: cfg80211: add a flag to disable wireless extensions Wireless extensions are already disabled if MLO is enabled, given that we cannot support MLO there with all the hard- coded assumptions about BSSID etc. However, the WiFi7 ecosystem is still stabilizing, and some devices may need MLO disabled while that happens. In that case, we might end up with a device that supports wext (but not MLO) in one kernel, and then breaks wext in the future (by enabling MLO), which is not desirable. Add a flag to let such drivers/devices disable wext even if MLO isn't yet enabled. Cc: stable@vger.kernel.org Link: https://msgid.link/20240314110951.b50f1dc4ec21.I656ddd8178eedb49dc5c6c0e70f8ce5807afb54f@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/wext-core.c | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2e2be4fd2bb6..1e09329acc42 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4991,6 +4991,7 @@ struct cfg80211_ops { * set this flag to update channels on beacon hints. * @WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY: support connection to non-primary link * of an NSTR mobile AP MLD. + * @WIPHY_FLAG_DISABLE_WEXT: disable wireless extensions for this device */ enum wiphy_flags { WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK = BIT(0), @@ -5002,6 +5003,7 @@ enum wiphy_flags { WIPHY_FLAG_4ADDR_STATION = BIT(6), WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), WIPHY_FLAG_IBSS_RSN = BIT(8), + WIPHY_FLAG_DISABLE_WEXT = BIT(9), WIPHY_FLAG_MESH_AUTH = BIT(10), WIPHY_FLAG_SUPPORTS_EXT_KCK_32 = BIT(11), WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY = BIT(12), diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index a161c64d1765..838ad6541a17 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -4,6 +4,7 @@ * Authors : Jean Tourrilhes - HPL - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. * Copyright 2009 Johannes Berg + * Copyright (C) 2024 Intel Corporation * * (As all part of the Linux kernel, this file is GPL) */ @@ -662,7 +663,8 @@ struct iw_statistics *get_wireless_stats(struct net_device *dev) dev->ieee80211_ptr->wiphy->wext && dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) { wireless_warn_cfg80211_wext(); - if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) + if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO | + WIPHY_FLAG_DISABLE_WEXT)) return NULL; return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); } @@ -704,7 +706,8 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd) #ifdef CONFIG_CFG80211_WEXT if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) { wireless_warn_cfg80211_wext(); - if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) + if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO | + WIPHY_FLAG_DISABLE_WEXT)) return NULL; handlers = dev->ieee80211_ptr->wiphy->wext; } -- cgit v1.2.3 From 0c76106cb97548810214def8ee22700bbbb90543 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 19 Mar 2024 16:12:09 +0900 Subject: scsi: sd: Fix TCG OPAL unlock on system resume Commit 3cc2ffe5c16d ("scsi: sd: Differentiate system and runtime start/stop management") introduced the manage_system_start_stop scsi_device flag to allow libata to indicate to the SCSI disk driver that nothing should be done when resuming a disk on system resume. This change turned the execution of sd_resume() into a no-op for ATA devices on system resume. While this solved deadlock issues during device resume, this change also wrongly removed the execution of opal_unlock_from_suspend(). As a result, devices with TCG OPAL locking enabled remain locked and inaccessible after a system resume from sleep. To fix this issue, introduce the SCSI driver resume method and implement it with the sd_resume() function calling opal_unlock_from_suspend(). The former sd_resume() function is renamed to sd_resume_common() and modified to call the new sd_resume() function. For non-ATA devices, this result in no functional changes. In order for libata to explicitly execute sd_resume() when a device is resumed during system restart, the function scsi_resume_device() is introduced. libata calls this function from the revalidation work executed on devie resume, a state that is indicated with the new device flag ATA_DFLAG_RESUMING. Doing so, locked TCG OPAL enabled devices are unlocked on resume, allowing normal operation. Fixes: 3cc2ffe5c16d ("scsi: sd: Differentiate system and runtime start/stop management") Link: https://bugzilla.kernel.org/show_bug.cgi?id=218538 Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Link: https://lore.kernel.org/r/20240319071209.1179257-1-dlemoal@kernel.org Signed-off-by: Martin K. Petersen --- drivers/ata/libata-eh.c | 5 ++++- drivers/ata/libata-scsi.c | 9 +++++++++ drivers/scsi/scsi_scan.c | 34 ++++++++++++++++++++++++++++++++++ drivers/scsi/sd.c | 23 +++++++++++++++++++---- include/linux/libata.h | 1 + include/scsi/scsi_driver.h | 1 + include/scsi/scsi_host.h | 1 + 7 files changed, 69 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index b0d6e69c4a5b..214b935c2ced 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -712,8 +712,10 @@ void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap) ehc->saved_ncq_enabled |= 1 << devno; /* If we are resuming, wake up the device */ - if (ap->pflags & ATA_PFLAG_RESUMING) + if (ap->pflags & ATA_PFLAG_RESUMING) { + dev->flags |= ATA_DFLAG_RESUMING; ehc->i.dev_action[devno] |= ATA_EH_SET_ACTIVE; + } } } @@ -3169,6 +3171,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, return 0; err: + dev->flags &= ~ATA_DFLAG_RESUMING; *r_failed_dev = dev; return rc; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 0a0f483124c3..2f4c58837641 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -4730,6 +4730,7 @@ void ata_scsi_dev_rescan(struct work_struct *work) struct ata_link *link; struct ata_device *dev; unsigned long flags; + bool do_resume; int ret = 0; mutex_lock(&ap->scsi_scan_mutex); @@ -4751,7 +4752,15 @@ void ata_scsi_dev_rescan(struct work_struct *work) if (scsi_device_get(sdev)) continue; + do_resume = dev->flags & ATA_DFLAG_RESUMING; + spin_unlock_irqrestore(ap->lock, flags); + if (do_resume) { + ret = scsi_resume_device(sdev); + if (ret == -EWOULDBLOCK) + goto unlock; + dev->flags &= ~ATA_DFLAG_RESUMING; + } ret = scsi_rescan_device(sdev); scsi_device_put(sdev); spin_lock_irqsave(ap->lock, flags); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 8d06475de17a..ffd7e7e72933 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1642,6 +1642,40 @@ int scsi_add_device(struct Scsi_Host *host, uint channel, } EXPORT_SYMBOL(scsi_add_device); +int scsi_resume_device(struct scsi_device *sdev) +{ + struct device *dev = &sdev->sdev_gendev; + int ret = 0; + + device_lock(dev); + + /* + * Bail out if the device or its queue are not running. Otherwise, + * the rescan may block waiting for commands to be executed, with us + * holding the device lock. This can result in a potential deadlock + * in the power management core code when system resume is on-going. + */ + if (sdev->sdev_state != SDEV_RUNNING || + blk_queue_pm_only(sdev->request_queue)) { + ret = -EWOULDBLOCK; + goto unlock; + } + + if (dev->driver && try_module_get(dev->driver->owner)) { + struct scsi_driver *drv = to_scsi_driver(dev->driver); + + if (drv->resume) + ret = drv->resume(dev); + module_put(dev->driver->owner); + } + +unlock: + device_unlock(dev); + + return ret; +} +EXPORT_SYMBOL(scsi_resume_device); + int scsi_rescan_device(struct scsi_device *sdev) { struct device *dev = &sdev->sdev_gendev; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index ccff8f2e2e75..3cf898670290 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -4108,7 +4108,21 @@ static int sd_suspend_runtime(struct device *dev) return sd_suspend_common(dev, true); } -static int sd_resume(struct device *dev, bool runtime) +static int sd_resume(struct device *dev) +{ + struct scsi_disk *sdkp = dev_get_drvdata(dev); + + sd_printk(KERN_NOTICE, sdkp, "Starting disk\n"); + + if (opal_unlock_from_suspend(sdkp->opal_dev)) { + sd_printk(KERN_NOTICE, sdkp, "OPAL unlock failed\n"); + return -EIO; + } + + return 0; +} + +static int sd_resume_common(struct device *dev, bool runtime) { struct scsi_disk *sdkp = dev_get_drvdata(dev); int ret; @@ -4124,7 +4138,7 @@ static int sd_resume(struct device *dev, bool runtime) sd_printk(KERN_NOTICE, sdkp, "Starting disk\n"); ret = sd_start_stop_device(sdkp, 1); if (!ret) { - opal_unlock_from_suspend(sdkp->opal_dev); + sd_resume(dev); sdkp->suspended = false; } @@ -4143,7 +4157,7 @@ static int sd_resume_system(struct device *dev) return 0; } - return sd_resume(dev, false); + return sd_resume_common(dev, false); } static int sd_resume_runtime(struct device *dev) @@ -4170,7 +4184,7 @@ static int sd_resume_runtime(struct device *dev) "Failed to clear sense data\n"); } - return sd_resume(dev, true); + return sd_resume_common(dev, true); } static const struct dev_pm_ops sd_pm_ops = { @@ -4193,6 +4207,7 @@ static struct scsi_driver sd_template = { .pm = &sd_pm_ops, }, .rescan = sd_rescan, + .resume = sd_resume, .init_command = sd_init_command, .uninit_command = sd_uninit_command, .done = sd_done, diff --git a/include/linux/libata.h b/include/linux/libata.h index 26d68115afb8..324d792e7c78 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -107,6 +107,7 @@ enum { ATA_DFLAG_NCQ_PRIO_ENABLED = (1 << 20), /* Priority cmds sent to dev */ ATA_DFLAG_CDL_ENABLED = (1 << 21), /* cmd duration limits is enabled */ + ATA_DFLAG_RESUMING = (1 << 22), /* Device is resuming */ ATA_DFLAG_DETACH = (1 << 24), ATA_DFLAG_DETACHED = (1 << 25), ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */ diff --git a/include/scsi/scsi_driver.h b/include/scsi/scsi_driver.h index 4ce1988b2ba0..f40915d2ecee 100644 --- a/include/scsi/scsi_driver.h +++ b/include/scsi/scsi_driver.h @@ -12,6 +12,7 @@ struct request; struct scsi_driver { struct device_driver gendrv; + int (*resume)(struct device *); void (*rescan)(struct device *); blk_status_t (*init_command)(struct scsi_cmnd *); void (*uninit_command)(struct scsi_cmnd *); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index b259d42a1e1a..129001f600fc 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -767,6 +767,7 @@ scsi_template_proc_dir(const struct scsi_host_template *sht); #define scsi_template_proc_dir(sht) NULL #endif extern void scsi_scan_host(struct Scsi_Host *); +extern int scsi_resume_device(struct scsi_device *sdev); extern int scsi_rescan_device(struct scsi_device *sdev); extern void scsi_remove_host(struct Scsi_Host *); extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *); -- cgit v1.2.3 From 429846b4b6ce9853e0d803a2357bb2e55083adf0 Mon Sep 17 00:00:00 2001 From: Shin'ichiro Kawasaki Date: Sat, 23 Mar 2024 17:41:55 +0900 Subject: scsi: mpi3mr: Avoid memcpy field-spanning write WARNING When the "storcli2 show" command is executed for eHBA-9600, mpi3mr driver prints this WARNING message: memcpy: detected field-spanning write (size 128) of single field "bsg_reply_buf->reply_buf" at drivers/scsi/mpi3mr/mpi3mr_app.c:1658 (size 1) WARNING: CPU: 0 PID: 12760 at drivers/scsi/mpi3mr/mpi3mr_app.c:1658 mpi3mr_bsg_request+0x6b12/0x7f10 [mpi3mr] The cause of the WARN is 128 bytes memcpy to the 1 byte size array "__u8 replay_buf[1]" in the struct mpi3mr_bsg_in_reply_buf. The array is intended to be a flexible length array, so the WARN is a false positive. To suppress the WARN, remove the constant number '1' from the array declaration and clarify that it has flexible length. Also, adjust the memory allocation size to match the change. Suggested-by: Sathya Prakash Veerichetty Signed-off-by: Shin'ichiro Kawasaki Link: https://lore.kernel.org/r/20240323084155.166835-1-shinichiro.kawasaki@wdc.com Signed-off-by: Martin K. Petersen --- drivers/scsi/mpi3mr/mpi3mr_app.c | 2 +- include/uapi/scsi/scsi_bsg_mpi3mr.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/scsi/mpi3mr/mpi3mr_app.c b/drivers/scsi/mpi3mr/mpi3mr_app.c index 0380996b5ad2..55d590b91947 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_app.c +++ b/drivers/scsi/mpi3mr/mpi3mr_app.c @@ -1644,7 +1644,7 @@ static long mpi3mr_bsg_process_mpt_cmds(struct bsg_job *job) if ((mpirep_offset != 0xFF) && drv_bufs[mpirep_offset].bsg_buf_len) { drv_buf_iter = &drv_bufs[mpirep_offset]; - drv_buf_iter->kern_buf_len = (sizeof(*bsg_reply_buf) - 1 + + drv_buf_iter->kern_buf_len = (sizeof(*bsg_reply_buf) + mrioc->reply_sz); bsg_reply_buf = kzalloc(drv_buf_iter->kern_buf_len, GFP_KERNEL); diff --git a/include/uapi/scsi/scsi_bsg_mpi3mr.h b/include/uapi/scsi/scsi_bsg_mpi3mr.h index c72ce387286a..30a5c1a59376 100644 --- a/include/uapi/scsi/scsi_bsg_mpi3mr.h +++ b/include/uapi/scsi/scsi_bsg_mpi3mr.h @@ -382,7 +382,7 @@ struct mpi3mr_bsg_in_reply_buf { __u8 mpi_reply_type; __u8 rsvd1; __u16 rsvd2; - __u8 reply_buf[1]; + __u8 reply_buf[]; }; /** -- cgit v1.2.3 From c2ddeb29612f7ca84ed10c6d4f3ac99705135447 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 25 Mar 2024 13:58:08 +0100 Subject: genirq: Introduce IRQF_COND_ONESHOT and use it in pinctrl-amd There is a problem when a driver requests a shared interrupt line to run a threaded handler on it without IRQF_ONESHOT set if that flag has been set already for the IRQ in question by somebody else. Namely, the request fails which usually leads to a probe failure even though the driver might have worked just fine with IRQF_ONESHOT, but it does not want to use it by default. Currently, the only way to handle this is to try to request the IRQ without IRQF_ONESHOT, but with IRQF_PROBE_SHARED set and if this fails, try again with IRQF_ONESHOT set. However, this is a bit cumbersome and not very clean. When commit 7a36b901a6eb ("ACPI: OSL: Use a threaded interrupt handler for SCI") switched the ACPI subsystem over to using a threaded interrupt handler for the SCI, it had to use IRQF_ONESHOT for it because that's required due to the way the SCI handler works (it needs to walk all of the enabled GPEs before the interrupt line can be unmasked). The SCI interrupt line is not shared with other users very often due to the SCI handling overhead, but on sone systems it is shared and when the other user of it attempts to install a threaded handler, a flags mismatch related to IRQF_ONESHOT may occur. As it turned out, that happened to the pinctrl-amd driver and so commit 4451e8e8415e ("pinctrl: amd: Add IRQF_ONESHOT to the interrupt request") attempted to address the issue by adding IRQF_ONESHOT to the interrupt flags in that driver, but this is now causing an IRQF_ONESHOT-related mismatch to occur on another system which cannot boot as a result of it. Clearly, pinctrl-amd can work with IRQF_ONESHOT if need be, but it should not set that flag by default, so it needs a way to indicate that to the interrupt subsystem. To that end, introdcuce a new interrupt flag, IRQF_COND_ONESHOT, which will only have effect when the IRQ line is shared and IRQF_ONESHOT has been set for it already, in which case it will be promoted to the latter. This is sufficient for drivers sharing the interrupt line with the SCI as it is requested by the ACPI subsystem before any drivers are probed, so they will always see IRQF_ONESHOT set for the interrupt in question. Fixes: 4451e8e8415e ("pinctrl: amd: Add IRQF_ONESHOT to the interrupt request") Reported-by: Francisco Ayala Le Brun Signed-off-by: Rafael J. Wysocki Signed-off-by: Thomas Gleixner Reviewed-by: Linus Walleij Cc: 6.8+ # 6.8+ Closes: https://lore.kernel.org/lkml/CAN-StX1HqWqi+YW=t+V52-38Mfp5fAz7YHx4aH-CQjgyNiKx3g@mail.gmail.com/ Link: https://lore.kernel.org/r/12417336.O9o76ZdvQC@kreacher --- drivers/pinctrl/pinctrl-amd.c | 2 +- include/linux/interrupt.h | 3 +++ kernel/irq/manage.c | 9 +++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 49f89b70dcec..7f66ec73199a 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -1159,7 +1159,7 @@ static int amd_gpio_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, gpio_dev->irq, amd_gpio_irq_handler, - IRQF_SHARED | IRQF_ONESHOT, KBUILD_MODNAME, gpio_dev); + IRQF_SHARED | IRQF_COND_ONESHOT, KBUILD_MODNAME, gpio_dev); if (ret) goto out2; diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 76121c2bb4f8..5c9bdd3ffccc 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -67,6 +67,8 @@ * later. * IRQF_NO_DEBUG - Exclude from runnaway detection for IPI and similar handlers, * depends on IRQF_PERCPU. + * IRQF_COND_ONESHOT - Agree to do IRQF_ONESHOT if already set for a shared + * interrupt. */ #define IRQF_SHARED 0x00000080 #define IRQF_PROBE_SHARED 0x00000100 @@ -82,6 +84,7 @@ #define IRQF_COND_SUSPEND 0x00040000 #define IRQF_NO_AUTOEN 0x00080000 #define IRQF_NO_DEBUG 0x00100000 +#define IRQF_COND_ONESHOT 0x00200000 #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index ad3eaf2ab959..bf9ae8a8686f 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1643,8 +1643,13 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) } if (!((old->flags & new->flags) & IRQF_SHARED) || - (oldtype != (new->flags & IRQF_TRIGGER_MASK)) || - ((old->flags ^ new->flags) & IRQF_ONESHOT)) + (oldtype != (new->flags & IRQF_TRIGGER_MASK))) + goto mismatch; + + if ((old->flags & IRQF_ONESHOT) && + (new->flags & IRQF_COND_ONESHOT)) + new->flags |= IRQF_ONESHOT; + else if ((old->flags ^ new->flags) & IRQF_ONESHOT) goto mismatch; /* All handlers must agree on per-cpuness */ -- cgit v1.2.3 From 151c9c724d05d5b0dd8acd3e11cb69ef1f2dbada Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 22 Mar 2024 13:57:32 +0000 Subject: tcp: properly terminate timers for kernel sockets We had various syzbot reports about tcp timers firing after the corresponding netns has been dismantled. Fortunately Josef Bacik could trigger the issue more often, and could test a patch I wrote two years ago. When TCP sockets are closed, we call inet_csk_clear_xmit_timers() to 'stop' the timers. inet_csk_clear_xmit_timers() can be called from any context, including when socket lock is held. This is the reason it uses sk_stop_timer(), aka del_timer(). This means that ongoing timers might finish much later. For user sockets, this is fine because each running timer holds a reference on the socket, and the user socket holds a reference on the netns. For kernel sockets, we risk that the netns is freed before timer can complete, because kernel sockets do not hold reference on the netns. This patch adds inet_csk_clear_xmit_timers_sync() function that using sk_stop_timer_sync() to make sure all timers are terminated before the kernel socket is released. Modules using kernel sockets close them in their netns exit() handler. Also add sock_not_owned_by_me() helper to get LOCKDEP support : inet_csk_clear_xmit_timers_sync() must not be called while socket lock is held. It is very possible we can revert in the future commit 3a58f13a881e ("net: rds: acquire refcount on TCP sockets") which attempted to solve the issue in rds only. (net/smc/af_smc.c and net/mptcp/subflow.c have similar code) We probably can remove the check_net() tests from tcp_out_of_resources() and __tcp_close() in the future. Reported-by: Josef Bacik Closes: https://lore.kernel.org/netdev/20240314210740.GA2823176@perftesting/ Fixes: 26abe14379f8 ("net: Modify sk_alloc to not reference count the netns of kernel sockets.") Fixes: 8a68173691f0 ("net: sk_clone_lock() should only do get_net() if the parent is not a kernel socket") Link: https://lore.kernel.org/bpf/CANn89i+484ffqb93aQm1N-tjxxvb3WDKX0EbD7318RwRgsatjw@mail.gmail.com/ Signed-off-by: Eric Dumazet Tested-by: Josef Bacik Cc: Tetsuo Handa Link: https://lore.kernel.org/r/20240322135732.1535772-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- include/net/inet_connection_sock.h | 1 + include/net/sock.h | 7 +++++++ net/ipv4/inet_connection_sock.c | 14 ++++++++++++++ net/ipv4/tcp.c | 2 ++ 4 files changed, 24 insertions(+) (limited to 'include') diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 9ab4bf704e86..ccf171f7eb60 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -175,6 +175,7 @@ void inet_csk_init_xmit_timers(struct sock *sk, void (*delack_handler)(struct timer_list *), void (*keepalive_handler)(struct timer_list *)); void inet_csk_clear_xmit_timers(struct sock *sk); +void inet_csk_clear_xmit_timers_sync(struct sock *sk); static inline void inet_csk_schedule_ack(struct sock *sk) { diff --git a/include/net/sock.h b/include/net/sock.h index b5e00702acc1..f57bfd8a2ad2 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1759,6 +1759,13 @@ static inline void sock_owned_by_me(const struct sock *sk) #endif } +static inline void sock_not_owned_by_me(const struct sock *sk) +{ +#ifdef CONFIG_LOCKDEP + WARN_ON_ONCE(lockdep_sock_is_held(sk) && debug_locks); +#endif +} + static inline bool sock_owned_by_user(const struct sock *sk) { sock_owned_by_me(sk); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7d8090f109ef..c038e28e2f1e 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -771,6 +771,20 @@ void inet_csk_clear_xmit_timers(struct sock *sk) } EXPORT_SYMBOL(inet_csk_clear_xmit_timers); +void inet_csk_clear_xmit_timers_sync(struct sock *sk) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + + /* ongoing timer handlers need to acquire socket lock. */ + sock_not_owned_by_me(sk); + + icsk->icsk_pending = icsk->icsk_ack.pending = 0; + + sk_stop_timer_sync(sk, &icsk->icsk_retransmit_timer); + sk_stop_timer_sync(sk, &icsk->icsk_delack_timer); + sk_stop_timer_sync(sk, &sk->sk_timer); +} + void inet_csk_delete_keepalive_timer(struct sock *sk) { sk_stop_timer(sk, &sk->sk_timer); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index d20b62d52171..e767721b3a58 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2931,6 +2931,8 @@ void tcp_close(struct sock *sk, long timeout) lock_sock(sk); __tcp_close(sk, timeout); release_sock(sk); + if (!sk->sk_net_refcnt) + inet_csk_clear_xmit_timers_sync(sk); sock_put(sk); } EXPORT_SYMBOL(tcp_close); -- cgit v1.2.3 From 52464f59a361a3ba49d6eabc4f65d5c0b9d1de39 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 25 Mar 2024 17:00:57 +0000 Subject: gpiolib: Add stubs for GPIO lookup functions The gpio_device_find_by_() functions do not have stubs which means that if they are referenced from code with an optiona dependency on gpiolib then the code will fail to link. Add stubs for lookups via fwnode and label. I have not added a stub for plain gpio_device_find() since it seems harder to see a use case for that which does not depend on gpiolib. With the addition of the GPIO reset controller (which lacks a gpiolib dependency) to the arm64 defconfig this is causing build breaks for arm64 virtconfig in -next: aarch64-linux-gnu-ld: drivers/reset/core.o: in function `__reset_add_reset_gpio_lookup': /build/stage/linux/drivers/reset/core.c:861:(.text+0xccc): undefined reference to `gpio_device_find_by_fwnode' Signed-off-by: Mark Brown Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index dc75f802e284..f8617eaf08ba 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -646,8 +646,6 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, struct gpio_device *gpio_device_find(const void *data, int (*match)(struct gpio_chip *gc, const void *data)); -struct gpio_device *gpio_device_find_by_label(const char *label); -struct gpio_device *gpio_device_find_by_fwnode(const struct fwnode_handle *fwnode); struct gpio_device *gpio_device_get(struct gpio_device *gdev); void gpio_device_put(struct gpio_device *gdev); @@ -814,6 +812,9 @@ struct gpio_device *gpiod_to_gpio_device(struct gpio_desc *desc); int gpio_device_get_base(struct gpio_device *gdev); const char *gpio_device_get_label(struct gpio_device *gdev); +struct gpio_device *gpio_device_find_by_label(const char *label); +struct gpio_device *gpio_device_find_by_fwnode(const struct fwnode_handle *fwnode); + #else /* CONFIG_GPIOLIB */ #include @@ -843,6 +844,18 @@ static inline const char *gpio_device_get_label(struct gpio_device *gdev) return NULL; } +static inline struct gpio_device *gpio_device_find_by_label(const char *label) +{ + WARN_ON(1); + return NULL; +} + +static inline struct gpio_device *gpio_device_find_by_fwnode(const struct fwnode_handle *fwnode) +{ + WARN_ON(1); + return NULL; +} + static inline int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) { -- cgit v1.2.3 From 9cecde80aae0fb0aa44425575d5aca71bc646d89 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Fri, 15 Mar 2024 14:08:21 +0000 Subject: mm: increase folio batch size On a 104 thread, 2 socket Skylake system, Intel report a 4.7% performance reduction with will-it-scale page_fault2. This was due to reducing the size of the batch from 32 to 15. Increasing the folio batch size from 15 to 31 gives a performance increase of 12.5% relative to the original, or 17.2% relative to the reduced performance commit. The penalty of this commit is an additional 128 bytes of stack usage. Six folio_batches are also allocated from percpu memory in cpu_fbatches so that will be an additional 768 bytes of percpu memory (per CPU). Tim Chen originally submitted a patch like this in 2020: https://lore.kernel.org/linux-mm/d1cc9f12a8ad6c2a52cb600d93b06b064f2bbc57.1593205965.git.tim.c.chen@linux.intel.com/ Link: https://lkml.kernel.org/r/20240315140823.2478146-1-willy@infradead.org Fixes: 99fbb6bfc16f ("mm: make folios_put() the basis of release_pages()") Signed-off-by: Matthew Wilcox (Oracle) Tested-by: Yujie Liu Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202403151058.7048f6a8-oliver.sang@intel.com Signed-off-by: Andrew Morton --- include/linux/pagevec.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/pagevec.h b/include/linux/pagevec.h index fcc06c300a72..5d3a0cccc6bf 100644 --- a/include/linux/pagevec.h +++ b/include/linux/pagevec.h @@ -11,8 +11,8 @@ #include -/* 15 pointers + header align the folio_batch structure to a power of two */ -#define PAGEVEC_SIZE 15 +/* 31 pointers + header align the folio_batch structure to a power of two */ +#define PAGEVEC_SIZE 31 struct folio; -- cgit v1.2.3 From d5aad4c2ca057e760a92a9a7d65bd38d72963f27 Mon Sep 17 00:00:00 2001 From: Zev Weiss Date: Mon, 26 Feb 2024 17:35:41 -0800 Subject: prctl: generalize PR_SET_MDWE support check to be per-arch Patch series "ARM: prctl: Reject PR_SET_MDWE where not supported". I noticed after a recent kernel update that my ARM926 system started segfaulting on any execve() after calling prctl(PR_SET_MDWE). After some investigation it appears that ARMv5 is incapable of providing the appropriate protections for MDWE, since any readable memory is also implicitly executable. The prctl_set_mdwe() function already had some special-case logic added disabling it on PARISC (commit 793838138c15, "prctl: Disable prctl(PR_SET_MDWE) on parisc"); this patch series (1) generalizes that check to use an arch_*() function, and (2) adds a corresponding override for ARM to disable MDWE on pre-ARMv6 CPUs. With the series applied, prctl(PR_SET_MDWE) is rejected on ARMv5 and subsequent execve() calls (as well as mmap(PROT_READ|PROT_WRITE)) can succeed instead of unconditionally failing; on ARMv6 the prctl works as it did previously. [0] https://lore.kernel.org/all/2023112456-linked-nape-bf19@gregkh/ This patch (of 2): There exist systems other than PARISC where MDWE may not be feasible to support; rather than cluttering up the generic code with additional arch-specific logic let's add a generic function for checking MDWE support and allow each arch to override it as needed. Link: https://lkml.kernel.org/r/20240227013546.15769-4-zev@bewilderbeest.net Link: https://lkml.kernel.org/r/20240227013546.15769-5-zev@bewilderbeest.net Signed-off-by: Zev Weiss Acked-by: Helge Deller [parisc] Cc: Borislav Petkov Cc: David Hildenbrand Cc: Florent Revest Cc: "James E.J. Bottomley" Cc: Josh Triplett Cc: Kees Cook Cc: Miguel Ojeda Cc: Mike Rapoport (IBM) Cc: Oleg Nesterov Cc: Ondrej Mosnacek Cc: Rick Edgecombe Cc: Russell King (Oracle) Cc: Sam James Cc: Stefan Roesch Cc: Yang Shi Cc: Yin Fengwei Cc: [6.3+] Signed-off-by: Andrew Morton --- arch/parisc/include/asm/mman.h | 14 ++++++++++++++ include/linux/mman.h | 8 ++++++++ kernel/sys.c | 7 +++++-- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 arch/parisc/include/asm/mman.h (limited to 'include') diff --git a/arch/parisc/include/asm/mman.h b/arch/parisc/include/asm/mman.h new file mode 100644 index 000000000000..47c5a1991d10 --- /dev/null +++ b/arch/parisc/include/asm/mman.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_MMAN_H__ +#define __ASM_MMAN_H__ + +#include + +/* PARISC cannot allow mdwe as it needs writable stacks */ +static inline bool arch_memory_deny_write_exec_supported(void) +{ + return false; +} +#define arch_memory_deny_write_exec_supported arch_memory_deny_write_exec_supported + +#endif /* __ASM_MMAN_H__ */ diff --git a/include/linux/mman.h b/include/linux/mman.h index dc7048824be8..bcb201ab7a41 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -162,6 +162,14 @@ calc_vm_flag_bits(unsigned long flags) unsigned long vm_commit_limit(void); +#ifndef arch_memory_deny_write_exec_supported +static inline bool arch_memory_deny_write_exec_supported(void) +{ + return true; +} +#define arch_memory_deny_write_exec_supported arch_memory_deny_write_exec_supported +#endif + /* * Denies creating a writable executable mapping or gaining executable permissions. * diff --git a/kernel/sys.c b/kernel/sys.c index f8e543f1e38a..8bb106a56b3a 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2408,8 +2408,11 @@ static inline int prctl_set_mdwe(unsigned long bits, unsigned long arg3, if (bits & PR_MDWE_NO_INHERIT && !(bits & PR_MDWE_REFUSE_EXEC_GAIN)) return -EINVAL; - /* PARISC cannot allow mdwe as it needs writable stacks */ - if (IS_ENABLED(CONFIG_PARISC)) + /* + * EOPNOTSUPP might be more appropriate here in principle, but + * existing userspace depends on EINVAL specifically. + */ + if (!arch_memory_deny_write_exec_supported()) return -EINVAL; current_bits = get_current_mdwe(); -- cgit v1.2.3 From ea2c09283b44d1a3732a195a9b257d56779c8863 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 25 Mar 2024 09:25:05 +0100 Subject: net: wan: framer: Add missing static inline qualifiers Compilation with CONFIG_GENERIC_FRAMER disabled lead to the following warnings: framer.h:184:16: warning: no previous prototype for function 'framer_get' [-Wmissing-prototypes] 184 | struct framer *framer_get(struct device *dev, const char *con_id) framer.h:184:1: note: declare 'static' if the function is not intended to be used outside of this translation unit 184 | struct framer *framer_get(struct device *dev, const char *con_id) framer.h:189:6: warning: no previous prototype for function 'framer_put' [-Wmissing-prototypes] 189 | void framer_put(struct device *dev, struct framer *framer) framer.h:189:1: note: declare 'static' if the function is not intended to be used outside of this translation unit 189 | void framer_put(struct device *dev, struct framer *framer) Add missing 'static inline' qualifiers for these functions. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202403241110.hfJqeJRu-lkp@intel.com/ Fixes: 82c944d05b1a ("net: wan: Add framer framework support") Cc: stable@vger.kernel.org Signed-off-by: Herve Codina Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- include/linux/framer/framer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/framer/framer.h b/include/linux/framer/framer.h index 9a9b88962c29..2b85fe9e7f9a 100644 --- a/include/linux/framer/framer.h +++ b/include/linux/framer/framer.h @@ -181,12 +181,12 @@ static inline int framer_notifier_unregister(struct framer *framer, return -ENOSYS; } -struct framer *framer_get(struct device *dev, const char *con_id) +static inline struct framer *framer_get(struct device *dev, const char *con_id) { return ERR_PTR(-ENOSYS); } -void framer_put(struct device *dev, struct framer *framer) +static inline void framer_put(struct device *dev, struct framer *framer) { } -- cgit v1.2.3 From 0cac183b98d8a8c692c98e8dba37df15a9e9210d Mon Sep 17 00:00:00 2001 From: Jonathan Kim Date: Fri, 1 Mar 2024 12:25:17 -0500 Subject: drm/amdkfd: range check cp bad op exception interrupts Due to a CP interrupt bug, bad packet garbage exception codes are raised. Do a range check so that the debugger and runtime do not receive garbage codes. Update the user api to guard exception code type checking as well. Signed-off-by: Jonathan Kim Tested-by: Jesse Zhang Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_int_process_v10.c | 3 ++- drivers/gpu/drm/amd/amdkfd/kfd_int_process_v11.c | 3 ++- drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c | 3 ++- include/uapi/linux/kfd_ioctl.h | 17 ++++++++++++++--- 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v10.c index 9a06c6fb6605..40a21be6c07c 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v10.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v10.c @@ -339,7 +339,8 @@ static void event_interrupt_wq_v10(struct kfd_node *dev, break; } kfd_signal_event_interrupt(pasid, context_id0 & 0x7fffff, 23); - } else if (source_id == SOC15_INTSRC_CP_BAD_OPCODE) { + } else if (source_id == SOC15_INTSRC_CP_BAD_OPCODE && + KFD_DBG_EC_TYPE_IS_PACKET(KFD_DEBUG_CP_BAD_OP_ECODE(context_id0))) { kfd_set_dbg_ev_from_interrupt(dev, pasid, KFD_DEBUG_DOORBELL_ID(context_id0), KFD_EC_MASK(KFD_DEBUG_CP_BAD_OP_ECODE(context_id0)), diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v11.c index 7e2859736a55..fe2ad0c0de95 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v11.c @@ -328,7 +328,8 @@ static void event_interrupt_wq_v11(struct kfd_node *dev, /* CP */ if (source_id == SOC15_INTSRC_CP_END_OF_PIPE) kfd_signal_event_interrupt(pasid, context_id0, 32); - else if (source_id == SOC15_INTSRC_CP_BAD_OPCODE) + else if (source_id == SOC15_INTSRC_CP_BAD_OPCODE && + KFD_DBG_EC_TYPE_IS_PACKET(KFD_CTXID0_CP_BAD_OP_ECODE(context_id0))) kfd_set_dbg_ev_from_interrupt(dev, pasid, KFD_CTXID0_DOORBELL_ID(context_id0), KFD_EC_MASK(KFD_CTXID0_CP_BAD_OP_ECODE(context_id0)), diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c index 91dd5e045b51..c4c6a29052ac 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c @@ -388,7 +388,8 @@ static void event_interrupt_wq_v9(struct kfd_node *dev, break; } kfd_signal_event_interrupt(pasid, sq_int_data, 24); - } else if (source_id == SOC15_INTSRC_CP_BAD_OPCODE) { + } else if (source_id == SOC15_INTSRC_CP_BAD_OPCODE && + KFD_DBG_EC_TYPE_IS_PACKET(KFD_DEBUG_CP_BAD_OP_ECODE(context_id0))) { kfd_set_dbg_ev_from_interrupt(dev, pasid, KFD_DEBUG_DOORBELL_ID(context_id0), KFD_EC_MASK(KFD_DEBUG_CP_BAD_OP_ECODE(context_id0)), diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h index 9ce46edc62a5..2040a470ddb4 100644 --- a/include/uapi/linux/kfd_ioctl.h +++ b/include/uapi/linux/kfd_ioctl.h @@ -913,14 +913,25 @@ enum kfd_dbg_trap_exception_code { KFD_EC_MASK(EC_DEVICE_NEW)) #define KFD_EC_MASK_PROCESS (KFD_EC_MASK(EC_PROCESS_RUNTIME) | \ KFD_EC_MASK(EC_PROCESS_DEVICE_REMOVE)) +#define KFD_EC_MASK_PACKET (KFD_EC_MASK(EC_QUEUE_PACKET_DISPATCH_DIM_INVALID) | \ + KFD_EC_MASK(EC_QUEUE_PACKET_DISPATCH_GROUP_SEGMENT_SIZE_INVALID) | \ + KFD_EC_MASK(EC_QUEUE_PACKET_DISPATCH_CODE_INVALID) | \ + KFD_EC_MASK(EC_QUEUE_PACKET_RESERVED) | \ + KFD_EC_MASK(EC_QUEUE_PACKET_UNSUPPORTED) | \ + KFD_EC_MASK(EC_QUEUE_PACKET_DISPATCH_WORK_GROUP_SIZE_INVALID) | \ + KFD_EC_MASK(EC_QUEUE_PACKET_DISPATCH_REGISTER_INVALID) | \ + KFD_EC_MASK(EC_QUEUE_PACKET_VENDOR_UNSUPPORTED)) /* Checks for exception code types for KFD search */ +#define KFD_DBG_EC_IS_VALID(ecode) (ecode > EC_NONE && ecode < EC_MAX) #define KFD_DBG_EC_TYPE_IS_QUEUE(ecode) \ - (!!(KFD_EC_MASK(ecode) & KFD_EC_MASK_QUEUE)) + (KFD_DBG_EC_IS_VALID(ecode) && !!(KFD_EC_MASK(ecode) & KFD_EC_MASK_QUEUE)) #define KFD_DBG_EC_TYPE_IS_DEVICE(ecode) \ - (!!(KFD_EC_MASK(ecode) & KFD_EC_MASK_DEVICE)) + (KFD_DBG_EC_IS_VALID(ecode) && !!(KFD_EC_MASK(ecode) & KFD_EC_MASK_DEVICE)) #define KFD_DBG_EC_TYPE_IS_PROCESS(ecode) \ - (!!(KFD_EC_MASK(ecode) & KFD_EC_MASK_PROCESS)) + (KFD_DBG_EC_IS_VALID(ecode) && !!(KFD_EC_MASK(ecode) & KFD_EC_MASK_PROCESS)) +#define KFD_DBG_EC_TYPE_IS_PACKET(ecode) \ + (KFD_DBG_EC_IS_VALID(ecode) && !!(KFD_EC_MASK(ecode) & KFD_EC_MASK_PACKET)) /* Runtime enable states */ -- cgit v1.2.3 From 0316e4b04e0156633df7474bae8e8b0791ce028f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 23 Mar 2024 18:06:15 +0900 Subject: export.h: remove include/asm-generic/export.h Commit 3a6dd5f614a1 ("riscv: remove unneeded #include ") removed the last use of include/asm-generic/export.h. This deprecated header can go away. Signed-off-by: Masahiro Yamada --- include/asm-generic/export.h | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 include/asm-generic/export.h (limited to 'include') diff --git a/include/asm-generic/export.h b/include/asm-generic/export.h deleted file mode 100644 index 570cd4da7210..000000000000 --- a/include/asm-generic/export.h +++ /dev/null @@ -1,11 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef __ASM_GENERIC_EXPORT_H -#define __ASM_GENERIC_EXPORT_H - -/* - * and are deprecated. - * Please include directly. - */ -#include - -#endif -- cgit v1.2.3 From 18685451fc4e546fc0e718580d32df3c0e5c8272 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 26 Mar 2024 11:18:41 +0100 Subject: inet: inet_defrag: prevent sk release while still in use ip_local_out() and other functions can pass skb->sk as function argument. If the skb is a fragment and reassembly happens before such function call returns, the sk must not be released. This affects skb fragments reassembled via netfilter or similar modules, e.g. openvswitch or ct_act.c, when run as part of tx pipeline. Eric Dumazet made an initial analysis of this bug. Quoting Eric: Calling ip_defrag() in output path is also implying skb_orphan(), which is buggy because output path relies on sk not disappearing. A relevant old patch about the issue was : 8282f27449bf ("inet: frag: Always orphan skbs inside ip_defrag()") [..] net/ipv4/ip_output.c depends on skb->sk being set, and probably to an inet socket, not an arbitrary one. If we orphan the packet in ipvlan, then downstream things like FQ packet scheduler will not work properly. We need to change ip_defrag() to only use skb_orphan() when really needed, ie whenever frag_list is going to be used. Eric suggested to stash sk in fragment queue and made an initial patch. However there is a problem with this: If skb is refragmented again right after, ip_do_fragment() will copy head->sk to the new fragments, and sets up destructor to sock_wfree. IOW, we have no choice but to fix up sk_wmem accouting to reflect the fully reassembled skb, else wmem will underflow. This change moves the orphan down into the core, to last possible moment. As ip_defrag_offset is aliased with sk_buff->sk member, we must move the offset into the FRAG_CB, else skb->sk gets clobbered. This allows to delay the orphaning long enough to learn if the skb has to be queued or if the skb is completing the reasm queue. In the former case, things work as before, skb is orphaned. This is safe because skb gets queued/stolen and won't continue past reasm engine. In the latter case, we will steal the skb->sk reference, reattach it to the head skb, and fix up wmem accouting when inet_frag inflates truesize. Fixes: 7026b1ddb6b8 ("netfilter: Pass socket pointer down through okfn().") Diagnosed-by: Eric Dumazet Reported-by: xingwei lee Reported-by: yue sun Reported-by: syzbot+e5167d7144a62715044c@syzkaller.appspotmail.com Signed-off-by: Florian Westphal Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20240326101845.30836-1-fw@strlen.de Signed-off-by: Paolo Abeni --- include/linux/skbuff.h | 7 +--- net/ipv4/inet_fragment.c | 70 +++++++++++++++++++++++++++------ net/ipv4/ip_fragment.c | 2 +- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- 4 files changed, 60 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 0c7c67b3a87b..9d24aec064e8 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -753,8 +753,6 @@ typedef unsigned char *sk_buff_data_t; * @list: queue head * @ll_node: anchor in an llist (eg socket defer_list) * @sk: Socket we are owned by - * @ip_defrag_offset: (aka @sk) alternate use of @sk, used in - * fragmentation management * @dev: Device we arrived on/are leaving by * @dev_scratch: (aka @dev) alternate use of @dev when @dev would be %NULL * @cb: Control buffer. Free for use by every layer. Put private vars here @@ -875,10 +873,7 @@ struct sk_buff { struct llist_node ll_node; }; - union { - struct sock *sk; - int ip_defrag_offset; - }; + struct sock *sk; union { ktime_t tstamp; diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 7072fc0783ef..c88c9034d630 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -24,6 +24,8 @@ #include #include +#include "../core/sock_destructor.h" + /* Use skb->cb to track consecutive/adjacent fragments coming at * the end of the queue. Nodes in the rb-tree queue will * contain "runs" of one or more adjacent fragments. @@ -39,6 +41,7 @@ struct ipfrag_skb_cb { }; struct sk_buff *next_frag; int frag_run_len; + int ip_defrag_offset; }; #define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb)) @@ -396,12 +399,12 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, */ if (!last) fragrun_create(q, skb); /* First fragment. */ - else if (last->ip_defrag_offset + last->len < end) { + else if (FRAG_CB(last)->ip_defrag_offset + last->len < end) { /* This is the common case: skb goes to the end. */ /* Detect and discard overlaps. */ - if (offset < last->ip_defrag_offset + last->len) + if (offset < FRAG_CB(last)->ip_defrag_offset + last->len) return IPFRAG_OVERLAP; - if (offset == last->ip_defrag_offset + last->len) + if (offset == FRAG_CB(last)->ip_defrag_offset + last->len) fragrun_append_to_last(q, skb); else fragrun_create(q, skb); @@ -418,13 +421,13 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, parent = *rbn; curr = rb_to_skb(parent); - curr_run_end = curr->ip_defrag_offset + + curr_run_end = FRAG_CB(curr)->ip_defrag_offset + FRAG_CB(curr)->frag_run_len; - if (end <= curr->ip_defrag_offset) + if (end <= FRAG_CB(curr)->ip_defrag_offset) rbn = &parent->rb_left; else if (offset >= curr_run_end) rbn = &parent->rb_right; - else if (offset >= curr->ip_defrag_offset && + else if (offset >= FRAG_CB(curr)->ip_defrag_offset && end <= curr_run_end) return IPFRAG_DUP; else @@ -438,7 +441,7 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, rb_insert_color(&skb->rbnode, &q->rb_fragments); } - skb->ip_defrag_offset = offset; + FRAG_CB(skb)->ip_defrag_offset = offset; return IPFRAG_OK; } @@ -448,13 +451,28 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, struct sk_buff *parent) { struct sk_buff *fp, *head = skb_rb_first(&q->rb_fragments); - struct sk_buff **nextp; + void (*destructor)(struct sk_buff *); + unsigned int orig_truesize = 0; + struct sk_buff **nextp = NULL; + struct sock *sk = skb->sk; int delta; + if (sk && is_skb_wmem(skb)) { + /* TX: skb->sk might have been passed as argument to + * dst->output and must remain valid until tx completes. + * + * Move sk to reassembled skb and fix up wmem accounting. + */ + orig_truesize = skb->truesize; + destructor = skb->destructor; + } + if (head != skb) { fp = skb_clone(skb, GFP_ATOMIC); - if (!fp) - return NULL; + if (!fp) { + head = skb; + goto out_restore_sk; + } FRAG_CB(fp)->next_frag = FRAG_CB(skb)->next_frag; if (RB_EMPTY_NODE(&skb->rbnode)) FRAG_CB(parent)->next_frag = fp; @@ -463,6 +481,12 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, &q->rb_fragments); if (q->fragments_tail == skb) q->fragments_tail = fp; + + if (orig_truesize) { + /* prevent skb_morph from releasing sk */ + skb->sk = NULL; + skb->destructor = NULL; + } skb_morph(skb, head); FRAG_CB(skb)->next_frag = FRAG_CB(head)->next_frag; rb_replace_node(&head->rbnode, &skb->rbnode, @@ -470,13 +494,13 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, consume_skb(head); head = skb; } - WARN_ON(head->ip_defrag_offset != 0); + WARN_ON(FRAG_CB(head)->ip_defrag_offset != 0); delta = -head->truesize; /* Head of list must not be cloned. */ if (skb_unclone(head, GFP_ATOMIC)) - return NULL; + goto out_restore_sk; delta += head->truesize; if (delta) @@ -492,7 +516,7 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, clone = alloc_skb(0, GFP_ATOMIC); if (!clone) - return NULL; + goto out_restore_sk; skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; skb_frag_list_init(head); for (i = 0; i < skb_shinfo(head)->nr_frags; i++) @@ -509,6 +533,21 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, nextp = &skb_shinfo(head)->frag_list; } +out_restore_sk: + if (orig_truesize) { + int ts_delta = head->truesize - orig_truesize; + + /* if this reassembled skb is fragmented later, + * fraglist skbs will get skb->sk assigned from head->sk, + * and each frag skb will be released via sock_wfree. + * + * Update sk_wmem_alloc. + */ + head->sk = sk; + head->destructor = destructor; + refcount_add(ts_delta, &sk->sk_wmem_alloc); + } + return nextp; } EXPORT_SYMBOL(inet_frag_reasm_prepare); @@ -516,6 +555,8 @@ EXPORT_SYMBOL(inet_frag_reasm_prepare); void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head, void *reasm_data, bool try_coalesce) { + struct sock *sk = is_skb_wmem(head) ? head->sk : NULL; + const unsigned int head_truesize = head->truesize; struct sk_buff **nextp = reasm_data; struct rb_node *rbn; struct sk_buff *fp; @@ -579,6 +620,9 @@ void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head, head->prev = NULL; head->tstamp = q->stamp; head->mono_delivery_time = q->mono_delivery_time; + + if (sk) + refcount_add(sum_truesize - head_truesize, &sk->sk_wmem_alloc); } EXPORT_SYMBOL(inet_frag_reasm_finish); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index a4941f53b523..fb947d1613fe 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -384,6 +384,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) } skb_dst_drop(skb); + skb_orphan(skb); return -EINPROGRESS; insert_error: @@ -487,7 +488,6 @@ int ip_defrag(struct net *net, struct sk_buff *skb, u32 user) struct ipq *qp; __IP_INC_STATS(net, IPSTATS_MIB_REASMREQDS); - skb_orphan(skb); /* Lookup (or create) queue header */ qp = ip_find(net, ip_hdr(skb), user, vif); diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 1a51a44571c3..d0dcbaca1994 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -294,6 +294,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, } skb_dst_drop(skb); + skb_orphan(skb); return -EINPROGRESS; insert_error: @@ -469,7 +470,6 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user) hdr = ipv6_hdr(skb); fhdr = (struct frag_hdr *)skb_transport_header(skb); - skb_orphan(skb); fq = fq_find(net, fhdr->identification, user, hdr, skb->dev ? skb->dev->ifindex : 0); if (fq == NULL) { -- cgit v1.2.3