OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [base/] [devres.c] - Blame information for rev 78

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * drivers/base/devres.c - device resource management
3
 *
4
 * Copyright (c) 2006  SUSE Linux Products GmbH
5
 * Copyright (c) 2006  Tejun Heo <teheo@suse.de>
6
 *
7
 * This file is released under the GPLv2.
8
 */
9
 
10
#include <linux/device.h>
11
#include <linux/module.h>
12
 
13
#include "base.h"
14
 
15
struct devres_node {
16
        struct list_head                entry;
17
        dr_release_t                    release;
18
#ifdef CONFIG_DEBUG_DEVRES
19
        const char                      *name;
20
        size_t                          size;
21
#endif
22
};
23
 
24
struct devres {
25
        struct devres_node              node;
26
        /* -- 3 pointers */
27
        unsigned long long              data[]; /* guarantee ull alignment */
28
};
29
 
30
struct devres_group {
31
        struct devres_node              node[2];
32
        void                            *id;
33
        int                             color;
34
        /* -- 8 pointers */
35
};
36
 
37
#ifdef CONFIG_DEBUG_DEVRES
38
static int log_devres = 0;
39
module_param_named(log, log_devres, int, S_IRUGO | S_IWUSR);
40
 
41
static void set_node_dbginfo(struct devres_node *node, const char *name,
42
                             size_t size)
43
{
44
        node->name = name;
45
        node->size = size;
46
}
47
 
48
static void devres_log(struct device *dev, struct devres_node *node,
49
                       const char *op)
50
{
51
        if (unlikely(log_devres))
52
                dev_printk(KERN_ERR, dev, "DEVRES %3s %p %s (%lu bytes)\n",
53
                           op, node, node->name, (unsigned long)node->size);
54
}
55
#else /* CONFIG_DEBUG_DEVRES */
56
#define set_node_dbginfo(node, n, s)    do {} while (0)
57
#define devres_log(dev, node, op)       do {} while (0)
58
#endif /* CONFIG_DEBUG_DEVRES */
59
 
60
/*
61
 * Release functions for devres group.  These callbacks are used only
62
 * for identification.
63
 */
64
static void group_open_release(struct device *dev, void *res)
65
{
66
        /* noop */
67
}
68
 
69
static void group_close_release(struct device *dev, void *res)
70
{
71
        /* noop */
72
}
73
 
74
static struct devres_group * node_to_group(struct devres_node *node)
75
{
76
        if (node->release == &group_open_release)
77
                return container_of(node, struct devres_group, node[0]);
78
        if (node->release == &group_close_release)
79
                return container_of(node, struct devres_group, node[1]);
80
        return NULL;
81
}
82
 
83
static __always_inline struct devres * alloc_dr(dr_release_t release,
84
                                                size_t size, gfp_t gfp)
85
{
86
        size_t tot_size = sizeof(struct devres) + size;
87
        struct devres *dr;
88
 
89
        dr = kmalloc_track_caller(tot_size, gfp);
90
        if (unlikely(!dr))
91
                return NULL;
92
 
93
        memset(dr, 0, tot_size);
94
        INIT_LIST_HEAD(&dr->node.entry);
95
        dr->node.release = release;
96
        return dr;
97
}
98
 
99
static void add_dr(struct device *dev, struct devres_node *node)
100
{
101
        devres_log(dev, node, "ADD");
102
        BUG_ON(!list_empty(&node->entry));
103
        list_add_tail(&node->entry, &dev->devres_head);
104
}
105
 
106
#ifdef CONFIG_DEBUG_DEVRES
107
void * __devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
108
                      const char *name)
109
{
110
        struct devres *dr;
111
 
112
        dr = alloc_dr(release, size, gfp);
113
        if (unlikely(!dr))
114
                return NULL;
115
        set_node_dbginfo(&dr->node, name, size);
116
        return dr->data;
117
}
118
EXPORT_SYMBOL_GPL(__devres_alloc);
119
#else
120
/**
121
 * devres_alloc - Allocate device resource data
122
 * @release: Release function devres will be associated with
123
 * @size: Allocation size
124
 * @gfp: Allocation flags
125
 *
126
 * Allocate devres of @size bytes.  The allocated area is zeroed, then
127
 * associated with @release.  The returned pointer can be passed to
128
 * other devres_*() functions.
129
 *
130
 * RETURNS:
131
 * Pointer to allocated devres on success, NULL on failure.
132
 */
