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

Subversion Repositories or1k_soc_on_altera_embedded_dev_kit

[/] [or1k_soc_on_altera_embedded_dev_kit/] [trunk/] [linux-2.6/] [linux-2.6.24/] [arch/] [powerpc/] [lib/] [rheap.c] - Blame information for rev 3

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 3 xianfeng
/*
2
 * A Remote Heap.  Remote means that we don't touch the memory that the
3
 * heap points to. Normal heap implementations use the memory they manage
4
 * to place their list. We cannot do that because the memory we manage may
5
 * have special properties, for example it is uncachable or of different
6
 * endianess.
7
 *
8
 * Author: Pantelis Antoniou <panto@intracom.gr>
9
 *
10
 * 2004 (c) INTRACOM S.A. Greece. This file is licensed under
11
 * the terms of the GNU General Public License version 2. This program
12
 * is licensed "as is" without any warranty of any kind, whether express
13
 * or implied.
14
 */
15
#include <linux/types.h>
16
#include <linux/errno.h>
17
#include <linux/kernel.h>
18
#include <linux/module.h>
19
#include <linux/mm.h>
20
#include <linux/err.h>
21
#include <linux/slab.h>
22
 
23
#include <asm/rheap.h>
24
 
25
/*
26
 * Fixup a list_head, needed when copying lists.  If the pointers fall
27
 * between s and e, apply the delta.  This assumes that
28
 * sizeof(struct list_head *) == sizeof(unsigned long *).
29
 */
30
static inline void fixup(unsigned long s, unsigned long e, int d,
31
                         struct list_head *l)
32
{
33
        unsigned long *pp;
34
 
35
        pp = (unsigned long *)&l->next;
36
        if (*pp >= s && *pp < e)
37
                *pp += d;
38
 
39
        pp = (unsigned long *)&l->prev;
40
        if (*pp >= s && *pp < e)
41
                *pp += d;
42
}
43
 
44
/* Grow the allocated blocks */
45
static int grow(rh_info_t * info, int max_blocks)
46
{
47
        rh_block_t *block, *blk;
48
        int i, new_blocks;
49
        int delta;
50
        unsigned long blks, blke;
51
 
52
        if (max_blocks <= info->max_blocks)
53
                return -EINVAL;
54
 
55
        new_blocks = max_blocks - info->max_blocks;
56
 
57
        block = kmalloc(sizeof(rh_block_t) * max_blocks, GFP_KERNEL);
58
        if (block == NULL)
59
                return -ENOMEM;
60
 
61
        if (info->max_blocks > 0) {
62
 
63
                /* copy old block area */
64
                memcpy(block, info->block,
65
                       sizeof(rh_block_t) * info->max_blocks);
66
 
67
                delta = (char *)block - (char *)info->block;
68
 
69
                /* and fixup list pointers */
70
                blks = (unsigned long)info->block;
71
                blke = (unsigned long)(info->block + info->max_blocks);
72
 
73
                for (i = 0, blk = block; i < info->max_blocks; i++, blk++)
74
                        fixup(blks, blke, delta, &blk->list);
75
 
76
                fixup(blks, blke, delta, &info->empty_list);
77
                fixup(blks, blke, delta, &info->free_list);
78
                fixup(blks, blke, delta, &info->taken_list);
79
 
80
                /* free the old allocated memory */
81
                if ((info->flags & RHIF_STATIC_BLOCK) == 0)
82
                        kfree(info->block);
83
        }
84
 
85
        info->block = block;
86
        info->empty_slots += new_blocks;
87
        info->max_blocks = max_blocks;
88
        info->flags &= ~RHIF_STATIC_BLOCK;
89
 
90
        /* add all new blocks to the free list */
91
        blk = block + info->max_blocks - new_blocks;
92
        for (i = 0; i < new_blocks; i++, blk++)
93
                list_add(&blk->list, &info->empty_list);
94
 
95
        return 0;
96
}
97
 
98
/*
99
 * Assure at least the required amount of empty slots.  If this function
100
 * causes a grow in the block area then all pointers kept to the block
101
 * area are invalid!
102
 */
