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: add free_node callback to struct devres_node

Currently, there are three "subclasses" of struct devres_node, which are
struct devres, struct devres_group, struct devres_action.

release_nodes(), which only knows about the base struct devres_node,
assumes that for all "subclasses" struct devres_node is the first member
in the structure and calls kfree() on struct devres_node.

While this technically works, we can still improve semantical
correctness and type safety with a corresponding free_node() callback.

Additionally, we will need this callback soon in the Rust Devres code,
to allocate and free the required memory on the Rust side.

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://patch.msgid.link/20260202235210.55176-6-dakr@kernel.org
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

+45 -12
+45 -12
drivers/base/devres.c
··· 18 18 19 19 struct devres_node; 20 20 typedef void (*dr_node_release_t)(struct device *dev, struct devres_node *node); 21 + typedef void (*dr_node_free_t)(struct devres_node *node); 21 22 22 23 struct devres_node { 23 24 struct list_head entry; 24 25 dr_node_release_t release; 26 + dr_node_free_t free_node; 25 27 const char *name; 26 28 size_t size; 27 29 }; ··· 48 46 /* -- 8 pointers */ 49 47 }; 50 48 51 - static void devres_node_init(struct devres_node *node, dr_node_release_t release) 49 + static void devres_node_init(struct devres_node *node, 50 + dr_node_release_t release, 51 + dr_node_free_t free_node) 52 52 { 53 53 INIT_LIST_HEAD(&node->entry); 54 54 node->release = release; 55 + node->free_node = free_node; 56 + } 57 + 58 + static inline void free_node(struct devres_node *node) 59 + { 60 + node->free_node(node); 55 61 } 56 62 57 63 static void set_node_dbginfo(struct devres_node *node, const char *name, ··· 134 124 dr->release(dev, dr->data); 135 125 } 136 126 127 + static void dr_node_free(struct devres_node *node) 128 + { 129 + struct devres *dr = container_of(node, struct devres, node); 130 + 131 + kfree(dr); 132 + } 133 + 137 134 static __always_inline struct devres *alloc_dr(dr_release_t release, 138 135 size_t size, gfp_t gfp, int nid) 139 136 { ··· 158 141 if (!(gfp & __GFP_ZERO)) 159 142 memset(dr, 0, offsetof(struct devres, data)); 160 143 161 - devres_node_init(&dr->node, dr_node_release); 144 + devres_node_init(&dr->node, dr_node_release, dr_node_free); 162 145 dr->release = release; 163 146 return dr; 164 147 } ··· 250 233 } 251 234 EXPORT_SYMBOL_GPL(devres_for_each_res); 252 235 236 + static inline void free_dr(struct devres *dr) 237 + { 238 + free_node(&dr->node); 239 + } 240 + 253 241 /** 254 242 * devres_free - Free device resource data 255 243 * @res: Pointer to devres data to free ··· 267 245 struct devres *dr = container_of(res, struct devres, data); 268 246 269 247 BUG_ON(!list_empty(&dr->node.entry)); 270 - kfree(dr); 248 + free_dr(dr); 271 249 } 272 250 } 273 251 EXPORT_SYMBOL_GPL(devres_free); ··· 544 522 { 545 523 struct devres_node *node, *tmp; 546 524 547 - /* Release. Note that devres, devres_action and devres_group are 548 - * handled as devres_node in the following loop. This is safe. 549 - */ 550 525 list_for_each_entry_safe_reverse(node, tmp, todo, entry) { 551 526 devres_log(dev, node, "REL"); 552 527 node->release(dev, node); 553 - kfree(node); 528 + free_node(node); 554 529 } 555 530 } 556 531 ··· 580 561 return cnt; 581 562 } 582 563 564 + static void devres_group_free(struct devres_node *node) 565 + { 566 + struct devres_group *grp = container_of(node, struct devres_group, node[0]); 567 + 568 + kfree(grp); 569 + } 570 + 583 571 /** 584 572 * devres_open_group - Open a new devres group 585 573 * @dev: Device to open devres group for ··· 608 582 if (unlikely(!grp)) 609 583 return NULL; 610 584 611 - devres_node_init(&grp->node[0], &group_open_release); 612 - devres_node_init(&grp->node[1], &group_close_release); 585 + devres_node_init(&grp->node[0], &group_open_release, devres_group_free); 586 + devres_node_init(&grp->node[1], &group_close_release, NULL); 613 587 set_node_dbginfo(&grp->node[0], "grp<", 0); 614 588 set_node_dbginfo(&grp->node[1], "grp>", 0); 615 589 grp->id = grp; ··· 780 754 devres->action.action(devres->action.data); 781 755 } 782 756 757 + static void devm_action_free(struct devres_node *node) 758 + { 759 + struct devres_action *action = container_of(node, struct devres_action, node); 760 + 761 + kfree(action); 762 + } 763 + 783 764 /** 784 765 * __devm_add_action() - add a custom action to list of managed resources 785 766 * @dev: Device that owns the action ··· 805 772 if (!devres) 806 773 return -ENOMEM; 807 774 808 - devres_node_init(&devres->node, devm_action_release); 775 + devres_node_init(&devres->node, devm_action_release, devm_action_free); 809 776 set_node_dbginfo(&devres->node, name, sizeof(*devres)); 810 777 811 778 devres->action.data = data; ··· 1048 1015 old_dr = find_dr(dev, devm_kmalloc_release, devm_kmalloc_match, ptr); 1049 1016 if (!old_dr) { 1050 1017 spin_unlock_irqrestore(&dev->devres_lock, flags); 1051 - kfree(new_dr); 1018 + free_dr(new_dr); 1052 1019 WARN(1, "Memory chunk not managed or managed by a different device."); 1053 1020 return NULL; 1054 1021 } ··· 1068 1035 * list. This is also the reason why we must not use devm_kfree() - the 1069 1036 * links are no longer valid. 1070 1037 */ 1071 - kfree(old_dr); 1038 + free_dr(old_dr); 1072 1039 1073 1040 return new_dr->data; 1074 1041 }