133
void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
134
{
135
        struct devres *dr;
136
 
137
        dr = alloc_dr(release, size, gfp);
138
        if (unlikely(!dr))
139
                return NULL;
140
        return dr->data;
141
}
142
EXPORT_SYMBOL_GPL(devres_alloc);
143
#endif
144
 
145
/**
146
 * devres_free - Free device resource data
147
 * @res: Pointer to devres data to free
148
 *
149
 * Free devres created with devres_alloc().
150
 */
151
void devres_free(void *res)
152
{
153
        if (res) {
154
                struct devres *dr = container_of(res, struct devres, data);
155
 
156
                BUG_ON(!list_empty(&dr->node.entry));
157
                kfree(dr);
158
        }
159
}
160
EXPORT_SYMBOL_GPL(devres_free);
161
 
162
/**
163
 * devres_add - Register device resource
164
 * @dev: Device to add resource to
165
 * @res: Resource to register
166
 *
167
 * Register devres @res to @dev.  @res should have been allocated
168
 * using devres_alloc().  On driver detach, the associated release
169
 * function will be invoked and devres will be freed automatically.
170
 */
171
void devres_add(struct device *dev, void *res)
172
{
173
        struct devres *dr = container_of(res, struct devres, data);
174
        unsigned long flags;
175
 
176
        spin_lock_irqsave(&dev->devres_lock, flags);
177
        add_dr(dev, &dr->node);
178
        spin_unlock_irqrestore(&dev->devres_lock, flags);
179
}
180
EXPORT_SYMBOL_GPL(devres_add);
181
 
182
static struct devres *find_dr(struct device *dev, dr_release_t release,
183
                              dr_match_t match, void *match_data)
184
{
185
        struct devres_node *node;
186
 
187
        list_for_each_entry_reverse(node, &dev->devres_head, entry) {
188
                struct devres *dr = container_of(node, struct devres, node);
189
 
190
                if (node->release != release)
191
                        continue;
192
                if (match && !match(dev, dr->data, match_data))
193
                        continue;
194
                return dr;
195
        }
196
 
197
        return NULL;
198
}
199
 
200
/**
201
 * devres_find - Find device resource
202
 * @dev: Device to lookup resource from
203
 * @release: Look for resources associated with this release function
204
 * @match: Match function (optional)
205
 * @match_data: Data for the match function
206
 *
207
 * Find the latest devres of @dev which is associated with @release
208
 * and for which @match returns 1.  If @match is NULL, it's considered
209
 * to match all.
210
 *
211
 * RETURNS:
212
 * Pointer to found devres, NULL if not found.
213
 */
214
void * devres_find(struct device *dev, dr_release_t release,
215
                   dr_match_t match, void *match_data)
216
{
217
        struct devres *dr;
218
        unsigned long flags;
219
 
220
        spin_lock_irqsave(&dev->devres_lock, flags);
221
        dr = find_dr(dev, release, match, match_data);
222
        spin_unlock_irqrestore(&dev->devres_lock, flags);
223
 
224
        if (dr)
225
                return dr->data;
226
        return NULL;
227
}
228
EXPORT_SYMBOL_GPL(devres_find);
229
 
230
/**
231
 * devres_get - Find devres, if non-existent, add one atomically
232
 * @dev: Device to lookup or add devres for
233
 * @new_res: Pointer to new initialized devres to add if not found
234
 * @match: Match function (optional)
235
 * @match_data: Data for the match function
236
 *
237
 * Find the latest devres of @dev which has the same release function
238
 * as @new_res and for which @match return 1.  If found, @new_res is
239
 * freed; otherwise, @new_res is added atomically.
240
 *
241
 * RETURNS:
242
 * Pointer to found or added devres.
243
 */
244
void * devres_get(struct device *dev, void *new_res,
245
                  dr_match_t match, void *match_data)
246
{
247
        struct devres *new_dr = container_of(new_res, struct devres, data);
248
        struct devres *dr;
249
        unsigned long flags;
250
 
251
        spin_lock_irqsave(&dev->devres_lock, flags);
252
        dr = find_dr(dev, new_dr->node.release, match, match_data);
253
        if (!dr) {
254
                add_dr(dev, &new_dr->node);
255
                dr = new_dr;
256
                new_dr = NULL;
257
        }
258
        spin_unlock_irqrestore(&dev->devres_lock, flags);
259
        devres_free(new_dr);
260
 
261
        return dr->data;
262
}
263
EXPORT_SYMBOL_GPL(devres_get);
264
 
