From nobody Tue Jun 23 04:04:30 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C36C7C433EF for ; Fri, 11 Mar 2022 01:33:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345374AbiCKBeP (ORCPT ); Thu, 10 Mar 2022 20:34:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50276 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345359AbiCKBeO (ORCPT ); Thu, 10 Mar 2022 20:34:14 -0500 Received: from mail-4316.protonmail.ch (mail-4316.protonmail.ch [185.70.43.16]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0432CF4071 for ; Thu, 10 Mar 2022 17:33:10 -0800 (PST) Date: Fri, 11 Mar 2022 01:32:59 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1646962388; bh=or5Z9XQTnZe/TfwjOEmbt6cnCyypwWzc2e6b6BZ37x4=; h=Date:To:From:Cc:Reply-To:Subject:Message-ID:In-Reply-To: References:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID; b=rfemLG9F0YBb9Vah2je7PZjNX/3vX3mCsL9oEFA6YCNZd8O3A1p/I/y4jP1UGVmNr vzX/zUlxu+gJqzSxAB21SXEB+SjkXFrETGwegaVuSsCM3DeRYM6swGaLyswKZfffpq ulVOCbLV6PPd1tdA4wctQXU1iXw7h5SaIl04rKS+OrqA3qQCdkxvBtJPya3qz6ImkY i/sihzagyo/xC2K/574FRz+VIEjbTuxagfKjUZaEC8IdBCBgdYpOB0xdS1oHbysvkk wHpLUUU2rNFg7pHMw6U1I2jXG8ifOxh3T3QbuzritLrhLE+T9PNgYapgnB9j3NsUDE yg2kw1F8ODFXQ== To: Linus Torvalds , linux-kernel@vger.kernel.org From: =?utf-8?Q?Barnab=C3=A1s_P=C5=91cze?= Cc: Greg Kroah-Hartman , Andrew Morton , Xiaomeng Tong , Kees Cook , Jakob Koschel , Arnd Bergmann Reply-To: =?utf-8?Q?Barnab=C3=A1s_P=C5=91cze?= Subject: [RFC PATCH v1 1/2] list: add type-safer list_head wrapper Message-ID: <20220311013238.3387227-2-pobrn@protonmail.com> In-Reply-To: <20220311013238.3387227-1-pobrn@protonmail.com> References: <20220311013238.3387227-1-pobrn@protonmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" The aim of this patch is to add a type-safer, lightweight wrapper around the currently available `list_head` facilities existing in the kernel. It is named "tlist", which may or may not stand for "typed list". The type-safe(r)ty is achieved by storing compile-time metadata in the list head: * the type of the objects ("value type"), and * the offset of the `list_head` member ("member offset") These only appear at compile-time, and do not affect the actual generated executable code. (They might show up in debug info, etc.) The underlying idea is to define each list head using an anonymous struct: #define TLIST(T, member) struct { struct list_head head; char _member_offset[0][offsetof(T, member) + BUILD_BUG_ON_ZERO(!__same_type(struct list_head, typeof_member(T, = member)))]; T _type[0]; } Arguably, this is an abuse of multiple features of GNU C. However, it is nothing new. The currently existing `__STRUCT_KFIFO_COMMON()` macro uses the same underlying idea to store type and size information in the type, like a C++ template. Note, that the `member` in `T` must be a `struct list_head` object, it is not possible to specify a non-list-head member by accident. Let us assume we have a tlist: struct wmi_block { struct wmi_device dev; struct list_head list; ... }; TLIST(struct wmi_block, list) wmi_block_list =3D ...; Note, that the items in the list still only embed a `list_head` object. Only the head of the list has a different type. In this scenario, the value type can be retrieved using: typeof(*wmi_block_list._type) and the member offset: sizeof(*wmi_block_list._member_offset) Looking at the `__STRUCT_KFIFO_COMMON()` one might ask: why aren't pointers used? Why zero-length arrays? The answer is const-correctness - that is, a const tlist can only be iterated with a `const value_type *` -, which I believe is nice to have. With the previous type and offset primitives, it is easy to implement a type-safe insertion macro: #define __tlist_ptroff(base, offset, T) ((T *) (((uintptr_t)(base)) + (offset))) #define tlist_item_to_node(list, item) (__tlist_ptroff((item), tlist_member_offset(list), struct list_he= ad) + BUILD_BUG_ON_ZERO(!__same_type(*(item), tlist_value_type(list)))) #define tlist_push_back(list, item) list_add_tail(tlist_head(list), tlist_item_to_node((list), (item)= )) Note, `tlist_head()` is a macro which simply expands to the underlying `list_head`. The real type checking is done inside the `tlist_item_to_node()` macro which does two things: * get a pointer to the embedded `list_head` object using the known member offset, and * check if `item` matches the value type of the tlist Note, that the existing `list_head` facilities can still be used since the head of a tlist can be easily retrieved and the list items only include a `list_head` object. All in all, for insertion, a tlist completely elliminates the possibility of inserting an object with the wrong type or wrong `list_head` member. (Of course, assuming the appropriate wrappers are used.) Iteration is also possible in a very convenient manner: #define tlist_node_to_item(list, node) __tlist_ptroff((node), -tlist_member_offset(list), tlist_value_ty= pe(list)) #define tlist_begin(list) tlist_node_to_item((list), tlist_head(list)->next) #define tlist_end(list) tlist_node_to_item((list), tlist_head(list)) #define tlist_item_next(list, item) \ tlist_node_to_item((list), tlist_item_to_node((list), (item))->ne= xt) #define tlist_for_each(list, iter) for (tlist_value_type(list) *iter =3D tlist_begin(list); iter !=3D tlist_end(list); iter =3D tlist_item_next(list, iter)) ... tlist_for_each(&wmi_block_list, wblock) { if (guid_equal(&wblock->gblock.guid, &guid_input)) { if (out) *out =3D wblock; return AE_OK; } } Note, the iterator is defined in the scope of the for loop, reuse after the loop is not possible by accident. Also note, that neither the type, nor the name of the correct `list_head` member need to be known (or specified) when iterating over a tlist. The "safe" iterations are also easily implementable (see this patch) without even needing to explicitly name/create the secondary iterator. Reverse iteration is naturally also implementable. Removal from a tlist is possible using the already existing `list_del()`, etc. facilities, or there is the newly added #define tlist_remove(list, item) list_del(tlist_item_to_node((list), (item))) This macro also does type-checking and selects the correct `list_head` member based on the known offset. There are, however, certain limitations of the tlist wrapper. The biggest of which is that they cannot easily be passed from/to functions since every tlist has a distinct type from every other type because they are anonymous structs. Furthermore, when declaring a tlist, the value type must be complete, otherwise e.g. the `offsetof()` would not work. Thus it is not possible to have a list of nodes which has no "separate" head. Signed-off-by: Barnab=C3=A1s P=C5=91cze --- include/linux/tlist.h | 75 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 include/linux/tlist.h diff --git a/include/linux/tlist.h b/include/linux/tlist.h new file mode 100644 index 000000000000..ad68de9d74fa --- /dev/null +++ b/include/linux/tlist.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_TLIST_H +#define _LINUX_TLIST_H + +#include +#include +#include +#include + +#define TLIST(T, member) \ +struct { \ + struct list_head head; \ + char _member_offset[0][offsetof(T, member) + \ + BUILD_BUG_ON_ZERO(!__same_type(struct list_head, \ + typeof_member(T, member)))]; \ + T _type[0]; \ +} + +#define TLIST_DEFINE(T, member, name) \ + TLIST(T, member) name =3D { LIST_HEAD_INIT((name).head) } + +#define tlist_value_type(list) \ + typeof(*(list)->_type) + +#define tlist_member_offset(list) \ + sizeof(*(list)->_member_offset) + +#define tlist_head(list) \ + (&(list)->head) + +#define tlist_init(list) \ + INIT_LIST_HEAD(tlist_head(list)) + +#define tlist_is_empty(list) \ + list_empty(tlist_head(list)) + +#define __tlist_ptroff(base, offset, T) \ + ((T *) (((uintptr_t)(base)) + (offset))) + +#define tlist_item_to_node(list, item) \ + (__tlist_ptroff((item), tlist_member_offset(list), struct list_head) + \ + BUILD_BUG_ON_ZERO(!__same_type(*(item), tlist_value_type(list)))) + +#define tlist_node_to_item(list, node) \ + __tlist_ptroff((node), -tlist_member_offset(list), tlist_value_type(list)) + +#define tlist_begin(list) \ + tlist_node_to_item((list), tlist_head(list)->next) + +#define tlist_end(list) \ + tlist_node_to_item((list), tlist_head(list)) + +#define tlist_remove(list, item) \ + list_del(tlist_item_to_node((list), (item))) + +#define tlist_push_back(list, item) \ + list_add_tail(tlist_head(list), tlist_item_to_node((list), (item))) + +#define tlist_item_next(list, item) \ + tlist_node_to_item((list), tlist_item_to_node((list), (item))->next) + +#define tlist_for_each(list, iter) \ + for (tlist_value_type(list) *iter =3D tlist_begin(list); \ + iter !=3D tlist_end(list); \ + iter =3D tlist_item_next(list, iter)) + +#define __tlist_for_each_safe(list, iter, next_iter) \ + for (tlist_value_type(list) *iter =3D tlist_begin(list), *next_iter; \ + iter !=3D tlist_end(list) && (next_iter =3D tlist_item_next(list, it= er), 1); \ + iter =3D next_iter) + +#define tlist_for_each_safe(list, iter) \ + __tlist_for_each_safe((list), iter, __UNIQUE_ID(tlist_next_)) + +#endif /* _LINUX_TLIST_H */ -- 2.35.1 From nobody Tue Jun 23 04:04:30 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 77CC9C43219 for ; Fri, 11 Mar 2022 01:33:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345401AbiCKBeZ (ORCPT ); Thu, 10 Mar 2022 20:34:25 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50990 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345396AbiCKBeX (ORCPT ); Thu, 10 Mar 2022 20:34:23 -0500 Received: from mail-41103.protonmail.ch (mail-41103.protonmail.ch [185.70.41.103]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1178615472B for ; Thu, 10 Mar 2022 17:33:20 -0800 (PST) Received: from mail-0301.mail-europe.com (mail-0301.mail-europe.com [188.165.51.139]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by mail-41103.protonmail.ch (Postfix) with ESMTPS id 4KF7jp2vjkz4xLGK for ; Fri, 11 Mar 2022 01:33:18 +0000 (UTC) Authentication-Results: mail-41103.protonmail.ch; dkim=pass (2048-bit key) header.d=protonmail.com header.i=@protonmail.com header.b="VjPOX00Z" Date: Fri, 11 Mar 2022 01:33:11 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1646962393; bh=pC0Stx9zAakq2s3xSgEIytNW/jYoF22vG7SHJdIxGsY=; h=Date:To:From:Cc:Reply-To:Subject:Message-ID:In-Reply-To: References:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID; b=VjPOX00ZDYe8ltsBmTBIX1vKQ+TO/UcgxZABeC9Xe4H9eH6IN3Vr/EUxCzkW2I4qo KzFQLpvgncSB51fT4QmdowF4PrtyQWAp603jgDIsjZFB9xEfQ2iJ0O2ZhzVIz2gSC1 5gdcIQINEL+vphU1EAyMu87EJsmweMRu2x1QAlzjIO/HNF1PHtk5e2wYgyPMe2qNru NungNWJdytg2ERYOEho0DwQFxyJiDqgo6Yblox/eo8953LZ/ls+2358htX9CqhEKiJ 4tbWcENKTWqJ79zUrJFv+7o4zO52LQgk6F+GIecdTxgqyltO6+f0nVMGSIdKffuVWd cAojbBg5o2goA== To: Linus Torvalds , linux-kernel@vger.kernel.org From: =?utf-8?Q?Barnab=C3=A1s_P=C5=91cze?= Cc: Greg Kroah-Hartman , Andrew Morton , Xiaomeng Tong , Kees Cook , Jakob Koschel , Arnd Bergmann Reply-To: =?utf-8?Q?Barnab=C3=A1s_P=C5=91cze?= Subject: [RFC PATCH v1 2/2] platform/x86: wmi: use tlist for WMI blocks Message-ID: <20220311013238.3387227-3-pobrn@protonmail.com> In-Reply-To: <20220311013238.3387227-1-pobrn@protonmail.com> References: <20220311013238.3387227-1-pobrn@protonmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" tlist is a type-safer wrapper around the existing `list_head` facilities. Use that to make the code type-safer and shorter. Signed-off-by: Barnab=C3=A1s P=C5=91cze --- drivers/platform/x86/wmi.c | 53 ++++++++++++++------------------------ 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 58a23a9adbef..4da968bda3dc 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -39,8 +39,6 @@ MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); MODULE_LICENSE("GPL"); -static LIST_HEAD(wmi_block_list); - struct guid_block { guid_t guid; union { @@ -75,6 +73,7 @@ struct wmi_block { unsigned long flags; }; +static TLIST_DEFINE(struct wmi_block, list, wmi_block_list); /* * If the GUID data block is marked as expensive, we must enable and @@ -121,7 +120,6 @@ static struct platform_driver acpi_wmi_driver =3D { static acpi_status find_guid(const char *guid_string, struct wmi_block **o= ut) { guid_t guid_input; - struct wmi_block *wblock; if (!guid_string) return AE_BAD_PARAMETER; @@ -129,7 +127,7 @@ static acpi_status find_guid(const char *guid_string, s= truct wmi_block **out) if (guid_parse(guid_string, &guid_input)) return AE_BAD_PARAMETER; - list_for_each_entry(wblock, &wmi_block_list, list) { + tlist_for_each(&wmi_block_list, wblock) { if (guid_equal(&wblock->gblock.guid, &guid_input)) { if (out) *out =3D wblock; @@ -565,7 +563,6 @@ acpi_status wmi_install_notify_handler(const char *guid, wmi_notify_handler handler, void *data) { - struct wmi_block *block; acpi_status status =3D AE_NOT_EXIST; guid_t guid_input; @@ -575,7 +572,7 @@ acpi_status wmi_install_notify_handler(const char *guid, if (guid_parse(guid, &guid_input)) return AE_BAD_PARAMETER; - list_for_each_entry(block, &wmi_block_list, list) { + tlist_for_each(&wmi_block_list, block) { acpi_status wmi_status; if (guid_equal(&block->gblock.guid, &guid_input)) { @@ -605,7 +602,6 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); */ acpi_status wmi_remove_notify_handler(const char *guid) { - struct wmi_block *block; acpi_status status =3D AE_NOT_EXIST; guid_t guid_input; @@ -615,7 +611,7 @@ acpi_status wmi_remove_notify_handler(const char *guid) if (guid_parse(guid, &guid_input)) return AE_BAD_PARAMETER; - list_for_each_entry(block, &wmi_block_list, list) { + tlist_for_each(&wmi_block_list, block) { acpi_status wmi_status; if (guid_equal(&block->gblock.guid, &guid_input)) { @@ -652,9 +648,7 @@ EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); */ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) { - struct wmi_block *wblock; - - list_for_each_entry(wblock, &wmi_block_list, list) { + tlist_for_each(&wmi_block_list, wblock) { struct guid_block *gblock =3D &wblock->gblock; if ((gblock->flags & ACPI_WMI_EVENT) && gblock->notify_id =3D=3D event) @@ -854,10 +848,8 @@ static int wmi_dev_match(struct device *dev, struct de= vice_driver *driver) static int wmi_char_open(struct inode *inode, struct file *filp) { const char *driver_name =3D filp->f_path.dentry->d_iname; - struct wmi_block *wblock; - struct wmi_block *next; - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { + tlist_for_each(&wmi_block_list, wblock) { if (!wblock->dev.dev.driver) continue; if (strcmp(driver_name, wblock->dev.dev.driver->name) =3D=3D 0) { @@ -1143,12 +1135,10 @@ static int wmi_create_device(struct device *wmi_bus= _dev, static void wmi_free_devices(struct acpi_device *device) { - struct wmi_block *wblock, *next; - /* Delete devices for all the GUIDs */ - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { + tlist_for_each_safe(&wmi_block_list, wblock) { if (wblock->acpi_device =3D=3D device) { - list_del(&wblock->list); + tlist_remove(&wmi_block_list, wblock); device_unregister(&wblock->dev.dev); } } @@ -1156,9 +1146,7 @@ static void wmi_free_devices(struct acpi_device *devi= ce) static bool guid_already_parsed(struct acpi_device *device, const guid_t *= guid) { - struct wmi_block *wblock; - - list_for_each_entry(wblock, &wmi_block_list, list) { + tlist_for_each(&wmi_block_list, wblock) { if (guid_equal(&wblock->gblock.guid, guid)) { /* * Because we historically didn't track the relationship @@ -1182,7 +1170,7 @@ static int parse_wdg(struct device *wmi_bus_dev, stru= ct acpi_device *device) { struct acpi_buffer out =3D {ACPI_ALLOCATE_BUFFER, NULL}; const struct guid_block *gblock; - struct wmi_block *wblock, *next; + struct wmi_block *wblock; union acpi_object *obj; acpi_status status; int retval =3D 0; @@ -1232,7 +1220,7 @@ static int parse_wdg(struct device *wmi_bus_dev, stru= ct acpi_device *device) continue; } - list_add_tail(&wblock->list, &wmi_block_list); + tlist_push_back(&wmi_block_list, wblock); if (debug_event) { wblock->handler =3D wmi_notify_debug; @@ -1244,7 +1232,7 @@ static int parse_wdg(struct device *wmi_bus_dev, stru= ct acpi_device *device) * Now that all of the devices are created, add them to the * device tree and probe subdrivers. */ - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { + tlist_for_each_safe(&wmi_block_list, wblock) { if (wblock->acpi_device !=3D device) continue; @@ -1254,7 +1242,7 @@ static int parse_wdg(struct device *wmi_bus_dev, stru= ct acpi_device *device) &wblock->gblock.guid); if (debug_event) wmi_method_enable(wblock, false); - list_del(&wblock->list); + tlist_remove(&wmi_block_list, wblock); put_device(&wblock->dev.dev); } } @@ -1308,21 +1296,20 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physic= al_address address, static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, void *context) { - struct wmi_block *wblock; - bool found_it =3D false; + struct wmi_block *wblock =3D NULL; - list_for_each_entry(wblock, &wmi_block_list, list) { - struct guid_block *block =3D &wblock->gblock; + tlist_for_each(&wmi_block_list, b) { + struct guid_block *block =3D &b->gblock; - if (wblock->acpi_device->handle =3D=3D handle && + if (b->acpi_device->handle =3D=3D handle && (block->flags & ACPI_WMI_EVENT) && (block->notify_id =3D=3D event)) { - found_it =3D true; + wblock =3D b; break; } } - if (!found_it) + if (!wblock) return; /* If a driver is bound, then notify the driver. */ -- 2.35.1