103
static int assure_empty(rh_info_t * info, int slots)
104
{
105
        int max_blocks;
106
 
107
        /* This function is not meant to be used to grow uncontrollably */
108
        if (slots >= 4)
109
                return -EINVAL;
110
 
111
        /* Enough space */
112
        if (info->empty_slots >= slots)
113
                return 0;
114
 
115
        /* Next 16 sized block */
116
        max_blocks = ((info->max_blocks + slots) + 15) & ~15;
117
 
118
        return grow(info, max_blocks);
119
}
120
 
121
static rh_block_t *get_slot(rh_info_t * info)
122
{
123
        rh_block_t *blk;
124
 
125
        /* If no more free slots, and failure to extend. */
126
        /* XXX: You should have called assure_empty before */
127
        if (info->empty_slots == 0) {
128
                printk(KERN_ERR "rh: out of slots; crash is imminent.\n");
129
                return NULL;
130
        }
131
 
132
        /* Get empty slot to use */
133
        blk = list_entry(info->empty_list.next, rh_block_t, list);
134
        list_del_init(&blk->list);
135
        info->empty_slots--;
136
 
137
        /* Initialize */
138
        blk->start = 0;
139
        blk->size = 0;
140
        blk->owner = NULL;
141
 
142
        return blk;
143
}
144
 
145
static inline void release_slot(rh_info_t * info, rh_block_t * blk)
146
{
147
        list_add(&blk->list, &info->empty_list);
148
        info->empty_slots++;
149
}
150
 
151
static void attach_free_block(rh_info_t * info, rh_block_t * blkn)
152
{
153
        rh_block_t *blk;
154
        rh_block_t *before;
155
        rh_block_t *after;
156
        rh_block_t *next;
157
        int size;
158
        unsigned long s, e, bs, be;
159
        struct list_head *l;
160
 
161
        /* We assume that they are aligned properly */
162
        size = blkn->size;
163
        s = blkn->start;
164
        e = s + size;
165
 
166
        /* Find the blocks immediately before and after the given one
167
         * (if any) */
168
        before = NULL;
169
        after = NULL;
170
        next = NULL;
171
 
172
        list_for_each(l, &info->free_list) {
173
                blk = list_entry(l, rh_block_t, list);
174
 
175
                bs = blk->start;
176
                be = bs + blk->size;
177
 
178
                if (next == NULL && s >= bs)
179
                        next = blk;
180
 
181
                if (be == s)
182
                        before = blk;
183
 
184
                if (e == bs)
185
                        after = blk;
186
 
187
                /* If both are not null, break now */
188
                if (before != NULL && after != NULL)
189
                        break;
190
        }
191
 
192
        /* Now check if they are really adjacent */
193
        if (before && s != (before->start + before->size))
194
                before = NULL;
195
 
196
        if (after && e != after->start)
197
                after = NULL;
198
 
199
        /* No coalescing; list insert and return */
200
        if (before == NULL && after == NULL) {
201
 
202
                if (next != NULL)
203
                        list_add(&blkn->list, &next->list);
204
                else
205
                        list_add(&blkn->list, &info->free_list);
206
 
207
                return;
208
        }
209
 
210
        /* We don't need it anymore */
211
        release_slot(info, blkn);
212
 
213
        /* Grow the before block */
214
        if (before != NULL && after == NULL) {
215
                before->size += size;
216
                return;
217
        }
218
 
219
        /* Grow the after block backwards */
220
        if (before == NULL && after != NULL) {
221
                after->start -= size;
222
                after->size += size;
223
                return;
224
        }
225
 
226
        /* Grow the before block, and release the after block */
227
        before->size += size + after->size;
228
        list_del(&after->list);
229
        release_slot(info, after);
230
}
231
 
232
static void attach_taken_block(rh_info_t * info, rh_block_t * blkn)
233
{
234
        rh_block_t *blk;
235
        struct list_head *l;
236
 
237
        /* Find the block immediately before the given one (if any) */
238
        list_for_each(l, &info->taken_list) {
239
                blk = list_entry(l, rh_block_t, list);
240
                if (blk->start > blkn->start) {
241
                        list_add_tail(&blkn->list, &blk->list);
242
                        return;
243
                }
244
        }
245
 
246
        list_add_tail(&blkn->list, &info->taken_list);
247
}
248
 