265
/**
266
 * devres_remove - Find a device resource and remove it
267
 * @dev: Device to find resource from
268
 * @release: Look for resources associated with this release function
269
 * @match: Match function (optional)
270
 * @match_data: Data for the match function
271
 *
272
 * Find the latest devres of @dev associated with @release and for
273
 * which @match returns 1.  If @match is NULL, it's considered to
274
 * match all.  If found, the resource is removed atomically and
275
 * returned.
276
 *
277
 * RETURNS:
278
 * Pointer to removed devres on success, NULL if not found.
279
 */
280
void * devres_remove(struct device *dev, dr_release_t release,
281
                     dr_match_t match, void *match_data)
282
{
283
        struct devres *dr;
284
        unsigned long flags;
285
 
286
        spin_lock_irqsave(&dev->devres_lock, flags);
287
        dr = find_dr(dev, release, match, match_data);
288
        if (dr) {
289
                list_del_init(&dr->node.entry);
290
                devres_log(dev, &dr->node, "REM");
291
        }
292
        spin_unlock_irqrestore(&dev->devres_lock, flags);
293
 
294
        if (dr)
295
                return dr->data;
296
        return NULL;
297
}
298
EXPORT_SYMBOL_GPL(devres_remove);
299
 
300
/**
301
 * devres_destroy - Find a device resource and destroy it
302
 * @dev: Device to find resource from
303
 * @release: Look for resources associated with this release function
304
 * @match: Match function (optional)
305
 * @match_data: Data for the match function
306
 *
307
 * Find the latest devres of @dev associated with @release and for
308
 * which @match returns 1.  If @match is NULL, it's considered to
309
 * match all.  If found, the resource is removed atomically and freed.
310
 *
311
 * RETURNS:
312
 * 0 if devres is found and freed, -ENOENT if not found.
313
 */
314
int devres_destroy(struct device *dev, dr_release_t release,
315
                   dr_match_t match, void *match_data)
316
{
317
        void *res;
318
 
319
        res = devres_remove(dev, release, match, match_data);
320
        if (unlikely(!res))
321
                return -ENOENT;
322
 
323
        devres_free(res);
324
        return 0;
325
}
326
EXPORT_SYMBOL_GPL(devres_destroy);
327
 
328
static int remove_nodes(struct device *dev,
329
                        struct list_head *first, struct list_head *end,
330
                        struct list_head *todo)
331
{
332
        int cnt = 0, nr_groups = 0;
333
        struct list_head *cur;
334
 
335
        /* First pass - move normal devres entries to @todo and clear
336
         * devres_group colors.
337
         */
338
        cur = first;
339
        while (cur != end) {
340
                struct devres_node *node;
341
                struct devres_group *grp;
342
 
343
                node = list_entry(cur, struct devres_node, entry);
344
                cur = cur->next;
345
 
346
                grp = node_to_group(node);
347
                if (grp) {
348
                        /* clear color of group markers in the first pass */
349
                        grp->color = 0;
350
                        nr_groups++;
351
                } else {
352
                        /* regular devres entry */
353
                        if (&node->entry == first)
354
                                first = first->next;
355
                        list_move_tail(&node->entry, todo);
356
                        cnt++;
357
                }
358
        }
359
 
360
        if (!nr_groups)
361
                return cnt;
362
 
363
        /* Second pass - Scan groups and color them.  A group gets
364
         * color value of two iff the group is wholly contained in
365
         * [cur, end).  That is, for a closed group, both opening and
366
         * closing markers should be in the range, while just the
367
         * opening marker is enough for an open group.
368
         */
369
        cur = first;
370
        while (cur != end) {
371
                struct devres_node *node;
372
                struct devres_group *grp;
373
 
374
                node = list_entry(cur, struct devres_node, entry);
375
                cur = cur->next;
376
 
377
                grp = node_to_group(node);
378
                BUG_ON(!grp || list_empty(&grp->node[0].entry));
379
 
380
                grp->color++;
381
                if (list_empty(&grp->node[1].entry))
382
                        grp->color++;
383
 
384
                BUG_ON(grp->color <= 0 || grp->color > 2);
385
                if (grp->color == 2) {
386
                        /* No need to update cur or end.  The removed
387
                         * nodes are always before both.
388
                         */
389
                        list_move_tail(&grp->node[0].entry, todo);
390
                        list_del_init(&grp->node[1].entry);
391
                }
392
        }
393
 
394
        return cnt;
395
}
396
 
