1 /*
2  *	aprsc
3  *
4  *	(c) Matti Aarnio, OH2MQK, <oh2mqk@sral.fi>
5  *
6  *	This program is licensed under the BSD license, which can be found
7  *	in the file LICENSE.
8  *
9  */
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <pthread.h>
16 #include <sys/mman.h>
17 #include <fcntl.h>
18 
19 #include "cellmalloc.h"
20 #include "hmalloc.h"
21 #include "hlog.h"
22 
23 /*
24  *   cellmalloc() -- manages arrays of cells of data
25  *
26  */
27 
28 #ifndef _FOR_VALGRIND_
29 struct cellhead;
30 
31 struct cellarena_t {
32 	int	cellsize;
33 	int	alignment;
34 	int	increment; /* alignment overhead applied.. */
35 	int	lifo_policy;
36   	int	minfree;
37 	int	use_mutex;
38 
39 	const char *arenaname;
40 
41 	pthread_mutex_t mutex;
42 
43   	struct cellhead *free_head;
44   	struct cellhead *free_tail;
45 
46 	int	 freecount;
47 	int	 createsize;
48 
49 	int	 cellblocks_count;
50 #define CELLBLOCKS_MAX 200 /* track client cell allocator limit! */
51 	char	*cellblocks[CELLBLOCKS_MAX];	/* ref as 'char pointer' for pointer arithmetics... */
52 };
53 
54 #define CELLHEAD_DEBUG 0
55 
56 struct cellhead {
57 #if CELLHEAD_DEBUG == 1
58 	struct cellarena_t *ca;
59 #endif
60 	struct cellhead *next;
61 };
62 
63 
64 /*
65  * new_cellblock() -- must be called MUTEX PROTECTED
66  *
67  */
68 
new_cellblock(cellarena_t * ca)69 int new_cellblock(cellarena_t *ca)
70 {
71 	int i;
72 	char *cb;
73 
74 #ifdef MEMDEBUG /* External backing-store files, unique ones for each cellblock,
75 		   which at Linux names memory blocks in  /proc/nnn/smaps "file"
76 		   with this filename.. */
77 	int fd;
78 	char name[2048];
79 
80 	sprintf(name, "/tmp/.-%d-%s-%d.mmap", getpid(), ca->arenaname, ca->cellblocks_count );
81 	unlink(name);
82 	fd = open(name, O_RDWR|O_CREAT, 644);
83 	unlink(name);
84 	if (fd >= 0) {
85 	  memset(name, 0, sizeof(name));
86 	  i = 0;
87 	  while (i < ca->createsize) {
88 	    int rc = write(fd, name, sizeof(name));
89 	    if (rc < 0) break;
90 	    i += rc;
91 	  }
92 	}
93 
94 	cb = mmap( NULL, ca->createsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
95 	close(fd);
96 #else
97 
98 #ifndef MAP_ANON
99 #  define MAP_ANON 0
100 #endif
101 	cb = mmap( NULL, ca->createsize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
102 #endif
103 	if (cb == NULL || cb == (char*)-1)
104 	  return -1;
105 
106 	if (ca->cellblocks_count >= CELLBLOCKS_MAX) return -1;
107 
108 	ca->cellblocks[ca->cellblocks_count++] = cb;
109 
110 	for (i = 0; i <= ca->createsize-ca->increment; i += ca->increment) {
111 		struct cellhead *ch = (struct cellhead *)(cb + i); /* pointer arithmentic! */
112 		if (!ca->free_head) {
113 		  ca->free_head = ch;
114 		} else {
115 		  ca->free_tail->next = ch;
116 		}
117 		ca->free_tail = ch;
118 		ch->next = NULL;
119 #if CELLHEAD_DEBUG == 1
120 		ch->ca   = ca; // cellhead pointer space
121 #endif
122 
123 		ca->freecount += 1;
124 	}
125 
126 	// hlog( LOG_DEBUG, "new_cellblock(%p) of %dB freecount %d  returns to %p/%p",
127 	//       ca, ca->cellsize, ca->freecount,
128 	//       __builtin_return_address(1), __builtin_return_address(2) );
129 
130 	return 0;
131 }
132 
133 
134 
135 /*
136  * cellinit()  -- the main program calls this once for each used cell type/size
137  *
138  */
139 
140 
cellinit(const char * arenaname,const int cellsize,const int alignment,const int policy,const int createkb,const int minfree)141 cellarena_t *cellinit( const char *arenaname, const int cellsize, const int alignment, const int policy, const int createkb, const int minfree )
142 {
143 	cellarena_t *ca = hmalloc(sizeof(*ca));
144 	int n;
145 
146 	memset(ca, 0, sizeof(*ca));
147 
148 	ca->arenaname = arenaname;
149 
150 #if CELLHEAD_DEBUG == 1
151 	if (alignment < __alignof__(void*))
152 		alignment = __alignof__(void*);   // cellhead pointer space
153 #endif
154 
155 	ca->cellsize  = cellsize;
156 	ca->alignment = alignment;
157 	ca->minfree   = minfree;
158 #if CELLHEAD_DEBUG == 1
159 	ca->increment = cellsize + sizeof(void*); // cellhead pointer space
160 #else
161 	ca->increment = cellsize;
162 #endif
163 	if ((cellsize % alignment) != 0) {
164 		ca->increment +=  alignment - cellsize % alignment;
165 	}
166 	ca->lifo_policy =  policy & CELLMALLOC_POLICY_LIFO;
167 	ca->use_mutex   = (policy & CELLMALLOC_POLICY_NOMUTEX) ? 0 : 1;
168 
169 	ca->createsize = createkb * 1024;
170 
171 	n = ca->createsize / ca->increment;
172 	hlog( LOG_DEBUG, "cellinit: %-12s block size %4d kB, cells/block: %d, %s", arenaname, createkb, n, ca->use_mutex ? "mutex" : "no mutex" );
173 
174 	pthread_mutex_init(&ca->mutex, NULL);
175 
176 	while (ca->freecount < ca->minfree)
177 		new_cellblock(ca); /* more until minfree is full */
178 
179 #if CELLHEAD_DEBUG == 1
180 	hlog(LOG_DEBUG, "cellinit()  cellhead=%p", ca);
181 #endif
182 	return ca;
183 }
184 
185 
cellhead_to_clientptr(struct cellhead * ch)186 static void *cellhead_to_clientptr(struct cellhead *ch)
187 {
188 	char *p = (char*)ch;
189 #if CELLHEAD_DEBUG == 1
190 	p += sizeof(void*);
191 #endif
192 	return p;
193 }
194 
clientptr_to_cellhead(void * v)195 static struct cellhead *clientptr_to_cellhead(void *v)
196 {
197 #if CELLHEAD_DEBUG == 1
198 	struct cellhead *ch = (struct cellhead *)(((char*)v) - sizeof(void*));
199 #else
200 	struct cellhead *ch = (struct cellhead*)v;
201 #endif
202 	return ch;
203 }
204 
205 
cellmalloc(cellarena_t * ca)206 void *cellmalloc(cellarena_t *ca)
207 {
208 	void *cp;
209 	struct cellhead *ch;
210 	int me;
211 
212 	if (ca->use_mutex) {
213 		if ((me = pthread_mutex_lock(&ca->mutex))) {
214 			hlog(LOG_ERR, "cellmalloc: could not lock mutex: %s", strerror(me));
215 			return NULL;
216 		}
217 	}
218 
219 	while (!ca->free_head  || (ca->freecount < ca->minfree))
220 		if (new_cellblock(ca)) {
221 			if (ca->use_mutex) {
222 				if ((me = pthread_mutex_unlock(&ca->mutex))) {
223 					hlog(LOG_ERR, "cellmalloc: could not unlock mutex: %s", strerror(me));
224 				}
225 			}
226 
227 			return NULL;
228 		}
229 
230 	/* Pick new one off the free-head ! */
231 	ch = ca->free_head;
232 	ca->free_head = ch->next;
233 	ch->next = NULL;
234 	cp = ch;
235 	if (ca->free_head == NULL)
236 	  ca->free_tail = NULL;
237 
238 	ca->freecount -= 1;
239 
240 	if (ca->use_mutex) {
241 		if ((me = pthread_mutex_unlock(&ca->mutex))) {
242 			hlog(LOG_ERR, "cellmalloc: could not unlock mutex: %s", strerror(me));
243 		}
244 	}
245 
246 	// hlog(LOG_DEBUG, "cellmalloc(%p at %p) freecount %d", cellhead_to_clientptr(cp), ca, ca->freecount);
247 
248 	return cellhead_to_clientptr(cp);
249 }
250 
251 /*
252  *  cellmallocmany() -- give many cells in single lock region
253  *
254  */
255 
cellmallocmany(cellarena_t * ca,void ** array,int numcells)256 int   cellmallocmany(cellarena_t *ca, void **array, int numcells)
257 {
258 	int count;
259 	struct cellhead *ch;
260 	int me;
261 
262 	if (ca->use_mutex) {
263 		if ((me = pthread_mutex_lock(&ca->mutex))) {
264 			hlog(LOG_ERR, "cellmallocmany: could not lock mutex: %s", strerror(me));
265 			return 0;
266 		}
267 	}
268 
269 	for (count = 0; count < numcells; ++count) {
270 
271 		while (!ca->free_head ||
272 		       ca->freecount < ca->minfree) {
273 			/* Out of free cells ? alloc new set */
274 			if (new_cellblock(ca)) {
275 				/* Failed ! */
276 				hlog(LOG_ERR, "cellmallocmany: failed to allocate new block!");
277 				break;
278 			}
279 		}
280 
281 		/* Pick new one off the free-head ! */
282 
283 		ch = ca->free_head;
284 
285 		// hlog( LOG_DEBUG, "cellmallocmany(%d of %d); freecount %d; %p at %p",
286 		//       count, numcells, ca->freecount, cellhead_to_clientptr(ch), ca );
287 
288 		if (!ch)
289 		 	break;	// Should not happen...
290 
291 		ca->free_head = ch->next;
292 		ch->next = NULL;
293 
294 		if (ca->free_head == NULL)
295 			ca->free_tail = NULL;
296 
297 		array[count] = cellhead_to_clientptr(ch);
298 
299 		ca->freecount -= 1;
300 
301 	}
302 
303 	if (ca->use_mutex) {
304 		if ((me = pthread_mutex_unlock(&ca->mutex))) {
305 			hlog(LOG_ERR, "cellmallocmany: could not unlock mutex: %s", strerror(me));
306 			return count;
307 		}
308 	}
309 
310 	return count;
311 }
312 
313 
314 
cellfree(cellarena_t * ca,void * p)315 void  cellfree(cellarena_t *ca, void *p)
316 {
317 	int me;
318 	struct cellhead *ch = clientptr_to_cellhead(p);
319 	ch->next = NULL;
320 #if CELLHEAD_DEBUG == 1
321 	if (ch->ca != ca) {
322 	  hlog(LOG_ERR, "cellfree(%p to %p) wrong cellhead->ca pointer %p", p, ca, ch->ca);
323 	}
324 #endif
325 
326 	// hlog(LOG_DEBUG, "cellfree() %p to %p", p, ca);
327 
328 	if (ca->use_mutex) {
329 		if ((me = pthread_mutex_lock(&ca->mutex))) {
330 			hlog(LOG_ERR, "cellfree: could not lock mutex: %s", strerror(me));
331 			return;
332 		}
333 	}
334 
335 	if (ca->lifo_policy) {
336 	  /* Put the cell on free-head */
337 	  ch->next = ca->free_head;
338 	  ca->free_head = ch;
339 
340 	} else {
341 	  /* Put the cell on free-tail */
342 	  if (ca->free_tail)
343 	    ca->free_tail->next = ch;
344 	  ca->free_tail = ch;
345 	  if (!ca->free_head)
346 	    ca->free_head = ch;
347 	  ch->next = NULL;
348 	}
349 
350 	ca->freecount += 1;
351 
352 	if (ca->use_mutex) {
353 		if ((me = pthread_mutex_unlock(&ca->mutex))) {
354 			hlog(LOG_ERR, "cellfree: could not unlock mutex: %s", strerror(me));
355 		}
356 	}
357 }
358 
359 /*
360  *  cellfreemany() -- release many cells in single lock region
361  *
362  */
363 
cellfreemany(cellarena_t * ca,void ** array,int numcells)364 void  cellfreemany(cellarena_t *ca, void **array, int numcells)
365 {
366 	int count;
367 	int me;
368 
369 	if (ca->use_mutex) {
370 		if ((me = pthread_mutex_lock(&ca->mutex))) {
371 			hlog(LOG_ERR, "cellfreemany: could not lock mutex: %s", strerror(me));
372 			return;
373 		}
374 	}
375 
376 	for (count = 0; count < numcells; ++count) {
377 
378 	  struct cellhead *ch = clientptr_to_cellhead(array[count]);
379 
380 #if CELLHEAD_DEBUG == 1
381 	  if (ch->ca != ca) {
382 	    hlog(LOG_ERR, "cellfreemany(%p to %p) wrong cellhead->ca pointer %p", array[count], ca, ch->ca);
383 	  }
384 #endif
385 
386 	  // hlog(LOG_DEBUG, "cellfreemany() %p to %p", ch, ca);
387 
388 	  if (ca->lifo_policy) {
389 	    /* Put the cell on free-head */
390 	    ch->next = ca->free_head;
391 	    ca->free_head = ch;
392 
393 	  } else {
394 	    /* Put the cell on free-tail */
395 	    if (ca->free_tail)
396 	      ca->free_tail->next = ch;
397 	    ca->free_tail = ch;
398 	    if (!ca->free_head)
399 	      ca->free_head = ch;
400 	    ch->next = NULL;
401 	  }
402 
403 	  ca->freecount += 1;
404 
405 	}
406 
407 	if (ca->use_mutex) {
408 		if ((me = pthread_mutex_unlock(&ca->mutex))) {
409 			hlog(LOG_ERR, "cellfreemany: could not unlock mutex: %s", strerror(me));
410 		}
411 	}
412 }
413 
cellstatus(cellarena_t * cellarena,struct cellstatus_t * status)414 void  cellstatus(cellarena_t *cellarena, struct cellstatus_t *status)
415 {
416 	/* TODO: try this for atomic cellstatus collection:
417 	if (ca->use_mutex)
418 		pthread_mutex_lock(&ca->mutex);
419 	*/
420 	status->cellsize = cellarena->cellsize;
421 	status->cellsize_aligned = cellarena->increment;
422 	status->alignment = cellarena->alignment;
423 	status->freecount = cellarena->freecount;
424 	status->cellcount = (cellarena->createsize / cellarena->increment) * cellarena->cellblocks_count;
425 	status->blocks = cellarena->cellblocks_count;
426 	status->blocks_max = CELLBLOCKS_MAX;
427 	status->block_size = cellarena->createsize;
428 	/* and this:
429 	if (ca->use_mutex)
430 		pthread_mutex_unlock(&ca->mutex);
431 	*/
432 }
433 
434 #endif /* (NOT) _FOR_VALGRIND_ */
435 
436