aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/component.c
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2015-11-17 07:08:01 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2015-12-06 19:02:05 -0500
commitce657b1cddf1f88c56ae683efa7130341c92808b (patch)
treec21cbca7e01935dc303b6dd7bdfb4f67ecc4ea47 /drivers/base/component.c
parentffc30b74fd6d01588bd3fdebc3b1acc0857e6fc8 (diff)
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>
Diffstat (limited to 'drivers/base/component.c')
-rw-r--r--drivers/base/component.c101
1 files changed, 66 insertions, 35 deletions
diff --git a/drivers/base/component.c b/drivers/base/component.c
index d99b06b341fb..89f5cf68d80a 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -20,15 +20,18 @@
20 20
21struct component; 21struct component;
22 22
23struct 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
23struct component_match { 31struct component_match {
24 size_t alloc; 32 size_t alloc;
25 size_t num; 33 size_t num;
26 struct { 34 struct component_match_array *compare;
27 void *data;
28 int (*fn)(struct device *, void *);
29 struct component *component;
30 bool duplicate;
31 } compare[0];
32}; 35};
33 36
34struct master { 37struct master {
@@ -92,6 +95,7 @@ static int find_components(struct master *master)
92 * any components which are found to this master. 95 * any components which are found to this master.
93 */ 96 */
94 for (i = 0; i < match->num; i++) { 97 for (i = 0; i < match->num; i++) {
98 struct component_match_array *mc = &match->compare[i];
95 struct component *c; 99 struct component *c;
96 100
97 dev_dbg(master->dev, "Looking for component %zu\n", i); 101 dev_dbg(master->dev, "Looking for component %zu\n", i);
@@ -99,8 +103,7 @@ static int find_components(struct master *master)
99 if (match->compare[i].component) 103 if (match->compare[i].component)
100 continue; 104 continue;
101 105
102 c = find_component(master, match->compare[i].fn, 106 c = find_component(master, mc->compare, mc->data);
103 match->compare[i].data);
104 if (!c) { 107 if (!c) {
105 ret = -ENXIO; 108 ret = -ENXIO;
106 break; 109 break;
@@ -192,41 +195,55 @@ static void take_down_master(struct master *master)
192 } 195 }
193} 196}
194 197
195static size_t component_match_size(size_t num) 198static void component_match_release(struct device *master,
199 struct component_match *match)
200{
201 unsigned int i;
202
203 for (i = 0; i < match->num; i++) {
204 struct component_match_array *mc = &match->compare[i];
205
206 if (mc->release)
207 mc->release(master, mc->data);
208 }
209}
210
211static void devm_component_match_release(struct device *dev, void *res)
196{ 212{
197 return offsetof(struct component_match, compare[num]); 213 component_match_release(dev, res);
198} 214}
199 215
200static struct component_match *component_match_realloc(struct device *dev, 216static int component_match_realloc(struct device *dev,
201 struct component_match *match, size_t num) 217 struct component_match *match, size_t num)
202{ 218{
203 struct component_match *new; 219 struct component_match_array *new;
204 220
205 if (match && match->alloc == num) 221 if (match->alloc == num)
206 return match; 222 return 0;
207 223
208 new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL); 224 new = devm_kmalloc_array(dev, num, sizeof(*new), GFP_KERNEL);
209 if (!new) 225 if (!new)
210 return ERR_PTR(-ENOMEM); 226 return -ENOMEM;
211 227
212 if (match) { 228 if (match->compare) {
213 memcpy(new, match, component_match_size(min(match->num, num))); 229 memcpy(new, match->compare, sizeof(*new) *
214 devm_kfree(dev, match); 230 min(match->num, num));
215 } else { 231 devm_kfree(dev, match->compare);
216 new->num = 0;
217 } 232 }
233 match->compare = new;
234 match->alloc = num;
218 235
219 new->alloc = num; 236 return 0;
220
221 return new;
222} 237}
223 238
224/* 239/*
225 * Add a component to be matched. 240 * Add a component to be matched, with a release function.
226 * 241 *
227 * The match array is first created or extended if necessary. 242 * The match array is first created or extended if necessary.
228 */ 243 */
229void component_match_add(struct device *dev, struct component_match **matchptr, 244void component_match_add_release(struct device *master,
245 struct component_match **matchptr,
246 void (*release)(struct device *, void *),
230 int (*compare)(struct device *, void *), void *compare_data) 247 int (*compare)(struct device *, void *), void *compare_data)
231{ 248{
232 struct component_match *match = *matchptr; 249 struct component_match *match = *matchptr;
@@ -234,23 +251,37 @@ void component_match_add(struct device *dev, struct component_match **matchptr,
234 if (IS_ERR(match)) 251 if (IS_ERR(match))
235 return; 252 return;
236 253
237 if (!match || match->num == match->alloc) { 254 if (!match) {
238 size_t new_size = match ? match->alloc + 16 : 15; 255 match = devres_alloc(devm_component_match_release,
256 sizeof(*match), GFP_KERNEL);
257 if (!match) {
258 *matchptr = ERR_PTR(-ENOMEM);
259 return;
260 }
239 261
240 match = component_match_realloc(dev, match, new_size); 262 devres_add(master, match);
241 263
242 *matchptr = match; 264 *matchptr = match;
265 }
266
267 if (match->num == match->alloc) {
268 size_t new_size = match ? match->alloc + 16 : 15;
269 int ret;
243 270
244 if (IS_ERR(match)) 271 ret = component_match_realloc(master, match, new_size);
272 if (ret) {
273 *matchptr = ERR_PTR(ret);
245 return; 274 return;
275 }
246 } 276 }
247 277
248 match->compare[match->num].fn = compare; 278 match->compare[match->num].compare = compare;
279 match->compare[match->num].release = release;
249 match->compare[match->num].data = compare_data; 280 match->compare[match->num].data = compare_data;
250 match->compare[match->num].component = NULL; 281 match->compare[match->num].component = NULL;
251 match->num++; 282 match->num++;
252} 283}
253EXPORT_SYMBOL(component_match_add); 284EXPORT_SYMBOL(component_match_add_release);
254 285
255int component_master_add_with_match(struct device *dev, 286int component_master_add_with_match(struct device *dev,
256 const struct component_master_ops *ops, 287 const struct component_master_ops *ops,
@@ -260,9 +291,9 @@ int component_master_add_with_match(struct device *dev,
260 int ret; 291 int ret;
261 292
262 /* Reallocate the match array for its true size */ 293 /* Reallocate the match array for its true size */
263 match = component_match_realloc(dev, match, match->num); 294 ret = component_match_realloc(dev, match, match->num);
264 if (IS_ERR(match)) 295 if (ret)
265 return PTR_ERR(match); 296 return ret;
266 297
267 master = kzalloc(sizeof(*master), GFP_KERNEL); 298 master = kzalloc(sizeof(*master), GFP_KERNEL);
268 if (!master) 299 if (!master)