249
/*
250
 * Create a remote heap dynamically.  Note that no memory for the blocks
251
 * are allocated.  It will upon the first allocation
252
 */
253
rh_info_t *rh_create(unsigned int alignment)
254
{
255
        rh_info_t *info;
256
 
257
        /* Alignment must be a power of two */
258
        if ((alignment & (alignment - 1)) != 0)
259
                return ERR_PTR(-EINVAL);
260
 
261
        info = kmalloc(sizeof(*info), GFP_KERNEL);
262
        if (info == NULL)
263
                return ERR_PTR(-ENOMEM);
264
 
265
        info->alignment = alignment;
266
 
267
        /* Initially everything as empty */
268
        info->block = NULL;
269
        info->max_blocks = 0;
270
        info->empty_slots = 0;
271
        info->flags = 0;
272
 
273
        INIT_LIST_HEAD(&info->empty_list);
274
        INIT_LIST_HEAD(&info->free_list);
275
        INIT_LIST_HEAD(&info->taken_list);
276
 
277
        return info;
278
}
279
EXPORT_SYMBOL_GPL(rh_create);
280
 
281
/*
282
 * Destroy a dynamically created remote heap.  Deallocate only if the areas
283
 * are not static
284
 */
285
void rh_destroy(rh_info_t * info)
286
{
287
        if ((info->flags & RHIF_STATIC_BLOCK) == 0 && info->block != NULL)
288
                kfree(info->block);
289
 
290
        if ((info->flags & RHIF_STATIC_INFO) == 0)
291
                kfree(info);
292
}
293
EXPORT_SYMBOL_GPL(rh_destroy);
294
 
295
/*
296
 * Initialize in place a remote heap info block.  This is needed to support
297
 * operation very early in the startup of the kernel, when it is not yet safe
298
 * to call kmalloc.
299
 */
300
void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks,
301
             rh_block_t * block)
302
{
303
        int i;
304
        rh_block_t *blk;
305
 
306
        /* Alignment must be a power of two */
307
        if ((alignment & (alignment - 1)) != 0)
308
                return;
309
 
310
        info->alignment = alignment;
311
 
312
        /* Initially everything as empty */
313
        info->block = block;
314
        info->max_blocks = max_blocks;
315
        info->empty_slots = max_blocks;
316
        info->flags = RHIF_STATIC_INFO | RHIF_STATIC_BLOCK;
317
 
318
        INIT_LIST_HEAD(&info->empty_list);
319
        INIT_LIST_HEAD(&info->free_list);
320
        INIT_LIST_HEAD(&info->taken_list);
321
 
322
        /* Add all new blocks to the free list */
323
        for (i = 0, blk = block; i < max_blocks; i++, blk++)
324
                list_add(&blk->list, &info->empty_list);
325
}
326
EXPORT_SYMBOL_GPL(rh_init);
327
 
328
/* Attach a free memory region, coalesces regions if adjuscent */
329
int rh_attach_region(rh_info_t * info, unsigned long start, int size)
330
{
331
        rh_block_t *blk;
332
        unsigned long s, e, m;
333
        int r;
334
 
335
        /* The region must be aligned */
336
        s = start;
337
        e = s + size;
338
        m = info->alignment - 1;
339
 
340
        /* Round start up */
341
        s = (s + m) & ~m;
342
 
343
        /* Round end down */
344
        e = e & ~m;
345
 
346
        if (IS_ERR_VALUE(e) || (e < s))
347
                return -ERANGE;
348
 
349
        /* Take final values */
350
        start = s;
351
        size = e - s;
352
 
353
        /* Grow the blocks, if needed */
354
        r = assure_empty(info, 1);
355
        if (r < 0)
356
                return r;
357
 
358
        blk = get_slot(info);
359
        blk->start = start;
360
        blk->size = size;
361
        blk->owner = NULL;
362
 
363
        attach_free_block(info, blk);
364
 
365
        return 0;
366
}
367
EXPORT_SYMBOL_GPL(rh_attach_region);
368
 
