-
Notifications
You must be signed in to change notification settings - Fork 4
/
memalloc.c
198 lines (181 loc) · 4.53 KB
/
memalloc.c
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 <[email protected]>
*
* 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);
}