📄 rheap.c
字号:
/* Validate size */ if (size <= 0) return (unsigned long) -EINVAL; /* The region must be aligned */ s = start; e = s + size; m = info->alignment - 1; /* Round start up */ s = (s + m) & ~m; /* Round end down */ e = e & ~m; if (assure_empty(info, 1) < 0) return (unsigned long) -ENOMEM; blk = NULL; list_for_each(l, &info->free_list) { blk = list_entry(l, rh_block_t, list); /* The range must lie entirely inside one free block */ bs = blk->start; be = blk->start + blk->size; if (s >= bs && e <= be) break; blk = NULL; } if (blk == NULL) return (unsigned long) -ENOMEM; /* Perfect fit */ if (bs == s && be == e) { /* Delete from free list, release slot */ list_del(&blk->list); release_slot(info, blk); return s; } /* blk still in free list, with updated start and/or size */ if (bs == s || be == e) { if (bs == s) blk->start += size; blk->size -= size; } else { /* The front free fragment */ blk->size = s - bs; /* the back free fragment */ newblk = get_slot(info); newblk->start = e; newblk->size = be - e; list_add(&newblk->list, &blk->list); } return s;}EXPORT_SYMBOL_GPL(rh_detach_region);/* Allocate a block of memory at the specified alignment. The value returned * is an offset into the buffer initialized by rh_init(), or a negative number * if there is an error. */unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const char *owner){ struct list_head *l; rh_block_t *blk; rh_block_t *newblk; unsigned long start, sp_size; /* Validate size, and alignment must be power of two */ if (size <= 0 || (alignment & (alignment - 1)) != 0) return (unsigned long) -EINVAL; /* Align to configured alignment */ size = (size + (info->alignment - 1)) & ~(info->alignment - 1); if (assure_empty(info, 2) < 0) return (unsigned long) -ENOMEM; blk = NULL; list_for_each(l, &info->free_list) { blk = list_entry(l, rh_block_t, list); if (size <= blk->size) { start = (blk->start + alignment - 1) & ~(alignment - 1); if (start + size <= blk->start + blk->size) break; } blk = NULL; } if (blk == NULL) return (unsigned long) -ENOMEM; /* Just fits */ if (blk->size == size) { /* Move from free list to taken list */ list_del(&blk->list); newblk = blk; } else { /* Fragment caused, split if needed */ /* Create block for fragment in the beginning */ sp_size = start - blk->start; if (sp_size) { rh_block_t *spblk; spblk = get_slot(info); spblk->start = blk->start; spblk->size = sp_size; /* add before the blk */ list_add(&spblk->list, blk->list.prev); } newblk = get_slot(info); newblk->start = start; newblk->size = size; /* blk still in free list, with updated start and size * for fragment in the end */ blk->start = start + size; blk->size -= sp_size + size; /* No fragment in the end, remove blk */ if (blk->size == 0) { list_del(&blk->list); release_slot(info, blk); } } newblk->owner = owner; attach_taken_block(info, newblk); return start;}EXPORT_SYMBOL_GPL(rh_alloc_align);/* Allocate a block of memory at the default alignment. The value returned is * an offset into the buffer initialized by rh_init(), or a negative number if * there is an error. */unsigned long rh_alloc(rh_info_t * info, int size, const char *owner){ return rh_alloc_align(info, size, info->alignment, owner);}EXPORT_SYMBOL_GPL(rh_alloc);/* Allocate a block of memory at the given offset, rounded up to the default * alignment. The value returned is an offset into the buffer initialized by * rh_init(), or a negative number if there is an error. */unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, const char *owner){ struct list_head *l; rh_block_t *blk, *newblk1, *newblk2; unsigned long s, e, m, bs = 0, be = 0; /* Validate size */ if (size <= 0) return (unsigned long) -EINVAL; /* The region must be aligned */ s = start; e = s + size; m = info->alignment - 1; /* Round start up */ s = (s + m) & ~m; /* Round end down */ e = e & ~m; if (assure_empty(info, 2) < 0) return (unsigned long) -ENOMEM; blk = NULL; list_for_each(l, &info->free_list) { blk = list_entry(l, rh_block_t, list); /* The range must lie entirely inside one free block */ bs = blk->start; be = blk->start + blk->size; if (s >= bs && e <= be) break; } if (blk == NULL) return (unsigned long) -ENOMEM; /* Perfect fit */ if (bs == s && be == e) { /* Move from free list to taken list */ list_del(&blk->list); blk->owner = owner; start = blk->start; attach_taken_block(info, blk); return start; } /* blk still in free list, with updated start and/or size */ if (bs == s || be == e) { if (bs == s) blk->start += size; blk->size -= size; } else { /* The front free fragment */ blk->size = s - bs; /* The back free fragment */ newblk2 = get_slot(info); newblk2->start = e; newblk2->size = be - e; list_add(&newblk2->list, &blk->list); } newblk1 = get_slot(info); newblk1->start = s; newblk1->size = e - s; newblk1->owner = owner; start = newblk1->start; attach_taken_block(info, newblk1); return start;}EXPORT_SYMBOL_GPL(rh_alloc_fixed);/* Deallocate the memory previously allocated by one of the rh_alloc functions. * The return value is the size of the deallocated block, or a negative number * if there is an error. */int rh_free(rh_info_t * info, unsigned long start){ rh_block_t *blk, *blk2; struct list_head *l; int size; /* Linear search for block */ blk = NULL; list_for_each(l, &info->taken_list) { blk2 = list_entry(l, rh_block_t, list); if (start < blk2->start) break; blk = blk2; } if (blk == NULL || start > (blk->start + blk->size)) return -EINVAL; /* Remove from taken list */ list_del(&blk->list); /* Get size of freed block */ size = blk->size; attach_free_block(info, blk); return size;}EXPORT_SYMBOL_GPL(rh_free);int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats){ rh_block_t *blk; struct list_head *l; struct list_head *h; int nr; switch (what) { case RHGS_FREE: h = &info->free_list; break; case RHGS_TAKEN: h = &info->taken_list; break; default: return -EINVAL; } /* Linear search for block */ nr = 0; list_for_each(l, h) { blk = list_entry(l, rh_block_t, list); if (stats != NULL && nr < max_stats) { stats->start = blk->start; stats->size = blk->size; stats->owner = blk->owner; stats++; } nr++; } return nr;}EXPORT_SYMBOL_GPL(rh_get_stats);int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner){ rh_block_t *blk, *blk2; struct list_head *l; int size; /* Linear search for block */ blk = NULL; list_for_each(l, &info->taken_list) { blk2 = list_entry(l, rh_block_t, list); if (start < blk2->start) break; blk = blk2; } if (blk == NULL || start > (blk->start + blk->size)) return -EINVAL; blk->owner = owner; size = blk->size; return size;}EXPORT_SYMBOL_GPL(rh_set_owner);void rh_dump(rh_info_t * info){ static rh_stats_t st[32]; /* XXX maximum 32 blocks */ int maxnr; int i, nr; maxnr = ARRAY_SIZE(st); printk(KERN_INFO "info @0x%p (%d slots empty / %d max)\n", info, info->empty_slots, info->max_blocks); printk(KERN_INFO " Free:\n"); nr = rh_get_stats(info, RHGS_FREE, maxnr, st); if (nr > maxnr) nr = maxnr; for (i = 0; i < nr; i++) printk(KERN_INFO " 0x%lx-0x%lx (%u)\n", st[i].start, st[i].start + st[i].size, st[i].size); printk(KERN_INFO "\n"); printk(KERN_INFO " Taken:\n"); nr = rh_get_stats(info, RHGS_TAKEN, maxnr, st); if (nr > maxnr) nr = maxnr; for (i = 0; i < nr; i++) printk(KERN_INFO " 0x%lx-0x%lx (%u) %s\n", st[i].start, st[i].start + st[i].size, st[i].size, st[i].owner != NULL ? st[i].owner : ""); printk(KERN_INFO "\n");}EXPORT_SYMBOL_GPL(rh_dump);void rh_dump_blk(rh_info_t * info, rh_block_t * blk){ printk(KERN_INFO "blk @0x%p: 0x%lx-0x%lx (%u)\n", blk, blk->start, blk->start + blk->size, blk->size);}EXPORT_SYMBOL_GPL(rh_dump_blk);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -