PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/lkcd-7.0.1/lkcdutils/lib/liballoc/alloc.c

#
C | 601 lines | 452 code | 47 blank | 102 comment | 72 complexity | 46a48b68c7584b9725f69f00323c1889 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.1, LGPL-2.0
  1. /*
  2. * $Id: alloc.c 1246 2006-01-11 19:19:24Z tjm $
  3. *
  4. * This file is part of liballoc.
  5. * A library which provides memory allocation facilities.
  6. *
  7. * Created by Silicon Graphics, Inc.
  8. *
  9. * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved.
  10. *
  11. * This code is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU Lesser Public License as published by
  13. * the Free Software Foundation; either version 2.1 of the License, or
  14. * (at your option) any later version. See the file COPYING for more
  15. * information.
  16. */
  17. #include <stdio.h>
  18. #include <signal.h>
  19. #include <sys/types.h>
  20. #include <sys/param.h>
  21. #ifdef ALLOC_DEBUG
  22. #include <assert.h>
  23. #endif
  24. #include <kl_lib.h>
  25. #include <alloc.h>
  26. int alloc_debug = 0;
  27. /* All chunks of memory are strung together via the next and prev
  28. * links.
  29. */
  30. typedef struct chunk_s {
  31. struct chunk_s *next; /* Must be first */
  32. struct chunk_s *prev; /* Must be second */
  33. void *addr;
  34. struct bucket_s *bucketp;
  35. uint32_t chunksz; /* size of memory chunk (via malloc()) */
  36. uint32_t blksz; /* Not including header */
  37. short blkcount; /* Number of blksz blocks in chunk */
  38. #ifdef ALLOC_DEBUG
  39. short blksfree; /* On bucket freelist */
  40. uint64_t chunk_magic;
  41. #endif
  42. } chunk_t;
  43. #ifdef ALLOC_DEBUG
  44. #define CHUNK_MAGIC 0x1baddeed
  45. #endif
  46. /* Header struct used when chaining free blocks together. The chunkp
  47. * pointer always contains a pointer to the chunk the block was allocated
  48. * from even when the block is on a freelist.
  49. */
  50. typedef struct blkhdr_s {
  51. struct blkhdr_s *next;
  52. union {
  53. struct blkhdr_s *prev;
  54. chunk_t *chunkp;
  55. } b_un;
  56. int flg;
  57. int size;
  58. #ifdef ALLOC_DEBUG
  59. void *alloc_pc; /* program counter of calling function */
  60. #endif
  61. } blkhdr_t;
  62. #define b_chunkp b_un.chunkp
  63. /* Flag value that is placed in the block header to indicate if a block
  64. * was allocated as a B_PERM or B_TEMP block.
  65. */
  66. #define PERMBLK 0x00123456
  67. #define TEMPBLK 0x00654321
  68. #define FREEFLG 0x10000000
  69. #define IS_PERMBLK(blk) (((unsigned long)blk->flg & 0xffffff) == PERMBLK)
  70. #define IS_TEMPBLK(blk) (((unsigned long)blk->flg & 0xffffff) == TEMPBLK)
  71. #ifdef ALLOC_DEBUG
  72. static blkhdr_t *permblk_list = (blkhdr_t *)NULL;
  73. #endif
  74. /* Make sure the block header size is 64-bit aligned
  75. */
  76. #define BLKHDR_SZ ((sizeof(blkhdr_t)/8 * 8) + (sizeof(blkhdr_t)%8 ? 8 : 0))
  77. static int blkhdr_sz = BLKHDR_SZ;
  78. /* Macros that return pointers to various parts of block
  79. */
  80. #define BLK_DATA(blk) ((void *)((unsigned long)blk + blkhdr_sz))
  81. #define BLK_HDR(blk) ((blkhdr_t*)((unsigned long)blk - blkhdr_sz))
  82. #define LASTBLK(cp) ((blkhdr_t *)((unsigned long)cp->addr + \
  83. ((cp->blkcount - 1) * (cp->blksz + blkhdr_sz))))
  84. #define NEXTBLK(blk, cp) ((blkhdr_t *)((unsigned long)blk + \
  85. (cp->blksz + blkhdr_sz)))
  86. typedef struct bucket_s {
  87. int blksize;
  88. int chunkcnt;
  89. chunk_t *chunk_list;
  90. blkhdr_t *freeblks;
  91. #ifdef ALLOC_DEBUG
  92. int blks_alloced;
  93. int bad_allocs;
  94. int blks_high;
  95. int alloc_calls;
  96. int free_calls;
  97. #endif
  98. } bucket_t;
  99. typedef struct bucket_rec_s {
  100. int size;
  101. int blks_per_chunk;
  102. } bucket_rec_t;
  103. #define NBUCKETS 14
  104. #define OVERSZ -1
  105. #ifdef ALLOC_DEBUG
  106. bucket_t bucket[NBUCKETS] = {
  107. { /* 0 */ 8, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  108. { /* 1 */ 16, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  109. { /* 2 */ 24, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  110. { /* 3 */ 32, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  111. { /* 4 */ 48, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  112. { /* 5 */ 64, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  113. { /* 6 */ 96, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  114. { /* 7 */ 128, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  115. { /* 8 */ 192, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  116. { /* 9 */ 256, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  117. { /* 10 */ 384, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  118. { /* 11 */ 512, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  119. { /* 12 */ 1024, 0, NULL, NULL, 0, 0, 0, 0, 0 },
  120. { /* 13 */ OVERSZ, 0, NULL, NULL, 0, 0, 0, 0, 0 }
  121. };
  122. #else
  123. bucket_t bucket[NBUCKETS] = {
  124. { /* 0 */ 8, 0, NULL, NULL },
  125. { /* 1 */ 16, 0, NULL, NULL },
  126. { /* 2 */ 24, 0, NULL, NULL },
  127. { /* 3 */ 32, 0, NULL, NULL },
  128. { /* 4 */ 48, 0, NULL, NULL },
  129. { /* 5 */ 64, 0, NULL, NULL },
  130. { /* 6 */ 96, 0, NULL, NULL },
  131. { /* 7 */ 128, 0, NULL, NULL },
  132. { /* 8 */ 192, 0, NULL, NULL },
  133. { /* 9 */ 256, 0, NULL, NULL },
  134. { /* 10 */ 384, 0, NULL, NULL },
  135. { /* 11 */ 512, 0, NULL, NULL },
  136. { /* 12 */ 1024, 0, NULL, NULL },
  137. { /* 13 */ OVERSZ, 0, NULL, NULL }
  138. };
  139. #endif
  140. bucket_rec_t bucket_size[] = {
  141. /* INDEX BLKSZ BLKS/CHUNK */
  142. { /* 0 */ 8, 64 },
  143. { /* 1 */ 16, 64 },
  144. { /* 2 */ 24, 32 },
  145. { /* 3 */ 32, 16 },
  146. { /* 4 */ 48, 16 },
  147. { /* 5 */ 64, 16 },
  148. { /* 6 */ 96, 16 },
  149. { /* 7 */ 128, 16 },
  150. { /* 8 */ 192, 16 },
  151. { /* 9 */ 256, 8 },
  152. { /* 10 */ 384, 4 },
  153. { /* 11 */ 512, 4 },
  154. { /* 12 */ 1024, 2 },
  155. { /* 13 */ OVERSZ, 1 }
  156. };
  157. /* set_aloc_debug()
  158. */
  159. int
  160. set_alloc_debug(int adb)
  161. {
  162. alloc_debug = adb;
  163. return 0;
  164. }
  165. /* Forward declarations
  166. */
  167. void print_blk_data(char *, blkhdr_t *);
  168. /*
  169. * hold_signals() -- Hold signals in critical blocks of code
  170. */
  171. static void
  172. hold_signals(void)
  173. {
  174. sighold((int)SIGINT);
  175. sighold((int)SIGPIPE);
  176. }
  177. /*
  178. * release_signals() -- Allow signals again
  179. */
  180. static void
  181. release_signals(void)
  182. {
  183. sigrelse((int)SIGINT);
  184. sigrelse((int)SIGPIPE);
  185. }
  186. /*
  187. * setup_chunk()
  188. */
  189. static void
  190. setup_chunk(chunk_t *cp)
  191. {
  192. int i = 0;
  193. blkhdr_t *blk, *nextblk;
  194. #ifdef ALLOC_DEBUG
  195. cp->blksfree = cp->blkcount;
  196. #endif
  197. memset(cp->addr, 0, cp->chunksz);
  198. blk = cp->addr;
  199. while (i < cp->blkcount) {
  200. blk->b_chunkp = cp;
  201. nextblk = NEXTBLK(blk, cp);
  202. i++;
  203. if (i < cp->blkcount) {
  204. blk->next = nextblk;
  205. } else {
  206. blk->next = NULL;
  207. }
  208. blk = nextblk;
  209. }
  210. }
  211. /*
  212. * alloc_chunk()
  213. */
  214. static chunk_t *
  215. alloc_chunk(int index, int size)
  216. {
  217. chunk_t *cp;
  218. cp = (chunk_t *)malloc(sizeof(chunk_t));
  219. memset(cp, 0, sizeof(chunk_t));
  220. if (size) {
  221. cp->blksz = size;
  222. } else {
  223. cp->blksz = bucket_size[index].size;
  224. }
  225. cp->blkcount = bucket_size[index].blks_per_chunk;
  226. #ifdef ALLOC_DEBUG
  227. cp->blksfree = cp->blkcount;
  228. cp->chunk_magic = CHUNK_MAGIC;
  229. #endif
  230. cp->bucketp = &bucket[index];
  231. cp->chunksz = ((cp->blksz + blkhdr_sz) * cp->blkcount);
  232. cp->addr = (void *)malloc(cp->chunksz);
  233. setup_chunk(cp);
  234. ENQUEUE(&bucket[index].chunk_list, cp);
  235. bucket[index].chunkcnt++;
  236. return(cp);
  237. }
  238. #ifdef NOTYET
  239. /*
  240. * free_chunk()
  241. */
  242. static void
  243. free_chunk(chunk_t *cp)
  244. {
  245. #ifdef ALLOC_DEBUG
  246. if (!cp || (cp->chunk_magic != CHUNK_MAGIC)) {
  247. return;
  248. }
  249. #endif
  250. if (cp->addr) {
  251. free(cp->addr);
  252. }
  253. free(cp);
  254. }
  255. #endif
  256. /*
  257. * alloc_temp_blk()
  258. */
  259. static blkhdr_t *
  260. alloc_temp_blk(int size, void *ra)
  261. {
  262. int i;
  263. chunk_t *cp;
  264. blkhdr_t *blk = NULL;
  265. /* Find the bucket with the closest fit (next largest) block
  266. * size. Then, get a block from that bucket. Note that we do
  267. * not search into the OVERSZ bucket.
  268. */
  269. for (i = 0; i < (NBUCKETS - 1); i++) {
  270. if (bucket[i].blksize < size) {
  271. continue;
  272. }
  273. break;
  274. }
  275. /* Check to see if this is an oversize block. If it isn't then
  276. * check to see if there are any blocks on the freelist. If there
  277. * aren't, then get the next new block from the chunk.
  278. */
  279. if (bucket[i].blksize == OVERSZ) {
  280. blkhdr_t *lastblk = NULL;
  281. if ((blk = bucket[i].freeblks)) {
  282. while (blk) {
  283. cp = blk->b_chunkp;
  284. if (cp->chunksz >= size) {
  285. break;
  286. }
  287. lastblk = blk;
  288. blk = blk->next;
  289. }
  290. }
  291. if (blk) {
  292. if (lastblk) {
  293. lastblk->next = blk->next;
  294. } else {
  295. bucket[i].freeblks = blk->next;
  296. }
  297. } else {
  298. cp = alloc_chunk(i, size);
  299. blk = (blkhdr_t *)cp->addr;
  300. }
  301. } else if ((blk = bucket[i].freeblks)) {
  302. cp = blk->b_chunkp;
  303. bucket[i].freeblks = blk->next;
  304. } else {
  305. /* We have to allocate a new chunk
  306. */
  307. cp = alloc_chunk(i, 0);
  308. blk = (blkhdr_t *)cp->addr;
  309. bucket[i].freeblks = blk->next;
  310. }
  311. /* If we got a block, fill in the rest of the details
  312. */
  313. if (blk) {
  314. memset(blk, 0, (cp->blksz + blkhdr_sz));
  315. blk->b_chunkp = cp;
  316. blk->flg = TEMPBLK;
  317. blk->size = size;
  318. #ifdef ALLOC_DEBUG
  319. cp->blksfree--;
  320. blk->alloc_pc = ra;
  321. bucket[i].alloc_calls++;
  322. bucket[i].blks_alloced++;
  323. if (bucket[i].blks_alloced > bucket[i].blks_high) {
  324. bucket[i].blks_high = bucket[i].blks_alloced;
  325. }
  326. } else {
  327. bucket[i].bad_allocs++;
  328. if (alloc_debug) {
  329. fprintf(stderr, "alloc_temp_block: Could not allocate "
  330. "a block of size %d\n", size);
  331. }
  332. #endif
  333. }
  334. return(blk);
  335. }
  336. /*
  337. * alloc_block() -- Allocate a block of memory.
  338. *
  339. */
  340. void *
  341. alloc_block(int size, int flag, void *ra)
  342. {
  343. blkhdr_t *blk;
  344. void *b;
  345. if (size == 0) {
  346. return((void *)NULL);
  347. }
  348. hold_signals();
  349. if (flag == B_PERM) {
  350. blk = (void*)malloc(size + blkhdr_sz);
  351. memset(blk, 0, (size + blkhdr_sz));
  352. blk->flg = PERMBLK;
  353. blk->size = size;
  354. #ifdef ALLOC_DEBUG
  355. blk->alloc_pc = ra;
  356. ENQUEUE(&permblk_list, blk);
  357. #endif
  358. } else {
  359. blk = alloc_temp_blk(size, ra);
  360. }
  361. if (blk) {
  362. if (alloc_debug > 1) {
  363. print_blk_data("alloc_block", blk);
  364. }
  365. b = BLK_DATA(blk);
  366. }
  367. release_signals();
  368. return(b);
  369. }
  370. /*
  371. * realloc_block()
  372. */
  373. void *
  374. realloc_block(void *b, int new_size, int flag, void *ra)
  375. {
  376. void *b2;
  377. blkhdr_t *blk = BLK_HDR(b);
  378. if ((b2 = alloc_block(new_size, flag, ra))) {
  379. bcopy(b, b2, blk->size);
  380. free_block(b);
  381. }
  382. return(b2);
  383. }
  384. /*
  385. * dup_block()
  386. */
  387. void *
  388. dup_block(void *b, int flag, void *ra)
  389. {
  390. void *b2;
  391. blkhdr_t *blk = BLK_HDR(b);
  392. if ((b2 = alloc_block(blk->size, flag, ra))) {
  393. bcopy(b, b2, blk->size);
  394. }
  395. return(b2);
  396. }
  397. /*
  398. * str_to_block()
  399. */
  400. void *
  401. str_to_block(char *s, int flag, void *ra)
  402. {
  403. int size;
  404. void *b;
  405. size = strlen(s) + 1;
  406. b = alloc_block(size, flag, ra);
  407. bcopy(s, b, size);
  408. return(b);
  409. }
  410. /*
  411. * free_block()
  412. */
  413. void
  414. free_block(void *b)
  415. {
  416. chunk_t *cp;
  417. blkhdr_t *blk = BLK_HDR(b);
  418. if (alloc_debug > 1) {
  419. print_blk_data("free_block", blk);
  420. }
  421. if (IS_PERMBLK(blk)) {
  422. #ifdef ALLOC_DEBUG
  423. if (blk->flg & FREEFLG) {
  424. print_blk_data("free_block (perm)", blk);
  425. } else {
  426. blk->flg |= FREEFLG;
  427. }
  428. REMQUEUE(&permblk_list, blk);
  429. #endif
  430. free((void*)blk);
  431. } else if (IS_TEMPBLK(blk)) {
  432. cp = blk->b_chunkp;
  433. #ifdef ALLOC_DEBUG
  434. if (blk->flg & FREEFLG) {
  435. print_blk_data("free_block (temp)", blk);
  436. assert(!(blk->flg & FREEFLG));
  437. } else {
  438. blk->flg |= FREEFLG;
  439. }
  440. cp->bucketp->free_calls++;
  441. cp->bucketp->blks_alloced--;
  442. #endif
  443. blk->next = cp->bucketp->freeblks;
  444. cp->bucketp->freeblks = blk;
  445. #ifdef ALLOC_DEBUG
  446. cp->blksfree++;
  447. #endif
  448. } else {
  449. print_blk_data("free_block", blk);
  450. }
  451. }
  452. /*
  453. * print_blk_data()
  454. */
  455. void
  456. print_blk_data(char *s, blkhdr_t *blk)
  457. {
  458. #ifdef ALLOC_DEBUG
  459. if (alloc_debug) {
  460. fprintf(stdout, "%s: blk=0x%lx: flg=0x%lx, alloc_pc=0x%lx\n",
  461. s, blk, blk->flg, blk->alloc_pc);
  462. }
  463. #endif
  464. }
  465. /*
  466. * free_temp_blocks() -- Free all temporarily allocated blocks
  467. */
  468. void
  469. free_temp_blocks(void)
  470. {
  471. int i;
  472. blkhdr_t *blk;
  473. chunk_t *cp;
  474. /* Now walk through the buckets and clean things up there
  475. */
  476. for (i = 0; i < NBUCKETS; i++) {
  477. /* Clean up the chunks for this bucket and rechain all
  478. * of the blocks onto a linked list.
  479. */
  480. cp = bucket[i].chunk_list;
  481. while(cp) {
  482. #ifdef ALLOC_DEBUG
  483. /* Walk through the blocks in this chunk and
  484. * print out details for any blocks that had not
  485. * been freed properly
  486. */
  487. blk = cp->addr;
  488. while (1) {
  489. if (blk->flg == TEMPBLK) {
  490. print_blk_data("free_temp_blocks", blk);
  491. }
  492. if (blk == LASTBLK(cp)) {
  493. break;
  494. }
  495. blk = NEXTBLK(blk, cp);
  496. }
  497. #endif
  498. setup_chunk(cp);
  499. if ((cp = cp->next) == bucket[i].chunk_list) {
  500. break;
  501. }
  502. }
  503. if (bucket[i].blksize == OVERSZ) {
  504. /* Make sure all the oversize bloks are linked
  505. * on the freelist.
  506. */
  507. cp = bucket[i].chunk_list;
  508. bucket[i].freeblks = NULL;
  509. blk = (blkhdr_t *)NULL;
  510. while (cp) {
  511. if(blk) {
  512. blk->next = cp->addr;
  513. blk = blk->next;
  514. } else {
  515. bucket[i].freeblks = cp->addr;
  516. blk = cp->addr;
  517. }
  518. cp = cp->next;
  519. if (cp == bucket[i].chunk_list) {
  520. break;
  521. }
  522. }
  523. if (blk) {
  524. blk->next = NULL;
  525. }
  526. continue;
  527. }
  528. bucket[i].freeblks = NULL;
  529. if ((cp = bucket[i].chunk_list)) {
  530. bucket[i].freeblks = cp->addr;
  531. while (cp->next != bucket[i].chunk_list) {
  532. LASTBLK(cp)->next = cp->next->addr;
  533. cp = cp->next;
  534. }
  535. }
  536. }
  537. }
  538. /*
  539. * is_temp_block()
  540. */
  541. int
  542. is_temp_block(void *p)
  543. {
  544. blkhdr_t *blk = BLK_HDR(p);
  545. #ifdef ALLOC_DEBUG
  546. if ((blk->flg == TEMPBLK) &&
  547. (blk->b_chunkp->chunk_magic == CHUNK_MAGIC)) {
  548. #else
  549. if (blk->flg == TEMPBLK) {
  550. #endif
  551. return(1);
  552. }
  553. return(0);
  554. }