369
/* Detatch given address range, splits free block if needed. */
370
unsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size)
371
{
372
        struct list_head *l;
373
        rh_block_t *blk, *newblk;
374
        unsigned long s, e, m, bs, be;
375
 
376
        /* Validate size */
377
        if (size <= 0)
378
                return (unsigned long) -EINVAL;
379
 
380
        /* The region must be aligned */
381
        s = start;
382
        e = s + size;
383
        m = info->alignment - 1;
384
 
385
        /* Round start up */
386
        s = (s + m) & ~m;
387
 
388
        /* Round end down */
389
        e = e & ~m;
390
 
391
        if (assure_empty(info, 1) < 0)
392
                return (unsigned long) -ENOMEM;
393
 
394
        blk = NULL;
395
        list_for_each(l, &info->free_list) {
396
                blk = list_entry(l, rh_block_t, list);
397
                /* The range must lie entirely inside one free block */
398
                bs = blk->start;
399
                be = blk->start + blk->size;
400
                if (s >= bs && e <= be)
401
                        break;
402
                blk = NULL;
403
        }
404
 
405
        if (blk == NULL)
406
                return (unsigned long) -ENOMEM;
407
 
408
        /* Perfect fit */
409
        if (bs == s && be == e) {
410
                /* Delete from free list, release slot */
411
                list_del(&blk->list);
412
                release_slot(info, blk);
413
                return s;
414
        }
415
 
416
        /* blk still in free list, with updated start and/or size */
417
        if (bs == s || be == e) {
418
                if (bs == s)
419
                        blk->start += size;
420
                blk->size -= size;
421
 
422
        } else {
423
                /* The front free fragment */
424
                blk->size = s - bs;
425
 
426
                /* the back free fragment */
427
                newblk = get_slot(info);
428
                newblk->start = e;
429
                newblk->size = be - e;
430
 
431
                list_add(&newblk->list, &blk->list);
432
        }
433
 
434
        return s;
435
}
436
EXPORT_SYMBOL_GPL(rh_detach_region);
437
 
438
/* Allocate a block of memory at the specified alignment.  The value returned
439
 * is an offset into the buffer initialized by rh_init(), or a negative number
440
 * if there is an error.
441
 */
442
unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const char *owner)
443
{
444
        struct list_head *l;
445
        rh_block_t *blk;
446
        rh_block_t *newblk;
447
        unsigned long start, sp_size;
448
 
449
        /* Validate size, and alignment must be power of two */
450
        if (size <= 0 || (alignment & (alignment - 1)) != 0)
451
                return (unsigned long) -EINVAL;
452
 
453
        /* Align to configured alignment */
454
        size = (size + (info->alignment - 1)) & ~(info->alignment - 1);
455
 
456
        if (assure_empty(info, 2) < 0)
457
                return (unsigned long) -ENOMEM;
458
 
459
        blk = NULL;
460
        list_for_each(l, &info->free_list) {
461
                blk = list_entry(l, rh_block_t, list);
462
                if (size <= blk->size) {
463
                        start = (blk->start + alignment - 1) & ~(alignment - 1);
464
                        if (start + size <= blk->start + blk->size)
465
                                break;
466
                }
467
                blk = NULL;
468
        }
469
 
470
        if (blk == NULL)
471
                return (unsigned long) -ENOMEM;
472
 
473
        /* Just fits */
474
        if (blk->size == size) {
475
                /* Move from free list to taken list */
476
                list_del(&blk->list);
477
                newblk = blk;
478
        } else {
479
                /* Fragment caused, split if needed */
480
                /* Create block for fragment in the beginning */
481
                sp_size = start - blk->start;
482
                if (sp_size) {
483
                        rh_block_t *spblk;
484
 
485
                        spblk = get_slot(info);
486
                        spblk->start = blk->start;
487
                        spblk->size = sp_size;
488
                        /* add before the blk */
489
                        list_add(&spblk->list, blk->list.prev);
490
                }
491
                newblk = get_slot(info);
492
                newblk->start = start;
493
                newblk->size = size;
494
 
495
                /* blk still in free list, with updated start and size
496
                 * for fragment in the end */
497
                blk->start = start + size;
498
                blk->size -= sp_size + size;
499
                /* No fragment in the end, remove blk */
500
                if (blk->size == 0) {
501
                        list_del(&blk->list);
502
                        release_slot(info, blk);
503
                }
504
        }
505
 
506
        newblk->owner = owner;
507
        attach_taken_block(info, newblk);
508
 
509
        return start;
510
}
511
EXPORT_SYMBOL_GPL(rh_alloc_align);
512
 