397
static int release_nodes(struct device *dev, struct list_head *first,
398
                         struct list_head *end, unsigned long flags)
399
{
400
        LIST_HEAD(todo);
401
        int cnt;
402
        struct devres *dr, *tmp;
403
 
404
        cnt = remove_nodes(dev, first, end, &todo);
405
 
406
        spin_unlock_irqrestore(&dev->devres_lock, flags);
407
 
408
        /* Release.  Note that both devres and devres_group are
409
         * handled as devres in the following loop.  This is safe.
410
         */
411
        list_for_each_entry_safe_reverse(dr, tmp, &todo, node.entry) {
412
                devres_log(dev, &dr->node, "REL");
413
                dr->node.release(dev, dr->data);
414
                kfree(dr);
415
        }
416
 
417
        return cnt;
418
}
419
 
420
/**
421
 * devres_release_all - Release all managed resources
422
 * @dev: Device to release resources for
423
 *
424
 * Release all resources associated with @dev.  This function is
425
 * called on driver detach.
426
 */
427
int devres_release_all(struct device *dev)
428
{
429
        unsigned long flags;
430
 
431
        spin_lock_irqsave(&dev->devres_lock, flags);
432
        return release_nodes(dev, dev->devres_head.next, &dev->devres_head,
433
                             flags);
434
}
435
 
436
/**
437
 * devres_open_group - Open a new devres group
438
 * @dev: Device to open devres group for
439
 * @id: Separator ID
440
 * @gfp: Allocation flags
441
 *
442
 * Open a new devres group for @dev with @id.  For @id, using a
443
 * pointer to an object which won't be used for another group is
444
 * recommended.  If @id is NULL, address-wise unique ID is created.
445
 *
446
 * RETURNS:
447
 * ID of the new group, NULL on failure.
448
 */
449
void * devres_open_group(struct device *dev, void *id, gfp_t gfp)
450
{
451
        struct devres_group *grp;
452
        unsigned long flags;
453
 
454
        grp = kmalloc(sizeof(*grp), gfp);
455
        if (unlikely(!grp))
456
                return NULL;
457
 
458
        grp->node[0].release = &group_open_release;
459
        grp->node[1].release = &group_close_release;
460
        INIT_LIST_HEAD(&grp->node[0].entry);
461
        INIT_LIST_HEAD(&grp->node[1].entry);
462
        set_node_dbginfo(&grp->node[0], "grp<", 0);
463
        set_node_dbginfo(&grp->node[1], "grp>", 0);
464
        grp->id = grp;
465
        if (id)
466
                grp->id = id;
467
 
468
        spin_lock_irqsave(&dev->devres_lock, flags);
469
        add_dr(dev, &grp->node[0]);
470
        spin_unlock_irqrestore(&dev->devres_lock, flags);
471
        return grp->id;
472
}
473
EXPORT_SYMBOL_GPL(devres_open_group);
474
 
475
/* Find devres group with ID @id.  If @id is NULL, look for the latest. */
476
static struct devres_group * find_group(struct device *dev, void *id)
477
{
478
        struct devres_node *node;
479
 
480
        list_for_each_entry_reverse(node, &dev->devres_head, entry) {
481
                struct devres_group *grp;
482
 
483
                if (node->release != &group_open_release)
484
                        continue;
485
 
486
                grp = container_of(node, struct devres_group, node[0]);
487
 
488
                if (id) {
489
                        if (grp->id == id)
490
                                return grp;
491
                } else if (list_empty(&grp->node[1].entry))
492
                        return grp;
493
        }
494
 
495
        return NULL;
496
}
497
 
498
/**
499
 * devres_close_group - Close a devres group
500
 * @dev: Device to close devres group for
501
 * @id: ID of target group, can be NULL
502
 *
503
 * Close the group identified by @id.  If @id is NULL, the latest open
504
 * group is selected.
505
 */
506
void devres_close_group(struct device *dev, void *id)
507
{
508
        struct devres_group *grp;
509
        unsigned long flags;
510
 
511
        spin_lock_irqsave(&dev->devres_lock, flags);
512
 
513
        grp = find_group(dev, id);
514
        if (grp)
515
                add_dr(dev, &grp->node[1]);
516
        else
517
                WARN_ON(1);
518
 
519
        spin_unlock_irqrestore(&dev->devres_lock, flags);
520
}
521
EXPORT_SYMBOL_GPL(devres_close_group);
522
 
