xref: /kernel/linux/linux-5.10/arch/powerpc/lib/rheap.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * A Remote Heap.  Remote means that we don't touch the memory that the
38c2ecf20Sopenharmony_ci * heap points to. Normal heap implementations use the memory they manage
48c2ecf20Sopenharmony_ci * to place their list. We cannot do that because the memory we manage may
58c2ecf20Sopenharmony_ci * have special properties, for example it is uncachable or of different
68c2ecf20Sopenharmony_ci * endianess.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Pantelis Antoniou <panto@intracom.gr>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * 2004 (c) INTRACOM S.A. Greece. This file is licensed under
118c2ecf20Sopenharmony_ci * the terms of the GNU General Public License version 2. This program
128c2ecf20Sopenharmony_ci * is licensed "as is" without any warranty of any kind, whether express
138c2ecf20Sopenharmony_ci * or implied.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci#include <linux/types.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/export.h>
198c2ecf20Sopenharmony_ci#include <linux/mm.h>
208c2ecf20Sopenharmony_ci#include <linux/err.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <asm/rheap.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * Fixup a list_head, needed when copying lists.  If the pointers fall
278c2ecf20Sopenharmony_ci * between s and e, apply the delta.  This assumes that
288c2ecf20Sopenharmony_ci * sizeof(struct list_head *) == sizeof(unsigned long *).
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_cistatic inline void fixup(unsigned long s, unsigned long e, int d,
318c2ecf20Sopenharmony_ci			 struct list_head *l)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	unsigned long *pp;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	pp = (unsigned long *)&l->next;
368c2ecf20Sopenharmony_ci	if (*pp >= s && *pp < e)
378c2ecf20Sopenharmony_ci		*pp += d;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	pp = (unsigned long *)&l->prev;
408c2ecf20Sopenharmony_ci	if (*pp >= s && *pp < e)
418c2ecf20Sopenharmony_ci		*pp += d;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* Grow the allocated blocks */
458c2ecf20Sopenharmony_cistatic int grow(rh_info_t * info, int max_blocks)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	rh_block_t *block, *blk;
488c2ecf20Sopenharmony_ci	int i, new_blocks;
498c2ecf20Sopenharmony_ci	int delta;
508c2ecf20Sopenharmony_ci	unsigned long blks, blke;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (max_blocks <= info->max_blocks)
538c2ecf20Sopenharmony_ci		return -EINVAL;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	new_blocks = max_blocks - info->max_blocks;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	block = kmalloc_array(max_blocks, sizeof(rh_block_t), GFP_ATOMIC);
588c2ecf20Sopenharmony_ci	if (block == NULL)
598c2ecf20Sopenharmony_ci		return -ENOMEM;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (info->max_blocks > 0) {
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		/* copy old block area */
648c2ecf20Sopenharmony_ci		memcpy(block, info->block,
658c2ecf20Sopenharmony_ci		       sizeof(rh_block_t) * info->max_blocks);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		delta = (char *)block - (char *)info->block;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci		/* and fixup list pointers */
708c2ecf20Sopenharmony_ci		blks = (unsigned long)info->block;
718c2ecf20Sopenharmony_ci		blke = (unsigned long)(info->block + info->max_blocks);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		for (i = 0, blk = block; i < info->max_blocks; i++, blk++)
748c2ecf20Sopenharmony_ci			fixup(blks, blke, delta, &blk->list);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		fixup(blks, blke, delta, &info->empty_list);
778c2ecf20Sopenharmony_ci		fixup(blks, blke, delta, &info->free_list);
788c2ecf20Sopenharmony_ci		fixup(blks, blke, delta, &info->taken_list);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		/* free the old allocated memory */
818c2ecf20Sopenharmony_ci		if ((info->flags & RHIF_STATIC_BLOCK) == 0)
828c2ecf20Sopenharmony_ci			kfree(info->block);
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	info->block = block;
868c2ecf20Sopenharmony_ci	info->empty_slots += new_blocks;
878c2ecf20Sopenharmony_ci	info->max_blocks = max_blocks;
888c2ecf20Sopenharmony_ci	info->flags &= ~RHIF_STATIC_BLOCK;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* add all new blocks to the free list */
918c2ecf20Sopenharmony_ci	blk = block + info->max_blocks - new_blocks;
928c2ecf20Sopenharmony_ci	for (i = 0; i < new_blocks; i++, blk++)
938c2ecf20Sopenharmony_ci		list_add(&blk->list, &info->empty_list);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci * Assure at least the required amount of empty slots.  If this function
1008c2ecf20Sopenharmony_ci * causes a grow in the block area then all pointers kept to the block
1018c2ecf20Sopenharmony_ci * area are invalid!
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_cistatic int assure_empty(rh_info_t * info, int slots)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	int max_blocks;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* This function is not meant to be used to grow uncontrollably */
1088c2ecf20Sopenharmony_ci	if (slots >= 4)
1098c2ecf20Sopenharmony_ci		return -EINVAL;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* Enough space */
1128c2ecf20Sopenharmony_ci	if (info->empty_slots >= slots)
1138c2ecf20Sopenharmony_ci		return 0;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Next 16 sized block */
1168c2ecf20Sopenharmony_ci	max_blocks = ((info->max_blocks + slots) + 15) & ~15;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return grow(info, max_blocks);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic rh_block_t *get_slot(rh_info_t * info)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	rh_block_t *blk;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* If no more free slots, and failure to extend. */
1268c2ecf20Sopenharmony_ci	/* XXX: You should have called assure_empty before */
1278c2ecf20Sopenharmony_ci	if (info->empty_slots == 0) {
1288c2ecf20Sopenharmony_ci		printk(KERN_ERR "rh: out of slots; crash is imminent.\n");
1298c2ecf20Sopenharmony_ci		return NULL;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* Get empty slot to use */
1338c2ecf20Sopenharmony_ci	blk = list_entry(info->empty_list.next, rh_block_t, list);
1348c2ecf20Sopenharmony_ci	list_del_init(&blk->list);
1358c2ecf20Sopenharmony_ci	info->empty_slots--;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* Initialize */
1388c2ecf20Sopenharmony_ci	blk->start = 0;
1398c2ecf20Sopenharmony_ci	blk->size = 0;
1408c2ecf20Sopenharmony_ci	blk->owner = NULL;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return blk;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic inline void release_slot(rh_info_t * info, rh_block_t * blk)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	list_add(&blk->list, &info->empty_list);
1488c2ecf20Sopenharmony_ci	info->empty_slots++;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic void attach_free_block(rh_info_t * info, rh_block_t * blkn)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	rh_block_t *blk;
1548c2ecf20Sopenharmony_ci	rh_block_t *before;
1558c2ecf20Sopenharmony_ci	rh_block_t *after;
1568c2ecf20Sopenharmony_ci	rh_block_t *next;
1578c2ecf20Sopenharmony_ci	int size;
1588c2ecf20Sopenharmony_ci	unsigned long s, e, bs, be;
1598c2ecf20Sopenharmony_ci	struct list_head *l;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* We assume that they are aligned properly */
1628c2ecf20Sopenharmony_ci	size = blkn->size;
1638c2ecf20Sopenharmony_ci	s = blkn->start;
1648c2ecf20Sopenharmony_ci	e = s + size;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Find the blocks immediately before and after the given one
1678c2ecf20Sopenharmony_ci	 * (if any) */
1688c2ecf20Sopenharmony_ci	before = NULL;
1698c2ecf20Sopenharmony_ci	after = NULL;
1708c2ecf20Sopenharmony_ci	next = NULL;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	list_for_each(l, &info->free_list) {
1738c2ecf20Sopenharmony_ci		blk = list_entry(l, rh_block_t, list);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		bs = blk->start;
1768c2ecf20Sopenharmony_ci		be = bs + blk->size;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		if (next == NULL && s >= bs)
1798c2ecf20Sopenharmony_ci			next = blk;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		if (be == s)
1828c2ecf20Sopenharmony_ci			before = blk;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		if (e == bs)
1858c2ecf20Sopenharmony_ci			after = blk;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		/* If both are not null, break now */
1888c2ecf20Sopenharmony_ci		if (before != NULL && after != NULL)
1898c2ecf20Sopenharmony_ci			break;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Now check if they are really adjacent */
1938c2ecf20Sopenharmony_ci	if (before && s != (before->start + before->size))
1948c2ecf20Sopenharmony_ci		before = NULL;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (after && e != after->start)
1978c2ecf20Sopenharmony_ci		after = NULL;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/* No coalescing; list insert and return */
2008c2ecf20Sopenharmony_ci	if (before == NULL && after == NULL) {
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		if (next != NULL)
2038c2ecf20Sopenharmony_ci			list_add(&blkn->list, &next->list);
2048c2ecf20Sopenharmony_ci		else
2058c2ecf20Sopenharmony_ci			list_add(&blkn->list, &info->free_list);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		return;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* We don't need it anymore */
2118c2ecf20Sopenharmony_ci	release_slot(info, blkn);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* Grow the before block */
2148c2ecf20Sopenharmony_ci	if (before != NULL && after == NULL) {
2158c2ecf20Sopenharmony_ci		before->size += size;
2168c2ecf20Sopenharmony_ci		return;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* Grow the after block backwards */
2208c2ecf20Sopenharmony_ci	if (before == NULL && after != NULL) {
2218c2ecf20Sopenharmony_ci		after->start -= size;
2228c2ecf20Sopenharmony_ci		after->size += size;
2238c2ecf20Sopenharmony_ci		return;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Grow the before block, and release the after block */
2278c2ecf20Sopenharmony_ci	before->size += size + after->size;
2288c2ecf20Sopenharmony_ci	list_del(&after->list);
2298c2ecf20Sopenharmony_ci	release_slot(info, after);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic void attach_taken_block(rh_info_t * info, rh_block_t * blkn)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	rh_block_t *blk;
2358c2ecf20Sopenharmony_ci	struct list_head *l;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* Find the block immediately before the given one (if any) */
2388c2ecf20Sopenharmony_ci	list_for_each(l, &info->taken_list) {
2398c2ecf20Sopenharmony_ci		blk = list_entry(l, rh_block_t, list);
2408c2ecf20Sopenharmony_ci		if (blk->start > blkn->start) {
2418c2ecf20Sopenharmony_ci			list_add_tail(&blkn->list, &blk->list);
2428c2ecf20Sopenharmony_ci			return;
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	list_add_tail(&blkn->list, &info->taken_list);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/*
2508c2ecf20Sopenharmony_ci * Create a remote heap dynamically.  Note that no memory for the blocks
2518c2ecf20Sopenharmony_ci * are allocated.  It will upon the first allocation
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_cirh_info_t *rh_create(unsigned int alignment)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	rh_info_t *info;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* Alignment must be a power of two */
2588c2ecf20Sopenharmony_ci	if ((alignment & (alignment - 1)) != 0)
2598c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	info = kmalloc(sizeof(*info), GFP_ATOMIC);
2628c2ecf20Sopenharmony_ci	if (info == NULL)
2638c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	info->alignment = alignment;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/* Initially everything as empty */
2688c2ecf20Sopenharmony_ci	info->block = NULL;
2698c2ecf20Sopenharmony_ci	info->max_blocks = 0;
2708c2ecf20Sopenharmony_ci	info->empty_slots = 0;
2718c2ecf20Sopenharmony_ci	info->flags = 0;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&info->empty_list);
2748c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&info->free_list);
2758c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&info->taken_list);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return info;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_create);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci/*
2828c2ecf20Sopenharmony_ci * Destroy a dynamically created remote heap.  Deallocate only if the areas
2838c2ecf20Sopenharmony_ci * are not static
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_civoid rh_destroy(rh_info_t * info)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	if ((info->flags & RHIF_STATIC_BLOCK) == 0)
2888c2ecf20Sopenharmony_ci		kfree(info->block);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if ((info->flags & RHIF_STATIC_INFO) == 0)
2918c2ecf20Sopenharmony_ci		kfree(info);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_destroy);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/*
2968c2ecf20Sopenharmony_ci * Initialize in place a remote heap info block.  This is needed to support
2978c2ecf20Sopenharmony_ci * operation very early in the startup of the kernel, when it is not yet safe
2988c2ecf20Sopenharmony_ci * to call kmalloc.
2998c2ecf20Sopenharmony_ci */
3008c2ecf20Sopenharmony_civoid rh_init(rh_info_t * info, unsigned int alignment, int max_blocks,
3018c2ecf20Sopenharmony_ci	     rh_block_t * block)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	int i;
3048c2ecf20Sopenharmony_ci	rh_block_t *blk;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* Alignment must be a power of two */
3078c2ecf20Sopenharmony_ci	if ((alignment & (alignment - 1)) != 0)
3088c2ecf20Sopenharmony_ci		return;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	info->alignment = alignment;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* Initially everything as empty */
3138c2ecf20Sopenharmony_ci	info->block = block;
3148c2ecf20Sopenharmony_ci	info->max_blocks = max_blocks;
3158c2ecf20Sopenharmony_ci	info->empty_slots = max_blocks;
3168c2ecf20Sopenharmony_ci	info->flags = RHIF_STATIC_INFO | RHIF_STATIC_BLOCK;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&info->empty_list);
3198c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&info->free_list);
3208c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&info->taken_list);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* Add all new blocks to the free list */
3238c2ecf20Sopenharmony_ci	for (i = 0, blk = block; i < max_blocks; i++, blk++)
3248c2ecf20Sopenharmony_ci		list_add(&blk->list, &info->empty_list);
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_init);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci/* Attach a free memory region, coalesces regions if adjacent */
3298c2ecf20Sopenharmony_ciint rh_attach_region(rh_info_t * info, unsigned long start, int size)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	rh_block_t *blk;
3328c2ecf20Sopenharmony_ci	unsigned long s, e, m;
3338c2ecf20Sopenharmony_ci	int r;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* The region must be aligned */
3368c2ecf20Sopenharmony_ci	s = start;
3378c2ecf20Sopenharmony_ci	e = s + size;
3388c2ecf20Sopenharmony_ci	m = info->alignment - 1;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* Round start up */
3418c2ecf20Sopenharmony_ci	s = (s + m) & ~m;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* Round end down */
3448c2ecf20Sopenharmony_ci	e = e & ~m;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (IS_ERR_VALUE(e) || (e < s))
3478c2ecf20Sopenharmony_ci		return -ERANGE;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Take final values */
3508c2ecf20Sopenharmony_ci	start = s;
3518c2ecf20Sopenharmony_ci	size = e - s;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* Grow the blocks, if needed */
3548c2ecf20Sopenharmony_ci	r = assure_empty(info, 1);
3558c2ecf20Sopenharmony_ci	if (r < 0)
3568c2ecf20Sopenharmony_ci		return r;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	blk = get_slot(info);
3598c2ecf20Sopenharmony_ci	blk->start = start;
3608c2ecf20Sopenharmony_ci	blk->size = size;
3618c2ecf20Sopenharmony_ci	blk->owner = NULL;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	attach_free_block(info, blk);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return 0;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_attach_region);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci/* Detatch given address range, splits free block if needed. */
3708c2ecf20Sopenharmony_ciunsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct list_head *l;
3738c2ecf20Sopenharmony_ci	rh_block_t *blk, *newblk;
3748c2ecf20Sopenharmony_ci	unsigned long s, e, m, bs, be;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Validate size */
3778c2ecf20Sopenharmony_ci	if (size <= 0)
3788c2ecf20Sopenharmony_ci		return (unsigned long) -EINVAL;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* The region must be aligned */
3818c2ecf20Sopenharmony_ci	s = start;
3828c2ecf20Sopenharmony_ci	e = s + size;
3838c2ecf20Sopenharmony_ci	m = info->alignment - 1;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	/* Round start up */
3868c2ecf20Sopenharmony_ci	s = (s + m) & ~m;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* Round end down */
3898c2ecf20Sopenharmony_ci	e = e & ~m;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (assure_empty(info, 1) < 0)
3928c2ecf20Sopenharmony_ci		return (unsigned long) -ENOMEM;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	blk = NULL;
3958c2ecf20Sopenharmony_ci	list_for_each(l, &info->free_list) {
3968c2ecf20Sopenharmony_ci		blk = list_entry(l, rh_block_t, list);
3978c2ecf20Sopenharmony_ci		/* The range must lie entirely inside one free block */
3988c2ecf20Sopenharmony_ci		bs = blk->start;
3998c2ecf20Sopenharmony_ci		be = blk->start + blk->size;
4008c2ecf20Sopenharmony_ci		if (s >= bs && e <= be)
4018c2ecf20Sopenharmony_ci			break;
4028c2ecf20Sopenharmony_ci		blk = NULL;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (blk == NULL)
4068c2ecf20Sopenharmony_ci		return (unsigned long) -ENOMEM;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* Perfect fit */
4098c2ecf20Sopenharmony_ci	if (bs == s && be == e) {
4108c2ecf20Sopenharmony_ci		/* Delete from free list, release slot */
4118c2ecf20Sopenharmony_ci		list_del(&blk->list);
4128c2ecf20Sopenharmony_ci		release_slot(info, blk);
4138c2ecf20Sopenharmony_ci		return s;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* blk still in free list, with updated start and/or size */
4178c2ecf20Sopenharmony_ci	if (bs == s || be == e) {
4188c2ecf20Sopenharmony_ci		if (bs == s)
4198c2ecf20Sopenharmony_ci			blk->start += size;
4208c2ecf20Sopenharmony_ci		blk->size -= size;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	} else {
4238c2ecf20Sopenharmony_ci		/* The front free fragment */
4248c2ecf20Sopenharmony_ci		blk->size = s - bs;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		/* the back free fragment */
4278c2ecf20Sopenharmony_ci		newblk = get_slot(info);
4288c2ecf20Sopenharmony_ci		newblk->start = e;
4298c2ecf20Sopenharmony_ci		newblk->size = be - e;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		list_add(&newblk->list, &blk->list);
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return s;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_detach_region);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci/* Allocate a block of memory at the specified alignment.  The value returned
4398c2ecf20Sopenharmony_ci * is an offset into the buffer initialized by rh_init(), or a negative number
4408c2ecf20Sopenharmony_ci * if there is an error.
4418c2ecf20Sopenharmony_ci */
4428c2ecf20Sopenharmony_ciunsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const char *owner)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct list_head *l;
4458c2ecf20Sopenharmony_ci	rh_block_t *blk;
4468c2ecf20Sopenharmony_ci	rh_block_t *newblk;
4478c2ecf20Sopenharmony_ci	unsigned long start, sp_size;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/* Validate size, and alignment must be power of two */
4508c2ecf20Sopenharmony_ci	if (size <= 0 || (alignment & (alignment - 1)) != 0)
4518c2ecf20Sopenharmony_ci		return (unsigned long) -EINVAL;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* Align to configured alignment */
4548c2ecf20Sopenharmony_ci	size = (size + (info->alignment - 1)) & ~(info->alignment - 1);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	if (assure_empty(info, 2) < 0)
4578c2ecf20Sopenharmony_ci		return (unsigned long) -ENOMEM;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	blk = NULL;
4608c2ecf20Sopenharmony_ci	list_for_each(l, &info->free_list) {
4618c2ecf20Sopenharmony_ci		blk = list_entry(l, rh_block_t, list);
4628c2ecf20Sopenharmony_ci		if (size <= blk->size) {
4638c2ecf20Sopenharmony_ci			start = (blk->start + alignment - 1) & ~(alignment - 1);
4648c2ecf20Sopenharmony_ci			if (start + size <= blk->start + blk->size)
4658c2ecf20Sopenharmony_ci				break;
4668c2ecf20Sopenharmony_ci		}
4678c2ecf20Sopenharmony_ci		blk = NULL;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (blk == NULL)
4718c2ecf20Sopenharmony_ci		return (unsigned long) -ENOMEM;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Just fits */
4748c2ecf20Sopenharmony_ci	if (blk->size == size) {
4758c2ecf20Sopenharmony_ci		/* Move from free list to taken list */
4768c2ecf20Sopenharmony_ci		list_del(&blk->list);
4778c2ecf20Sopenharmony_ci		newblk = blk;
4788c2ecf20Sopenharmony_ci	} else {
4798c2ecf20Sopenharmony_ci		/* Fragment caused, split if needed */
4808c2ecf20Sopenharmony_ci		/* Create block for fragment in the beginning */
4818c2ecf20Sopenharmony_ci		sp_size = start - blk->start;
4828c2ecf20Sopenharmony_ci		if (sp_size) {
4838c2ecf20Sopenharmony_ci			rh_block_t *spblk;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci			spblk = get_slot(info);
4868c2ecf20Sopenharmony_ci			spblk->start = blk->start;
4878c2ecf20Sopenharmony_ci			spblk->size = sp_size;
4888c2ecf20Sopenharmony_ci			/* add before the blk */
4898c2ecf20Sopenharmony_ci			list_add(&spblk->list, blk->list.prev);
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci		newblk = get_slot(info);
4928c2ecf20Sopenharmony_ci		newblk->start = start;
4938c2ecf20Sopenharmony_ci		newblk->size = size;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		/* blk still in free list, with updated start and size
4968c2ecf20Sopenharmony_ci		 * for fragment in the end */
4978c2ecf20Sopenharmony_ci		blk->start = start + size;
4988c2ecf20Sopenharmony_ci		blk->size -= sp_size + size;
4998c2ecf20Sopenharmony_ci		/* No fragment in the end, remove blk */
5008c2ecf20Sopenharmony_ci		if (blk->size == 0) {
5018c2ecf20Sopenharmony_ci			list_del(&blk->list);
5028c2ecf20Sopenharmony_ci			release_slot(info, blk);
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	newblk->owner = owner;
5078c2ecf20Sopenharmony_ci	attach_taken_block(info, newblk);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	return start;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_alloc_align);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci/* Allocate a block of memory at the default alignment.  The value returned is
5148c2ecf20Sopenharmony_ci * an offset into the buffer initialized by rh_init(), or a negative number if
5158c2ecf20Sopenharmony_ci * there is an error.
5168c2ecf20Sopenharmony_ci */
5178c2ecf20Sopenharmony_ciunsigned long rh_alloc(rh_info_t * info, int size, const char *owner)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	return rh_alloc_align(info, size, info->alignment, owner);
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_alloc);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci/* Allocate a block of memory at the given offset, rounded up to the default
5248c2ecf20Sopenharmony_ci * alignment.  The value returned is an offset into the buffer initialized by
5258c2ecf20Sopenharmony_ci * rh_init(), or a negative number if there is an error.
5268c2ecf20Sopenharmony_ci */
5278c2ecf20Sopenharmony_ciunsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, const char *owner)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	struct list_head *l;
5308c2ecf20Sopenharmony_ci	rh_block_t *blk, *newblk1, *newblk2;
5318c2ecf20Sopenharmony_ci	unsigned long s, e, m, bs = 0, be = 0;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* Validate size */
5348c2ecf20Sopenharmony_ci	if (size <= 0)
5358c2ecf20Sopenharmony_ci		return (unsigned long) -EINVAL;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* The region must be aligned */
5388c2ecf20Sopenharmony_ci	s = start;
5398c2ecf20Sopenharmony_ci	e = s + size;
5408c2ecf20Sopenharmony_ci	m = info->alignment - 1;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	/* Round start up */
5438c2ecf20Sopenharmony_ci	s = (s + m) & ~m;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* Round end down */
5468c2ecf20Sopenharmony_ci	e = e & ~m;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	if (assure_empty(info, 2) < 0)
5498c2ecf20Sopenharmony_ci		return (unsigned long) -ENOMEM;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	blk = NULL;
5528c2ecf20Sopenharmony_ci	list_for_each(l, &info->free_list) {
5538c2ecf20Sopenharmony_ci		blk = list_entry(l, rh_block_t, list);
5548c2ecf20Sopenharmony_ci		/* The range must lie entirely inside one free block */
5558c2ecf20Sopenharmony_ci		bs = blk->start;
5568c2ecf20Sopenharmony_ci		be = blk->start + blk->size;
5578c2ecf20Sopenharmony_ci		if (s >= bs && e <= be)
5588c2ecf20Sopenharmony_ci			break;
5598c2ecf20Sopenharmony_ci		blk = NULL;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (blk == NULL)
5638c2ecf20Sopenharmony_ci		return (unsigned long) -ENOMEM;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/* Perfect fit */
5668c2ecf20Sopenharmony_ci	if (bs == s && be == e) {
5678c2ecf20Sopenharmony_ci		/* Move from free list to taken list */
5688c2ecf20Sopenharmony_ci		list_del(&blk->list);
5698c2ecf20Sopenharmony_ci		blk->owner = owner;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci		start = blk->start;
5728c2ecf20Sopenharmony_ci		attach_taken_block(info, blk);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		return start;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* blk still in free list, with updated start and/or size */
5798c2ecf20Sopenharmony_ci	if (bs == s || be == e) {
5808c2ecf20Sopenharmony_ci		if (bs == s)
5818c2ecf20Sopenharmony_ci			blk->start += size;
5828c2ecf20Sopenharmony_ci		blk->size -= size;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	} else {
5858c2ecf20Sopenharmony_ci		/* The front free fragment */
5868c2ecf20Sopenharmony_ci		blk->size = s - bs;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		/* The back free fragment */
5898c2ecf20Sopenharmony_ci		newblk2 = get_slot(info);
5908c2ecf20Sopenharmony_ci		newblk2->start = e;
5918c2ecf20Sopenharmony_ci		newblk2->size = be - e;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		list_add(&newblk2->list, &blk->list);
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	newblk1 = get_slot(info);
5978c2ecf20Sopenharmony_ci	newblk1->start = s;
5988c2ecf20Sopenharmony_ci	newblk1->size = e - s;
5998c2ecf20Sopenharmony_ci	newblk1->owner = owner;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	start = newblk1->start;
6028c2ecf20Sopenharmony_ci	attach_taken_block(info, newblk1);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	return start;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_alloc_fixed);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci/* Deallocate the memory previously allocated by one of the rh_alloc functions.
6098c2ecf20Sopenharmony_ci * The return value is the size of the deallocated block, or a negative number
6108c2ecf20Sopenharmony_ci * if there is an error.
6118c2ecf20Sopenharmony_ci */
6128c2ecf20Sopenharmony_ciint rh_free(rh_info_t * info, unsigned long start)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	rh_block_t *blk, *blk2;
6158c2ecf20Sopenharmony_ci	struct list_head *l;
6168c2ecf20Sopenharmony_ci	int size;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	/* Linear search for block */
6198c2ecf20Sopenharmony_ci	blk = NULL;
6208c2ecf20Sopenharmony_ci	list_for_each(l, &info->taken_list) {
6218c2ecf20Sopenharmony_ci		blk2 = list_entry(l, rh_block_t, list);
6228c2ecf20Sopenharmony_ci		if (start < blk2->start)
6238c2ecf20Sopenharmony_ci			break;
6248c2ecf20Sopenharmony_ci		blk = blk2;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (blk == NULL || start > (blk->start + blk->size))
6288c2ecf20Sopenharmony_ci		return -EINVAL;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* Remove from taken list */
6318c2ecf20Sopenharmony_ci	list_del(&blk->list);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	/* Get size of freed block */
6348c2ecf20Sopenharmony_ci	size = blk->size;
6358c2ecf20Sopenharmony_ci	attach_free_block(info, blk);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	return size;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_free);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ciint rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	rh_block_t *blk;
6448c2ecf20Sopenharmony_ci	struct list_head *l;
6458c2ecf20Sopenharmony_ci	struct list_head *h;
6468c2ecf20Sopenharmony_ci	int nr;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	switch (what) {
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	case RHGS_FREE:
6518c2ecf20Sopenharmony_ci		h = &info->free_list;
6528c2ecf20Sopenharmony_ci		break;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	case RHGS_TAKEN:
6558c2ecf20Sopenharmony_ci		h = &info->taken_list;
6568c2ecf20Sopenharmony_ci		break;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	default:
6598c2ecf20Sopenharmony_ci		return -EINVAL;
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* Linear search for block */
6638c2ecf20Sopenharmony_ci	nr = 0;
6648c2ecf20Sopenharmony_ci	list_for_each(l, h) {
6658c2ecf20Sopenharmony_ci		blk = list_entry(l, rh_block_t, list);
6668c2ecf20Sopenharmony_ci		if (stats != NULL && nr < max_stats) {
6678c2ecf20Sopenharmony_ci			stats->start = blk->start;
6688c2ecf20Sopenharmony_ci			stats->size = blk->size;
6698c2ecf20Sopenharmony_ci			stats->owner = blk->owner;
6708c2ecf20Sopenharmony_ci			stats++;
6718c2ecf20Sopenharmony_ci		}
6728c2ecf20Sopenharmony_ci		nr++;
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	return nr;
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_get_stats);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ciint rh_set_owner(rh_info_t * info, unsigned long start, const char *owner)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	rh_block_t *blk, *blk2;
6828c2ecf20Sopenharmony_ci	struct list_head *l;
6838c2ecf20Sopenharmony_ci	int size;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* Linear search for block */
6868c2ecf20Sopenharmony_ci	blk = NULL;
6878c2ecf20Sopenharmony_ci	list_for_each(l, &info->taken_list) {
6888c2ecf20Sopenharmony_ci		blk2 = list_entry(l, rh_block_t, list);
6898c2ecf20Sopenharmony_ci		if (start < blk2->start)
6908c2ecf20Sopenharmony_ci			break;
6918c2ecf20Sopenharmony_ci		blk = blk2;
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	if (blk == NULL || start > (blk->start + blk->size))
6958c2ecf20Sopenharmony_ci		return -EINVAL;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	blk->owner = owner;
6988c2ecf20Sopenharmony_ci	size = blk->size;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	return size;
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_set_owner);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_civoid rh_dump(rh_info_t * info)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	static rh_stats_t st[32];	/* XXX maximum 32 blocks */
7078c2ecf20Sopenharmony_ci	int maxnr;
7088c2ecf20Sopenharmony_ci	int i, nr;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	maxnr = ARRAY_SIZE(st);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	printk(KERN_INFO
7138c2ecf20Sopenharmony_ci	       "info @0x%p (%d slots empty / %d max)\n",
7148c2ecf20Sopenharmony_ci	       info, info->empty_slots, info->max_blocks);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	printk(KERN_INFO "  Free:\n");
7178c2ecf20Sopenharmony_ci	nr = rh_get_stats(info, RHGS_FREE, maxnr, st);
7188c2ecf20Sopenharmony_ci	if (nr > maxnr)
7198c2ecf20Sopenharmony_ci		nr = maxnr;
7208c2ecf20Sopenharmony_ci	for (i = 0; i < nr; i++)
7218c2ecf20Sopenharmony_ci		printk(KERN_INFO
7228c2ecf20Sopenharmony_ci		       "    0x%lx-0x%lx (%u)\n",
7238c2ecf20Sopenharmony_ci		       st[i].start, st[i].start + st[i].size,
7248c2ecf20Sopenharmony_ci		       st[i].size);
7258c2ecf20Sopenharmony_ci	printk(KERN_INFO "\n");
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	printk(KERN_INFO "  Taken:\n");
7288c2ecf20Sopenharmony_ci	nr = rh_get_stats(info, RHGS_TAKEN, maxnr, st);
7298c2ecf20Sopenharmony_ci	if (nr > maxnr)
7308c2ecf20Sopenharmony_ci		nr = maxnr;
7318c2ecf20Sopenharmony_ci	for (i = 0; i < nr; i++)
7328c2ecf20Sopenharmony_ci		printk(KERN_INFO
7338c2ecf20Sopenharmony_ci		       "    0x%lx-0x%lx (%u) %s\n",
7348c2ecf20Sopenharmony_ci		       st[i].start, st[i].start + st[i].size,
7358c2ecf20Sopenharmony_ci		       st[i].size, st[i].owner != NULL ? st[i].owner : "");
7368c2ecf20Sopenharmony_ci	printk(KERN_INFO "\n");
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_dump);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_civoid rh_dump_blk(rh_info_t * info, rh_block_t * blk)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	printk(KERN_INFO
7438c2ecf20Sopenharmony_ci	       "blk @0x%p: 0x%lx-0x%lx (%u)\n",
7448c2ecf20Sopenharmony_ci	       blk, blk->start, blk->start + blk->size, blk->size);
7458c2ecf20Sopenharmony_ci}
7468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rh_dump_blk);
7478c2ecf20Sopenharmony_ci
748