513
/* Allocate a block of memory at the default alignment.  The value returned is
514
 * an offset into the buffer initialized by rh_init(), or a negative number if
515
 * there is an error.
516
 */
517
unsigned long rh_alloc(rh_info_t * info, int size, const char *owner)
518
{
519
        return rh_alloc_align(info, size, info->alignment, owner);
520
}
521
EXPORT_SYMBOL_GPL(rh_alloc);
522
 
523
/* Allocate a block of memory at the given offset, rounded up to the default
524
 * alignment.  The value returned is an offset into the buffer initialized by
525
 * rh_init(), or a negative number if there is an error.
526
 */
527
unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, const char *owner)
528
{
529
        struct list_head *l;
530
        rh_block_t *blk, *newblk1, *newblk2;
531
        unsigned long s, e, m, bs = 0, be = 0;
532
 
533
        /* Validate size */
534
        if (size <= 0)
535
                return (unsigned long) -EINVAL;
536
 
537
        /* The region must be aligned */
538
        s = start;
539
        e = s + size;
540
        m = info->alignment - 1;
541
 
542
        /* Round start up */
543
        s = (s + m) & ~m;
544
 
545
        /* Round end down */
546
        e = e & ~m;
547
 
548
        if (assure_empty(info, 2) < 0)
549
                return (unsigned long) -ENOMEM;
550
 
551
        blk = NULL;
552
        list_for_each(l, &info->free_list) {
553
                blk = list_entry(l, rh_block_t, list);
554
                /* The range must lie entirely inside one free block */
555
                bs = blk->start;
556
                be = blk->start + blk->size;
557
                if (s >= bs && e <= be)
558
                        break;
559
        }
560
 
561
        if (blk == NULL)
562
                return (unsigned long) -ENOMEM;
563
 
564
        /* Perfect fit */
565
        if (bs == s && be == e) {
566
                /* Move from free list to taken list */
567
                list_del(&blk->list);
568
                blk->owner = owner;
569
 
570
                start = blk->start;
571
                attach_taken_block(info, blk);
572
 
573
                return start;
574
 
575
        }
576
 
577
        /* blk still in free list, with updated start and/or size */
578
        if (bs == s || be == e) {
579
                if (bs == s)
580
                        blk->start += size;
581
                blk->size -= size;
582
 
583
        } else {
584
                /* The front free fragment */
585
                blk->size = s - bs;
586
 
587
                /* The back free fragment */
588
                newblk2 = get_slot(info);
589
                newblk2->start = e;
590
                newblk2->size = be - e;
591
 
592
                list_add(&newblk2->list, &blk->list);
593
        }
594
 
595
        newblk1 = get_slot(info);
596
        newblk1->start = s;
597
        newblk1->size = e - s;
598
        newblk1->owner = owner;
599
 
600
        start = newblk1->start;
601
        attach_taken_block(info, newblk1);
602
 
603
        return start;
604
}
605
EXPORT_SYMBOL_GPL(rh_alloc_fixed);
606
 
607
/* Deallocate the memory previously allocated by one of the rh_alloc functions.
608
 * The return value is the size of the deallocated block, or a negative number
609
 * if there is an error.
610
 */
