···2323endif24242525# Temporary hack until we have migrated to asm-powerpc2626-ifeq ($(CONFIG_PPC_MERGE),y)2726obj-$(CONFIG_8xx) += rheap.o2827obj-$(CONFIG_CPM2) += rheap.o2929-endif
···11-/*22- * A Remote Heap. Remote means that we don't touch the memory that the33- * heap points to. Normal heap implementations use the memory they manage44- * to place their list. We cannot do that because the memory we manage may55- * have special properties, for example it is uncachable or of different66- * endianess.77- *88- * Author: Pantelis Antoniou <panto@intracom.gr>99- *1010- * 2004 (c) INTRACOM S.A. Greece. This file is licensed under1111- * the terms of the GNU General Public License version 2. This program1212- * is licensed "as is" without any warranty of any kind, whether express1313- * or implied.1414- */1515-#include <linux/types.h>1616-#include <linux/errno.h>1717-#include <linux/kernel.h>1818-#include <linux/mm.h>1919-#include <linux/slab.h>2020-2121-#include <asm/rheap.h>2222-2323-/*2424- * Fixup a list_head, needed when copying lists. If the pointers fall2525- * between s and e, apply the delta. This assumes that2626- * sizeof(struct list_head *) == sizeof(unsigned long *).2727- */2828-static inline void fixup(unsigned long s, unsigned long e, int d,2929- struct list_head *l)3030-{3131- unsigned long *pp;3232-3333- pp = (unsigned long *)&l->next;3434- if (*pp >= s && *pp < e)3535- *pp += d;3636-3737- pp = (unsigned long *)&l->prev;3838- if (*pp >= s && *pp < e)3939- *pp += d;4040-}4141-4242-/* Grow the allocated blocks */4343-static int grow(rh_info_t * info, int max_blocks)4444-{4545- rh_block_t *block, *blk;4646- int i, new_blocks;4747- int delta;4848- unsigned long blks, blke;4949-5050- if (max_blocks <= info->max_blocks)5151- return -EINVAL;5252-5353- new_blocks = max_blocks - info->max_blocks;5454-5555- block = kmalloc(sizeof(rh_block_t) * max_blocks, GFP_KERNEL);5656- if (block == NULL)5757- return -ENOMEM;5858-5959- if (info->max_blocks > 0) {6060-6161- /* copy old block area */6262- memcpy(block, info->block,6363- sizeof(rh_block_t) * info->max_blocks);6464-6565- delta = (char *)block - (char *)info->block;6666-6767- /* and fixup list pointers */6868- blks = (unsigned long)info->block;6969- blke = (unsigned long)(info->block + info->max_blocks);7070-7171- for (i = 0, blk = block; i < info->max_blocks; i++, blk++)7272- fixup(blks, blke, delta, &blk->list);7373-7474- fixup(blks, blke, delta, &info->empty_list);7575- fixup(blks, blke, delta, &info->free_list);7676- fixup(blks, blke, delta, &info->taken_list);7777-7878- /* free the old allocated memory */7979- if ((info->flags & RHIF_STATIC_BLOCK) == 0)8080- kfree(info->block);8181- }8282-8383- info->block = block;8484- info->empty_slots += new_blocks;8585- info->max_blocks = max_blocks;8686- info->flags &= ~RHIF_STATIC_BLOCK;8787-8888- /* add all new blocks to the free list */8989- for (i = 0, blk = block + info->max_blocks; i < new_blocks; i++, blk++)9090- list_add(&blk->list, &info->empty_list);9191-9292- return 0;9393-}9494-9595-/*9696- * Assure at least the required amount of empty slots. If this function9797- * causes a grow in the block area then all pointers kept to the block9898- * area are invalid!9999- */100100-static int assure_empty(rh_info_t * info, int slots)101101-{102102- int max_blocks;103103-104104- /* This function is not meant to be used to grow uncontrollably */105105- if (slots >= 4)106106- return -EINVAL;107107-108108- /* Enough space */109109- if (info->empty_slots >= slots)110110- return 0;111111-112112- /* Next 16 sized block */113113- max_blocks = ((info->max_blocks + slots) + 15) & ~15;114114-115115- return grow(info, max_blocks);116116-}117117-118118-static rh_block_t *get_slot(rh_info_t * info)119119-{120120- rh_block_t *blk;121121-122122- /* If no more free slots, and failure to extend. */123123- /* XXX: You should have called assure_empty before */124124- if (info->empty_slots == 0) {125125- printk(KERN_ERR "rh: out of slots; crash is imminent.\n");126126- return NULL;127127- }128128-129129- /* Get empty slot to use */130130- blk = list_entry(info->empty_list.next, rh_block_t, list);131131- list_del_init(&blk->list);132132- info->empty_slots--;133133-134134- /* Initialize */135135- blk->start = 0;136136- blk->size = 0;137137- blk->owner = NULL;138138-139139- return blk;140140-}141141-142142-static inline void release_slot(rh_info_t * info, rh_block_t * blk)143143-{144144- list_add(&blk->list, &info->empty_list);145145- info->empty_slots++;146146-}147147-148148-static void attach_free_block(rh_info_t * info, rh_block_t * blkn)149149-{150150- rh_block_t *blk;151151- rh_block_t *before;152152- rh_block_t *after;153153- rh_block_t *next;154154- int size;155155- unsigned long s, e, bs, be;156156- struct list_head *l;157157-158158- /* We assume that they are aligned properly */159159- size = blkn->size;160160- s = blkn->start;161161- e = s + size;162162-163163- /* Find the blocks immediately before and after the given one164164- * (if any) */165165- before = NULL;166166- after = NULL;167167- next = NULL;168168-169169- list_for_each(l, &info->free_list) {170170- blk = list_entry(l, rh_block_t, list);171171-172172- bs = blk->start;173173- be = bs + blk->size;174174-175175- if (next == NULL && s >= bs)176176- next = blk;177177-178178- if (be == s)179179- before = blk;180180-181181- if (e == bs)182182- after = blk;183183-184184- /* If both are not null, break now */185185- if (before != NULL && after != NULL)186186- break;187187- }188188-189189- /* Now check if they are really adjacent */190190- if (before && s != (before->start + before->size))191191- before = NULL;192192-193193- if (after && e != after->start)194194- after = NULL;195195-196196- /* No coalescing; list insert and return */197197- if (before == NULL && after == NULL) {198198-199199- if (next != NULL)200200- list_add(&blkn->list, &next->list);201201- else202202- list_add(&blkn->list, &info->free_list);203203-204204- return;205205- }206206-207207- /* We don't need it anymore */208208- release_slot(info, blkn);209209-210210- /* Grow the before block */211211- if (before != NULL && after == NULL) {212212- before->size += size;213213- return;214214- }215215-216216- /* Grow the after block backwards */217217- if (before == NULL && after != NULL) {218218- after->start -= size;219219- after->size += size;220220- return;221221- }222222-223223- /* Grow the before block, and release the after block */224224- before->size += size + after->size;225225- list_del(&after->list);226226- release_slot(info, after);227227-}228228-229229-static void attach_taken_block(rh_info_t * info, rh_block_t * blkn)230230-{231231- rh_block_t *blk;232232- struct list_head *l;233233-234234- /* Find the block immediately before the given one (if any) */235235- list_for_each(l, &info->taken_list) {236236- blk = list_entry(l, rh_block_t, list);237237- if (blk->start > blkn->start) {238238- list_add_tail(&blkn->list, &blk->list);239239- return;240240- }241241- }242242-243243- list_add_tail(&blkn->list, &info->taken_list);244244-}245245-246246-/*247247- * Create a remote heap dynamically. Note that no memory for the blocks248248- * are allocated. It will upon the first allocation249249- */250250-rh_info_t *rh_create(unsigned int alignment)251251-{252252- rh_info_t *info;253253-254254- /* Alignment must be a power of two */255255- if ((alignment & (alignment - 1)) != 0)256256- return ERR_PTR(-EINVAL);257257-258258- info = kmalloc(sizeof(*info), GFP_KERNEL);259259- if (info == NULL)260260- return ERR_PTR(-ENOMEM);261261-262262- info->alignment = alignment;263263-264264- /* Initially everything as empty */265265- info->block = NULL;266266- info->max_blocks = 0;267267- info->empty_slots = 0;268268- info->flags = 0;269269-270270- INIT_LIST_HEAD(&info->empty_list);271271- INIT_LIST_HEAD(&info->free_list);272272- INIT_LIST_HEAD(&info->taken_list);273273-274274- return info;275275-}276276-277277-/*278278- * Destroy a dynamically created remote heap. Deallocate only if the areas279279- * are not static280280- */281281-void rh_destroy(rh_info_t * info)282282-{283283- if ((info->flags & RHIF_STATIC_BLOCK) == 0 && info->block != NULL)284284- kfree(info->block);285285-286286- if ((info->flags & RHIF_STATIC_INFO) == 0)287287- kfree(info);288288-}289289-290290-/*291291- * Initialize in place a remote heap info block. This is needed to support292292- * operation very early in the startup of the kernel, when it is not yet safe293293- * to call kmalloc.294294- */295295-void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks,296296- rh_block_t * block)297297-{298298- int i;299299- rh_block_t *blk;300300-301301- /* Alignment must be a power of two */302302- if ((alignment & (alignment - 1)) != 0)303303- return;304304-305305- info->alignment = alignment;306306-307307- /* Initially everything as empty */308308- info->block = block;309309- info->max_blocks = max_blocks;310310- info->empty_slots = max_blocks;311311- info->flags = RHIF_STATIC_INFO | RHIF_STATIC_BLOCK;312312-313313- INIT_LIST_HEAD(&info->empty_list);314314- INIT_LIST_HEAD(&info->free_list);315315- INIT_LIST_HEAD(&info->taken_list);316316-317317- /* Add all new blocks to the free list */318318- for (i = 0, blk = block; i < max_blocks; i++, blk++)319319- list_add(&blk->list, &info->empty_list);320320-}321321-322322-/* Attach a free memory region, coalesces regions if adjuscent */323323-int rh_attach_region(rh_info_t * info, unsigned long start, int size)324324-{325325- rh_block_t *blk;326326- unsigned long s, e, m;327327- int r;328328-329329- /* The region must be aligned */330330- s = start;331331- e = s + size;332332- m = info->alignment - 1;333333-334334- /* Round start up */335335- s = (s + m) & ~m;336336-337337- /* Round end down */338338- e = e & ~m;339339-340340- if (IS_ERR_VALUE(e) || (e < s))341341- return -ERANGE;342342-343343- /* Take final values */344344- start = s;345345- size = e - s;346346-347347- /* Grow the blocks, if needed */348348- r = assure_empty(info, 1);349349- if (r < 0)350350- return r;351351-352352- blk = get_slot(info);353353- blk->start = start;354354- blk->size = size;355355- blk->owner = NULL;356356-357357- attach_free_block(info, blk);358358-359359- return 0;360360-}361361-362362-/* Detatch given address range, splits free block if needed. */363363-unsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size)364364-{365365- struct list_head *l;366366- rh_block_t *blk, *newblk;367367- unsigned long s, e, m, bs, be;368368-369369- /* Validate size */370370- if (size <= 0)371371- return (unsigned long) -EINVAL;372372-373373- /* The region must be aligned */374374- s = start;375375- e = s + size;376376- m = info->alignment - 1;377377-378378- /* Round start up */379379- s = (s + m) & ~m;380380-381381- /* Round end down */382382- e = e & ~m;383383-384384- if (assure_empty(info, 1) < 0)385385- return (unsigned long) -ENOMEM;386386-387387- blk = NULL;388388- list_for_each(l, &info->free_list) {389389- blk = list_entry(l, rh_block_t, list);390390- /* The range must lie entirely inside one free block */391391- bs = blk->start;392392- be = blk->start + blk->size;393393- if (s >= bs && e <= be)394394- break;395395- blk = NULL;396396- }397397-398398- if (blk == NULL)399399- return (unsigned long) -ENOMEM;400400-401401- /* Perfect fit */402402- if (bs == s && be == e) {403403- /* Delete from free list, release slot */404404- list_del(&blk->list);405405- release_slot(info, blk);406406- return s;407407- }408408-409409- /* blk still in free list, with updated start and/or size */410410- if (bs == s || be == e) {411411- if (bs == s)412412- blk->start += size;413413- blk->size -= size;414414-415415- } else {416416- /* The front free fragment */417417- blk->size = s - bs;418418-419419- /* the back free fragment */420420- newblk = get_slot(info);421421- newblk->start = e;422422- newblk->size = be - e;423423-424424- list_add(&newblk->list, &blk->list);425425- }426426-427427- return s;428428-}429429-430430-unsigned long rh_alloc(rh_info_t * info, int size, const char *owner)431431-{432432- struct list_head *l;433433- rh_block_t *blk;434434- rh_block_t *newblk;435435- unsigned long start;436436-437437- /* Validate size */438438- if (size <= 0)439439- return (unsigned long) -EINVAL;440440-441441- /* Align to configured alignment */442442- size = (size + (info->alignment - 1)) & ~(info->alignment - 1);443443-444444- if (assure_empty(info, 1) < 0)445445- return (unsigned long) -ENOMEM;446446-447447- blk = NULL;448448- list_for_each(l, &info->free_list) {449449- blk = list_entry(l, rh_block_t, list);450450- if (size <= blk->size)451451- break;452452- blk = NULL;453453- }454454-455455- if (blk == NULL)456456- return (unsigned long) -ENOMEM;457457-458458- /* Just fits */459459- if (blk->size == size) {460460- /* Move from free list to taken list */461461- list_del(&blk->list);462462- blk->owner = owner;463463- start = blk->start;464464-465465- attach_taken_block(info, blk);466466-467467- return start;468468- }469469-470470- newblk = get_slot(info);471471- newblk->start = blk->start;472472- newblk->size = size;473473- newblk->owner = owner;474474-475475- /* blk still in free list, with updated start, size */476476- blk->start += size;477477- blk->size -= size;478478-479479- start = newblk->start;480480-481481- attach_taken_block(info, newblk);482482-483483- return start;484484-}485485-486486-/* allocate at precisely the given address */487487-unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, const char *owner)488488-{489489- struct list_head *l;490490- rh_block_t *blk, *newblk1, *newblk2;491491- unsigned long s, e, m, bs=0, be=0;492492-493493- /* Validate size */494494- if (size <= 0)495495- return (unsigned long) -EINVAL;496496-497497- /* The region must be aligned */498498- s = start;499499- e = s + size;500500- m = info->alignment - 1;501501-502502- /* Round start up */503503- s = (s + m) & ~m;504504-505505- /* Round end down */506506- e = e & ~m;507507-508508- if (assure_empty(info, 2) < 0)509509- return (unsigned long) -ENOMEM;510510-511511- blk = NULL;512512- list_for_each(l, &info->free_list) {513513- blk = list_entry(l, rh_block_t, list);514514- /* The range must lie entirely inside one free block */515515- bs = blk->start;516516- be = blk->start + blk->size;517517- if (s >= bs && e <= be)518518- break;519519- }520520-521521- if (blk == NULL)522522- return (unsigned long) -ENOMEM;523523-524524- /* Perfect fit */525525- if (bs == s && be == e) {526526- /* Move from free list to taken list */527527- list_del(&blk->list);528528- blk->owner = owner;529529-530530- start = blk->start;531531- attach_taken_block(info, blk);532532-533533- return start;534534-535535- }536536-537537- /* blk still in free list, with updated start and/or size */538538- if (bs == s || be == e) {539539- if (bs == s)540540- blk->start += size;541541- blk->size -= size;542542-543543- } else {544544- /* The front free fragment */545545- blk->size = s - bs;546546-547547- /* The back free fragment */548548- newblk2 = get_slot(info);549549- newblk2->start = e;550550- newblk2->size = be - e;551551-552552- list_add(&newblk2->list, &blk->list);553553- }554554-555555- newblk1 = get_slot(info);556556- newblk1->start = s;557557- newblk1->size = e - s;558558- newblk1->owner = owner;559559-560560- start = newblk1->start;561561- attach_taken_block(info, newblk1);562562-563563- return start;564564-}565565-566566-int rh_free(rh_info_t * info, unsigned long start)567567-{568568- rh_block_t *blk, *blk2;569569- struct list_head *l;570570- int size;571571-572572- /* Linear search for block */573573- blk = NULL;574574- list_for_each(l, &info->taken_list) {575575- blk2 = list_entry(l, rh_block_t, list);576576- if (start < blk2->start)577577- break;578578- blk = blk2;579579- }580580-581581- if (blk == NULL || start > (blk->start + blk->size))582582- return -EINVAL;583583-584584- /* Remove from taken list */585585- list_del(&blk->list);586586-587587- /* Get size of freed block */588588- size = blk->size;589589- attach_free_block(info, blk);590590-591591- return size;592592-}593593-594594-int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats)595595-{596596- rh_block_t *blk;597597- struct list_head *l;598598- struct list_head *h;599599- int nr;600600-601601- switch (what) {602602-603603- case RHGS_FREE:604604- h = &info->free_list;605605- break;606606-607607- case RHGS_TAKEN:608608- h = &info->taken_list;609609- break;610610-611611- default:612612- return -EINVAL;613613- }614614-615615- /* Linear search for block */616616- nr = 0;617617- list_for_each(l, h) {618618- blk = list_entry(l, rh_block_t, list);619619- if (stats != NULL && nr < max_stats) {620620- stats->start = blk->start;621621- stats->size = blk->size;622622- stats->owner = blk->owner;623623- stats++;624624- }625625- nr++;626626- }627627-628628- return nr;629629-}630630-631631-int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner)632632-{633633- rh_block_t *blk, *blk2;634634- struct list_head *l;635635- int size;636636-637637- /* Linear search for block */638638- blk = NULL;639639- list_for_each(l, &info->taken_list) {640640- blk2 = list_entry(l, rh_block_t, list);641641- if (start < blk2->start)642642- break;643643- blk = blk2;644644- }645645-646646- if (blk == NULL || start > (blk->start + blk->size))647647- return -EINVAL;648648-649649- blk->owner = owner;650650- size = blk->size;651651-652652- return size;653653-}654654-655655-void rh_dump(rh_info_t * info)656656-{657657- static rh_stats_t st[32]; /* XXX maximum 32 blocks */658658- int maxnr;659659- int i, nr;660660-661661- maxnr = ARRAY_SIZE(st);662662-663663- printk(KERN_INFO664664- "info @0x%p (%d slots empty / %d max)\n",665665- info, info->empty_slots, info->max_blocks);666666-667667- printk(KERN_INFO " Free:\n");668668- nr = rh_get_stats(info, RHGS_FREE, maxnr, st);669669- if (nr > maxnr)670670- nr = maxnr;671671- for (i = 0; i < nr; i++)672672- printk(KERN_INFO673673- " 0x%lx-0x%lx (%u)\n",674674- st[i].start, st[i].start + st[i].size,675675- st[i].size);676676- printk(KERN_INFO "\n");677677-678678- printk(KERN_INFO " Taken:\n");679679- nr = rh_get_stats(info, RHGS_TAKEN, maxnr, st);680680- if (nr > maxnr)681681- nr = maxnr;682682- for (i = 0; i < nr; i++)683683- printk(KERN_INFO684684- " 0x%lx-0x%lx (%u) %s\n",685685- st[i].start, st[i].start + st[i].size,686686- st[i].size, st[i].owner != NULL ? st[i].owner : "");687687- printk(KERN_INFO "\n");688688-}689689-690690-void rh_dump_blk(rh_info_t * info, rh_block_t * blk)691691-{692692- printk(KERN_INFO693693- "blk @0x%p: 0x%lx-0x%lx (%u)\n",694694- blk, blk->start, blk->start + blk->size, blk->size);695695-}