| /* |
| * memalloc.c |
| * |
| * Memory allocation functions for user land hibernation utilities. |
| * |
| * Copyright (C) 2008 Rafael J. Wysocki <rjw@sisk.pl> |
| * |
| * This file is released under the GPLv2. |
| * |
| */ |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| |
| #include "memalloc.h" |
| |
| unsigned int page_size; |
| unsigned int buffer_size; |
| |
| /* Mask used for page aligning */ |
| static size_t page_mask; |
| /* Memory pool used for memory allocations */ |
| static void *mem_pool; |
| /* Size of the pool */ |
| static size_t pool_size; |
| /* The array of objects representig memory allocations */ |
| static struct mem_slot *slots; |
| /* Size of the slots array */ |
| static unsigned int nr_slots; |
| /* The struct mem_slot object to be used next */ |
| static struct mem_slot *cur_slot; |
| |
| void get_page_and_buffer_sizes(void) |
| { |
| page_size = getpagesize(); |
| page_mask = ~((size_t)page_size - 1); |
| buffer_size = page_size * BUFFER_PAGES; |
| } |
| |
| /** |
| * round_up_page_size - round given number up to the closest multiple of |
| * page size |
| * @size: the number to round |
| */ |
| size_t round_up_page_size(size_t size) |
| { |
| return ((size + page_size - 1) & page_mask); |
| } |
| |
| /** |
| * round_down_page_size - round given number down to the closest multiple |
| * of the page size lesser than that number |
| * @size: the number to round |
| */ |
| size_t round_down_page_size(size_t size) |
| { |
| return size & page_mask; |
| } |
| |
| /** |
| * init_memalloc - initialize memory allocation structures |
| * @aux: Number of bytes for the internal use of the allocator |
| * @pool: Number of bytes to preallocate for the users |
| */ |
| int init_memalloc(size_t aux, size_t pool) |
| { |
| void *mem; |
| unsigned long addr; |
| size_t slots_size; |
| |
| slots_size = round_up_page_size(aux); |
| pool_size = round_up_page_size(pool); |
| /* The additional page_size may be necessary for page alignment */ |
| mem = malloc(slots_size + pool_size + page_size); |
| if (!mem) |
| return -ENOMEM; |
| slots = mem; |
| nr_slots = slots_size / sizeof(struct mem_slot); |
| cur_slot = slots; |
| /* Make the pool address be page aligned */ |
| mem += slots_size; |
| addr = ((unsigned long)mem & ~((unsigned long)page_size - 1)); |
| if (addr < (unsigned long)mem) |
| mem = (void *)(addr + page_size); |
| mem_pool = mem; |
| return 0; |
| } |
| |
| /** |
| * getmem - get some memory from the preallocated pool |
| * @size: Number of bytes needed |
| * |
| * Return the address of @size bytes of memory, aligned to the first |
| * natural power of 2 greater than or equal to @size and not greater than |
| * page_size. |
| */ |
| void *getmem(size_t size) |
| { |
| void *addr = mem_pool; |
| size_t used; |
| |
| if (cur_slot - slots >= nr_slots) { |
| fprintf(stderr, "WARNING: Not enough memory slots\n"); |
| return NULL; |
| } |
| if (cur_slot > slots) { |
| unsigned int align_size, align_mask; |
| struct mem_slot *last_slot = cur_slot - 1; |
| |
| if (size >= page_size) { |
| align_size = page_size; |
| align_mask = page_mask; |
| } else { |
| align_size = ALIGN_QWORD; |
| while (align_size < size) |
| align_size <<= 1; |
| align_mask = ~(align_size - 1); |
| } |
| addr += ((last_slot->addr - mem_pool + last_slot->size + |
| align_size - 1) & align_mask); |
| } |
| used = addr - mem_pool; |
| if (used > pool_size || size > pool_size - used) { |
| fprintf(stderr, "WARNING: Not enough memory in the pool\n"); |
| return NULL; |
| } |
| cur_slot->addr = addr; |
| cur_slot->size = size; |
| cur_slot++; |
| return addr; |
| } |
| |
| /** |
| * free_slot - mark given memory slot as freed and try to remove it |
| * @slot: Number of memory slot to free |
| */ |
| static void free_slot(int slot) |
| { |
| struct mem_slot *last_slot = cur_slot - 1; |
| |
| /* |
| * To free the slot, set its size to zero. If this is the most recently |
| * allocated one, remove it from the array along with some other already |
| * freed slots (the size of the last remaining slot must be greater than |
| * zero). |
| */ |
| slots[slot].size = 0; |
| if (slots + slot == last_slot) { |
| do |
| last_slot--; |
| while (!last_slot->size && last_slot >= slots); |
| cur_slot = last_slot + 1; |
| } |
| } |
| |
| /** |
| * freemem - free memory allocated by getmem |
| * @address: Start of the memory area to free |
| */ |
| void freemem(void *address) |
| { |
| int i, j, k; |
| |
| /* Check if there is anything to do */ |
| if (cur_slot <= slots) |
| return; |
| /* Find the slot to free, using the observation that they are sorted */ |
| i = 0; |
| j = cur_slot - slots - 1; |
| do { |
| k = (i + j) / 2; |
| if (slots[k].addr == address) { |
| free_slot(k); |
| break; |
| } else if (address > slots[k].addr) { |
| if (i != k) { |
| i = k; |
| } else { |
| if (slots[j].addr == address) |
| free_slot(j); |
| break; |
| } |
| } else { |
| j = k; |
| } |
| } while (i < j); |
| } |
| |
| /** |
| * end_memalloc - free memory used by the allocator |
| */ |
| void free_memalloc(void) |
| { |
| if (slots) |
| free(slots); |
| } |