611
int rh_free(rh_info_t * info, unsigned long start)
612
{
613
        rh_block_t *blk, *blk2;
614
        struct list_head *l;
615
        int size;
616
 
617
        /* Linear search for block */
618
        blk = NULL;
619
        list_for_each(l, &info->taken_list) {
620
                blk2 = list_entry(l, rh_block_t, list);
621
                if (start < blk2->start)
622
                        break;
623
                blk = blk2;
624
        }
625
 
626
        if (blk == NULL || start > (blk->start + blk->size))
627
                return -EINVAL;
628
 
629
        /* Remove from taken list */
630
        list_del(&blk->list);
631
 
632
        /* Get size of freed block */
633
        size = blk->size;
634
        attach_free_block(info, blk);
635
 
636
        return size;
637
}
638
EXPORT_SYMBOL_GPL(rh_free);
639
 
640
int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats)
641
{
642
        rh_block_t *blk;
643
        struct list_head *l;
644
        struct list_head *h;
645
        int nr;
646
 
647
        switch (what) {
648
 
649
        case RHGS_FREE:
650
                h = &info->free_list;
651
                break;
652
 
653
        case RHGS_TAKEN:
654
                h = &info->taken_list;
655
                break;
656
 
657
        default:
658
                return -EINVAL;
659
        }
660
 
661
        /* Linear search for block */
662
        nr = 0;
663
        list_for_each(l, h) {
664
                blk = list_entry(l, rh_block_t, list);
665
                if (stats != NULL && nr < max_stats) {
666
                        stats->start = blk->start;
667
                        stats->size = blk->size;
668
                        stats->owner = blk->owner;
669
                        stats++;
670
                }
671
                nr++;
672
        }
673
 
674
        return nr;
675
}
676
EXPORT_SYMBOL_GPL(rh_get_stats);
677
 
678
int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner)
679
{
680
        rh_block_t *blk, *blk2;
681
        struct list_head *l;
682
        int size;
683
 
684
        /* Linear search for block */
685
        blk = NULL;
686
        list_for_each(l, &info->taken_list) {
687
                blk2 = list_entry(l, rh_block_t, list);
688
                if (start < blk2->start)
689
                        break;
690
                blk = blk2;
691
        }
692
 
693
        if (blk == NULL || start > (blk->start + blk->size))
694
                return -EINVAL;
695
 
696
        blk->owner = owner;
697
        size = blk->size;
698
 
699
        return size;
700
}
701
EXPORT_SYMBOL_GPL(rh_set_owner);
702
 
703
void rh_dump(rh_info_t * info)
704
{
705
        static rh_stats_t st[32];       /* XXX maximum 32 blocks */
706
        int maxnr;
707
        int i, nr;
708
 
709
        maxnr = ARRAY_SIZE(st);
710
 
711
        printk(KERN_INFO
712
               "info @0x%p (%d slots empty / %d max)\n",
713
               info, info->empty_slots, info->max_blocks);
714
 
715
        printk(KERN_INFO "  Free:\n");
716
        nr = rh_get_stats(info, RHGS_FREE, maxnr, st);
717
        if (nr > maxnr)
718
                nr = maxnr;
719
        for (i = 0; i < nr; i++)
720
                printk(KERN_INFO
721
                       "    0x%lx-0x%lx (%u)\n",
722
                       st[i].start, st[i].start + st[i].size,
723
                       st[i].size);
724
        printk(KERN_INFO "\n");
725
 
726
        printk(KERN_INFO "  Taken:\n");
727
        nr = rh_get_stats(info, RHGS_TAKEN, maxnr, st);
728
        if (nr > maxnr)
729
                nr = maxnr;
730
        for (i = 0; i < nr; i++)
731
                printk(KERN_INFO
732
                       "    0x%lx-0x%lx (%u) %s\n",
733
                       st[i].start, st[i].start + st[i].size,
734
                       st[i].size, st[i].owner != NULL ? st[i].owner : "");
735
        printk(KERN_INFO "\n");
736
}
737
EXPORT_SYMBOL_GPL(rh_dump);
738
 
739
void rh_dump_blk(rh_info_t * info, rh_block_t * blk)
740
{
741
        printk(KERN_INFO
742
               "blk @0x%p: 0x%lx-0x%lx (%u)\n",
743
               blk, blk->start, blk->start + blk->size, blk->size);
744
}
745
EXPORT_SYMBOL_GPL(rh_dump_blk);
746
 

powered by: WebSVN 2.1.0

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