523
/**
524
 * devres_remove_group - Remove a devres group
525
 * @dev: Device to remove group for
526
 * @id: ID of target group, can be NULL
527
 *
528
 * Remove the group identified by @id.  If @id is NULL, the latest
529
 * open group is selected.  Note that removing a group doesn't affect
530
 * any other resources.
531
 */
532
void devres_remove_group(struct device *dev, void *id)
533
{
534
        struct devres_group *grp;
535
        unsigned long flags;
536
 
537
        spin_lock_irqsave(&dev->devres_lock, flags);
538
 
539
        grp = find_group(dev, id);
540
        if (grp) {
541
                list_del_init(&grp->node[0].entry);
542
                list_del_init(&grp->node[1].entry);
543
                devres_log(dev, &grp->node[0], "REM");
544
        } else
545
                WARN_ON(1);
546
 
547
        spin_unlock_irqrestore(&dev->devres_lock, flags);
548
 
549
        kfree(grp);
550
}
551
EXPORT_SYMBOL_GPL(devres_remove_group);
552
 
553
/**
554
 * devres_release_group - Release resources in a devres group
555
 * @dev: Device to release group for
556
 * @id: ID of target group, can be NULL
557
 *
558
 * Release all resources in the group identified by @id.  If @id is
559
 * NULL, the latest open group is selected.  The selected group and
560
 * groups properly nested inside the selected group are removed.
561
 *
562
 * RETURNS:
563
 * The number of released non-group resources.
564
 */
565
int devres_release_group(struct device *dev, void *id)
566
{
567
        struct devres_group *grp;
568
        unsigned long flags;
569
        int cnt = 0;
570
 
571
        spin_lock_irqsave(&dev->devres_lock, flags);
572
 
573
        grp = find_group(dev, id);
574
        if (grp) {
575
                struct list_head *first = &grp->node[0].entry;
576
                struct list_head *end = &dev->devres_head;
577
 
578
                if (!list_empty(&grp->node[1].entry))
579
                        end = grp->node[1].entry.next;
580
 
581
                cnt = release_nodes(dev, first, end, flags);
582
        } else {
583
                WARN_ON(1);
584
                spin_unlock_irqrestore(&dev->devres_lock, flags);
585
        }
586
 
587
        return cnt;
588
}
589
EXPORT_SYMBOL_GPL(devres_release_group);
590
 
591
/*
592
 * Managed kzalloc/kfree
593
 */
594
static void devm_kzalloc_release(struct device *dev, void *res)
595
{
596
        /* noop */
597
}
598
 
599
static int devm_kzalloc_match(struct device *dev, void *res, void *data)
600
{
601
        return res == data;
602
}
603
 
604
/**
605
 * devm_kzalloc - Resource-managed kzalloc
606
 * @dev: Device to allocate memory for
607
 * @size: Allocation size
608
 * @gfp: Allocation gfp flags
609
 *
610
 * Managed kzalloc.  Memory allocated with this function is
611
 * automatically freed on driver detach.  Like all other devres
612
 * resources, guaranteed alignment is unsigned long long.
613
 *
614
 * RETURNS:
615
 * Pointer to allocated memory on success, NULL on failure.
616
 */
617
void * devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
618
{
619
        struct devres *dr;
620
 
621
        /* use raw alloc_dr for kmalloc caller tracing */
622
        dr = alloc_dr(devm_kzalloc_release, size, gfp);
623
        if (unlikely(!dr))
624
                return NULL;
625
 
626
        set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
627
        devres_add(dev, dr->data);
628
        return dr->data;
629
}
630
EXPORT_SYMBOL_GPL(devm_kzalloc);
631
 
632
/**
633
 * devm_kfree - Resource-managed kfree
634
 * @dev: Device this memory belongs to
635
 * @p: Memory to free
636
 *
637
 * Free memory allocated with dev_kzalloc().
638
 */
639
void devm_kfree(struct device *dev, void *p)
640
{
641
        int rc;
642
 
643
        rc = devres_destroy(dev, devm_kzalloc_release, devm_kzalloc_match, p);
644
        WARN_ON(rc);
645
}
646
EXPORT_SYMBOL_GPL(devm_kfree);

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.