1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
/*
* 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);
}
|