Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

devres: don't require ARCH_DMA_MINALIGN for devres actions

Currently, devres actions are allocated with devres_alloc(), which
allocates a struct devres with a flexible array member for the actual
data of the resource. The flexible array member is aligned to
ARCH_DMA_MINALIGN, which is wasteful for devres actions that only need
to store a struct action_devres.

Introduce struct devres_action to handle devres actions separately from
struct devres, analogous to what we do for struct devres_group.

Speaking of which, without this patch struct devres_group is treated as
struct devres in release_nodes(). While this is not an actual bug, as
release callbacks for devres nodes in struct devres_group are empty
functions anyways, it is a bit messy and can be confusing.

(Note that besides devres actions, the Rust devres code will also make
use of this. The Rust compiler can figure out the correct alignment of T
in Devres<T> itself, i.e. no need to force a minimum alignment.)

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://patch.msgid.link/20260202235210.55176-5-dakr@kernel.org
[ Add missing node->release check in devres_for_each_res() and
find_dr(); use kzalloc_obj(). - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

+105 -45
+105 -45
drivers/base/devres.c
··· 16 16 #include "base.h" 17 17 #include "trace.h" 18 18 19 + struct devres_node; 20 + typedef void (*dr_node_release_t)(struct device *dev, struct devres_node *node); 21 + 19 22 struct devres_node { 20 23 struct list_head entry; 21 - dr_release_t release; 24 + dr_node_release_t release; 22 25 const char *name; 23 26 size_t size; 24 27 }; 25 28 26 29 struct devres { 27 30 struct devres_node node; 31 + dr_release_t release; 28 32 /* 29 33 * Some archs want to perform DMA into kmalloc caches 30 34 * and need a guaranteed alignment larger than ··· 46 42 /* -- 8 pointers */ 47 43 }; 48 44 49 - static void devres_node_init(struct devres_node *node, dr_release_t release) 45 + static void devres_node_init(struct devres_node *node, dr_node_release_t release) 50 46 { 51 47 INIT_LIST_HEAD(&node->entry); 52 48 node->release = release; ··· 85 81 * Release functions for devres group. These callbacks are used only 86 82 * for identification. 87 83 */ 88 - static void group_open_release(struct device *dev, void *res) 84 + static void group_open_release(struct device *dev, struct devres_node *node) 89 85 { 90 86 /* noop */ 91 87 } 92 88 93 - static void group_close_release(struct device *dev, void *res) 89 + static void group_close_release(struct device *dev, struct devres_node *node) 94 90 { 95 91 /* noop */ 96 92 } ··· 117 113 return true; 118 114 } 119 115 116 + static void dr_node_release(struct device *dev, struct devres_node *node) 117 + { 118 + struct devres *dr = container_of(node, struct devres, node); 119 + 120 + dr->release(dev, dr->data); 121 + } 122 + 120 123 static __always_inline struct devres *alloc_dr(dr_release_t release, 121 124 size_t size, gfp_t gfp, int nid) 122 125 { ··· 141 130 if (!(gfp & __GFP_ZERO)) 142 131 memset(dr, 0, offsetof(struct devres, data)); 143 132 144 - devres_node_init(&dr->node, release); 133 + devres_node_init(&dr->node, dr_node_release); 134 + dr->release = release; 145 135 return dr; 146 136 } 147 137 ··· 221 209 &dev->devres_head, entry) { 222 210 struct devres *dr = container_of(node, struct devres, node); 223 211 224 - if (node->release != release) 212 + if (node->release != dr_node_release) 213 + continue; 214 + if (dr->release != release) 225 215 continue; 226 216 if (match && !match(dev, dr->data, match_data)) 227 217 continue; ··· 282 268 list_for_each_entry_reverse(node, &dev->devres_head, entry) { 283 269 struct devres *dr = container_of(node, struct devres, node); 284 270 285 - if (node->release != release) 271 + if (node->release != dr_node_release) 272 + continue; 273 + if (dr->release != release) 286 274 continue; 287 275 if (match && !match(dev, dr->data, match_data)) 288 276 continue; ··· 346 330 unsigned long flags; 347 331 348 332 spin_lock_irqsave(&dev->devres_lock, flags); 349 - dr = find_dr(dev, new_dr->node.release, match, match_data); 333 + dr = find_dr(dev, new_dr->release, match, match_data); 350 334 if (!dr) { 351 335 add_dr(dev, &new_dr->node); 352 336 dr = new_dr; ··· 520 504 521 505 static void release_nodes(struct device *dev, struct list_head *todo) 522 506 { 523 - struct devres *dr, *tmp; 507 + struct devres_node *node, *tmp; 524 508 525 - /* Release. Note that both devres and devres_group are 526 - * handled as devres in the following loop. This is safe. 509 + /* Release. Note that devres, devres_action and devres_group are 510 + * handled as devres_node in the following loop. This is safe. 527 511 */ 528 - list_for_each_entry_safe_reverse(dr, tmp, todo, node.entry) { 529 - devres_log(dev, &dr->node, "REL"); 530 - dr->node.release(dev, dr->data); 531 - kfree(dr); 512 + list_for_each_entry_safe_reverse(node, tmp, todo, entry) { 513 + devres_log(dev, node, "REL"); 514 + node->release(dev, node); 515 + kfree(node); 532 516 } 533 517 } 534 518 ··· 736 720 void (*action)(void *); 737 721 }; 738 722 739 - static int devm_action_match(struct device *dev, void *res, void *p) 740 - { 741 - struct action_devres *devres = res; 742 - struct action_devres *target = p; 723 + struct devres_action { 724 + struct devres_node node; 725 + struct action_devres action; 726 + }; 743 727 744 - return devres->action == target->action && 745 - devres->data == target->data; 728 + static int devm_action_match(struct devres_action *devres, struct action_devres *target) 729 + { 730 + return devres->action.action == target->action && 731 + devres->action.data == target->data; 746 732 } 747 733 748 - static void devm_action_release(struct device *dev, void *res) 734 + static void devm_action_release(struct device *dev, struct devres_node *node) 749 735 { 750 - struct action_devres *devres = res; 736 + struct devres_action *devres = container_of(node, struct devres_action, node); 751 737 752 - devres->action(devres->data); 738 + devres->action.action(devres->action.data); 753 739 } 754 740 755 741 /** ··· 766 748 */ 767 749 int __devm_add_action(struct device *dev, void (*action)(void *), void *data, const char *name) 768 750 { 769 - struct action_devres *devres; 751 + struct devres_action *devres; 770 752 771 - devres = __devres_alloc_node(devm_action_release, sizeof(struct action_devres), 772 - GFP_KERNEL, NUMA_NO_NODE, name); 753 + devres = kzalloc_obj(*devres); 773 754 if (!devres) 774 755 return -ENOMEM; 775 756 776 - devres->data = data; 777 - devres->action = action; 757 + devres_node_init(&devres->node, devm_action_release); 758 + set_node_dbginfo(&devres->node, name, sizeof(*devres)); 778 759 779 - devres_add(dev, devres); 760 + devres->action.data = data; 761 + devres->action.action = action; 762 + 763 + devres_node_add(dev, &devres->node); 780 764 return 0; 781 765 } 782 766 EXPORT_SYMBOL_GPL(__devm_add_action); 783 767 784 - bool devm_is_action_added(struct device *dev, void (*action)(void *), void *data) 768 + static struct devres_action *devres_action_find(struct device *dev, 769 + void (*action)(void *), 770 + void *data) 785 771 { 786 - struct action_devres devres = { 772 + struct devres_node *node; 773 + struct action_devres target = { 787 774 .data = data, 788 775 .action = action, 789 776 }; 790 777 791 - return devres_find(dev, devm_action_release, devm_action_match, &devres); 778 + list_for_each_entry_reverse(node, &dev->devres_head, entry) { 779 + struct devres_action *dr = container_of(node, struct devres_action, node); 780 + 781 + if (node->release != devm_action_release) 782 + continue; 783 + if (devm_action_match(dr, &target)) 784 + return dr; 785 + } 786 + 787 + return NULL; 788 + } 789 + 790 + bool devm_is_action_added(struct device *dev, void (*action)(void *), void *data) 791 + { 792 + guard(spinlock_irqsave)(&dev->devres_lock); 793 + 794 + return !!devres_action_find(dev, action, data); 792 795 } 793 796 EXPORT_SYMBOL_GPL(devm_is_action_added); 797 + 798 + static struct devres_action *remove_action(struct device *dev, 799 + void (*action)(void *), 800 + void *data) 801 + { 802 + struct devres_action *dr; 803 + 804 + guard(spinlock_irqsave)(&dev->devres_lock); 805 + 806 + dr = devres_action_find(dev, action, data); 807 + if (!dr) 808 + return ERR_PTR(-ENOENT); 809 + 810 + list_del_init(&dr->node.entry); 811 + devres_log(dev, &dr->node, "REM"); 812 + 813 + return dr; 814 + } 794 815 795 816 /** 796 817 * devm_remove_action_nowarn() - removes previously added custom action ··· 855 798 void (*action)(void *), 856 799 void *data) 857 800 { 858 - struct action_devres devres = { 859 - .data = data, 860 - .action = action, 861 - }; 801 + struct devres_action *dr; 862 802 863 - return devres_destroy(dev, devm_action_release, devm_action_match, 864 - &devres); 803 + dr = remove_action(dev, action, data); 804 + if (IS_ERR(dr)) 805 + return PTR_ERR(dr); 806 + 807 + kfree(dr); 808 + 809 + return 0; 865 810 } 866 811 EXPORT_SYMBOL_GPL(devm_remove_action_nowarn); 867 812 ··· 879 820 */ 880 821 void devm_release_action(struct device *dev, void (*action)(void *), void *data) 881 822 { 882 - struct action_devres devres = { 883 - .data = data, 884 - .action = action, 885 - }; 823 + struct devres_action *dr; 886 824 887 - WARN_ON(devres_release(dev, devm_action_release, devm_action_match, 888 - &devres)); 825 + dr = remove_action(dev, action, data); 826 + if (WARN_ON(IS_ERR(dr))) 827 + return; 889 828 829 + dr->action.action(dr->action.data); 830 + 831 + kfree(dr); 890 832 } 891 833 EXPORT_SYMBOL_GPL(devm_release_action); 892 834