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

component: add support for releasing match data

The component helper treats the void match data pointer as an opaque
object which needs no further management. When device nodes being
passed, this is not true: the caller should pass its refcount to the
component helper, and there should be a way to drop the refcount when
the matching information is destroyed.

This patch provides a per-match release method in addition to the match
method to solve this issue. Rather than using component_match_add(),
users should use component_match_add_release() which takes an additional
function pointer for releasing this reference.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

+89 -44
+68 -37
drivers/base/component.c
··· 20 20 21 21 struct component; 22 22 23 + struct component_match_array { 24 + void *data; 25 + int (*compare)(struct device *, void *); 26 + void (*release)(struct device *, void *); 27 + struct component *component; 28 + bool duplicate; 29 + }; 30 + 23 31 struct component_match { 24 32 size_t alloc; 25 33 size_t num; 26 - struct { 27 - void *data; 28 - int (*fn)(struct device *, void *); 29 - struct component *component; 30 - bool duplicate; 31 - } compare[0]; 34 + struct component_match_array *compare; 32 35 }; 33 36 34 37 struct master { ··· 95 92 * any components which are found to this master. 96 93 */ 97 94 for (i = 0; i < match->num; i++) { 95 + struct component_match_array *mc = &match->compare[i]; 98 96 struct component *c; 99 97 100 98 dev_dbg(master->dev, "Looking for component %zu\n", i); ··· 103 99 if (match->compare[i].component) 104 100 continue; 105 101 106 - c = find_component(master, match->compare[i].fn, 107 - match->compare[i].data); 102 + c = find_component(master, mc->compare, mc->data); 108 103 if (!c) { 109 104 ret = -ENXIO; 110 105 break; ··· 195 192 } 196 193 } 197 194 198 - static size_t component_match_size(size_t num) 195 + static void component_match_release(struct device *master, 196 + struct component_match *match) 199 197 { 200 - return offsetof(struct component_match, compare[num]); 198 + unsigned int i; 199 + 200 + for (i = 0; i < match->num; i++) { 201 + struct component_match_array *mc = &match->compare[i]; 202 + 203 + if (mc->release) 204 + mc->release(master, mc->data); 205 + } 201 206 } 202 207 203 - static struct component_match *component_match_realloc(struct device *dev, 208 + static void devm_component_match_release(struct device *dev, void *res) 209 + { 210 + component_match_release(dev, res); 211 + } 212 + 213 + static int component_match_realloc(struct device *dev, 204 214 struct component_match *match, size_t num) 205 215 { 206 - struct component_match *new; 216 + struct component_match_array *new; 207 217 208 - if (match && match->alloc == num) 209 - return match; 218 + if (match->alloc == num) 219 + return 0; 210 220 211 - new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL); 221 + new = devm_kmalloc_array(dev, num, sizeof(*new), GFP_KERNEL); 212 222 if (!new) 213 - return ERR_PTR(-ENOMEM); 223 + return -ENOMEM; 214 224 215 - if (match) { 216 - memcpy(new, match, component_match_size(min(match->num, num))); 217 - devm_kfree(dev, match); 218 - } else { 219 - new->num = 0; 225 + if (match->compare) { 226 + memcpy(new, match->compare, sizeof(*new) * 227 + min(match->num, num)); 228 + devm_kfree(dev, match->compare); 220 229 } 230 + match->compare = new; 231 + match->alloc = num; 221 232 222 - new->alloc = num; 223 - 224 - return new; 233 + return 0; 225 234 } 226 235 227 236 /* 228 - * Add a component to be matched. 237 + * Add a component to be matched, with a release function. 229 238 * 230 239 * The match array is first created or extended if necessary. 231 240 */ 232 - void component_match_add(struct device *dev, struct component_match **matchptr, 241 + void component_match_add_release(struct device *master, 242 + struct component_match **matchptr, 243 + void (*release)(struct device *, void *), 233 244 int (*compare)(struct device *, void *), void *compare_data) 234 245 { 235 246 struct component_match *match = *matchptr; ··· 251 234 if (IS_ERR(match)) 252 235 return; 253 236 254 - if (!match || match->num == match->alloc) { 255 - size_t new_size = match ? match->alloc + 16 : 15; 237 + if (!match) { 238 + match = devres_alloc(devm_component_match_release, 239 + sizeof(*match), GFP_KERNEL); 240 + if (!match) { 241 + *matchptr = ERR_PTR(-ENOMEM); 242 + return; 243 + } 256 244 257 - match = component_match_realloc(dev, match, new_size); 245 + devres_add(master, match); 258 246 259 247 *matchptr = match; 260 - 261 - if (IS_ERR(match)) 262 - return; 263 248 } 264 249 265 - match->compare[match->num].fn = compare; 250 + if (match->num == match->alloc) { 251 + size_t new_size = match ? match->alloc + 16 : 15; 252 + int ret; 253 + 254 + ret = component_match_realloc(master, match, new_size); 255 + if (ret) { 256 + *matchptr = ERR_PTR(ret); 257 + return; 258 + } 259 + } 260 + 261 + match->compare[match->num].compare = compare; 262 + match->compare[match->num].release = release; 266 263 match->compare[match->num].data = compare_data; 267 264 match->compare[match->num].component = NULL; 268 265 match->num++; 269 266 } 270 - EXPORT_SYMBOL(component_match_add); 267 + EXPORT_SYMBOL(component_match_add_release); 271 268 272 269 int component_master_add_with_match(struct device *dev, 273 270 const struct component_master_ops *ops, ··· 291 260 int ret; 292 261 293 262 /* Reallocate the match array for its true size */ 294 - match = component_match_realloc(dev, match, match->num); 295 - if (IS_ERR(match)) 296 - return PTR_ERR(match); 263 + ret = component_match_realloc(dev, match, match->num); 264 + if (ret) 265 + return ret; 297 266 298 267 master = kzalloc(sizeof(*master), GFP_KERNEL); 299 268 if (!master)
+21 -7
include/linux/component.h
··· 1 1 #ifndef COMPONENT_H 2 2 #define COMPONENT_H 3 3 4 + #include <linux/stddef.h> 5 + 4 6 struct device; 5 7 6 8 struct component_ops { 7 - int (*bind)(struct device *, struct device *, void *); 8 - void (*unbind)(struct device *, struct device *, void *); 9 + int (*bind)(struct device *comp, struct device *master, 10 + void *master_data); 11 + void (*unbind)(struct device *comp, struct device *master, 12 + void *master_data); 9 13 }; 10 14 11 15 int component_add(struct device *, const struct component_ops *); 12 16 void component_del(struct device *, const struct component_ops *); 13 17 14 - int component_bind_all(struct device *, void *); 15 - void component_unbind_all(struct device *, void *); 18 + int component_bind_all(struct device *master, void *master_data); 19 + void component_unbind_all(struct device *master, void *master_data); 16 20 17 21 struct master; 18 22 19 23 struct component_master_ops { 20 - int (*bind)(struct device *); 21 - void (*unbind)(struct device *); 24 + int (*bind)(struct device *master); 25 + void (*unbind)(struct device *master); 22 26 }; 23 27 24 28 void component_master_del(struct device *, ··· 32 28 33 29 int component_master_add_with_match(struct device *, 34 30 const struct component_master_ops *, struct component_match *); 35 - void component_match_add(struct device *, struct component_match **, 31 + void component_match_add_release(struct device *master, 32 + struct component_match **matchptr, 33 + void (*release)(struct device *, void *), 36 34 int (*compare)(struct device *, void *), void *compare_data); 35 + 36 + static inline void component_match_add(struct device *master, 37 + struct component_match **matchptr, 38 + int (*compare)(struct device *, void *), void *compare_data) 39 + { 40 + component_match_add_release(master, matchptr, NULL, compare, 41 + compare_data); 42 + } 37 43 38 44 #endif