qmalloc.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. /*
  10. * malloc
  11. *
  12. * Uses Quickfit (see SIGPLAN Notices October 1988)
  13. * with allocator from Kernighan & Ritchie
  14. *
  15. * This is a placeholder.
  16. */
  17. #include "u.h"
  18. #include "../port/lib.h"
  19. #include "mem.h"
  20. #include "dat.h"
  21. #include "fns.h"
  22. #include <pool.h>
  23. typedef double Align;
  24. typedef union Header Header;
  25. typedef struct Qlist Qlist;
  26. union Header {
  27. struct {
  28. Header *next;
  29. u32 size;
  30. } s;
  31. Align al;
  32. };
  33. struct Qlist {
  34. Lock lk;
  35. Header *first;
  36. u32 nalloc;
  37. };
  38. enum {
  39. Unitsz = sizeof(Header), /* 16 bytes on amd64 */
  40. };
  41. #define NUNITS(n) (HOWMANY(n, Unitsz) + 1)
  42. #define NQUICK ((512 / Unitsz) + 1) /* 33 on amd64 */
  43. static Qlist quicklist[NQUICK + 1];
  44. static Header misclist;
  45. static Header *rover;
  46. static unsigned tailsize;
  47. static unsigned tailnunits;
  48. static Header *tailbase;
  49. static Header *tailptr;
  50. static Header checkval;
  51. static int morecore(unsigned);
  52. enum {
  53. QSmalign = 0,
  54. QSmalignquick,
  55. QSmalignrover,
  56. QSmalignfront,
  57. QSmalignback,
  58. QSmaligntail,
  59. QSmalignnottail,
  60. QSmalloc,
  61. QSmallocrover,
  62. QSmalloctail,
  63. QSfree,
  64. QSfreetail,
  65. QSfreequick,
  66. QSfreenext,
  67. QSfreeprev,
  68. QSmax
  69. };
  70. static void qfreeinternal(void *);
  71. static int qstats[QSmax];
  72. static char *qstatstr[QSmax] = {
  73. [QSmalign] = "malign",
  74. [QSmalignquick] = "malignquick",
  75. [QSmalignrover] = "malignrover",
  76. [QSmalignfront] = "malignfront",
  77. [QSmalignback] = "malignback",
  78. [QSmaligntail] = "maligntail",
  79. [QSmalignnottail] = "malignnottail",
  80. [QSmalloc] = "malloc",
  81. [QSmallocrover] = "mallocrover",
  82. [QSmalloctail] = "malloctail",
  83. [QSfree] = "free",
  84. [QSfreetail] = "freetail",
  85. [QSfreequick] = "freequick",
  86. [QSfreenext] = "freenext",
  87. [QSfreeprev] = "freeprev",
  88. };
  89. static Lock mainlock;
  90. #define MLOCK ilock(&mainlock)
  91. #define MUNLOCK iunlock(&mainlock)
  92. #define QLOCK(l) ilock(l)
  93. #define QUNLOCK(l) iunlock(l)
  94. #define tailalloc(p, n) ((p) = tailptr, tailsize -= (n), tailptr += (n), \
  95. (p)->s.size = (n), (p)->s.next = &checkval)
  96. #define ISPOWEROF2(x) (/*((x) != 0) && */ !((x) & ((x)-1)))
  97. #define ALIGNHDR(h, a) (Header *)((((usize)(h)) + ((a)-1)) & ~((a)-1))
  98. /*
  99. * From libc malloc.c to *draw devices
  100. */
  101. typedef struct Private Private;
  102. struct Private {
  103. Lock lk;
  104. char *end;
  105. char msg[256]; /* a rock for messages to be printed at unlock */
  106. };
  107. /*
  108. * Experiment: per-core quick lists.
  109. * change quicklist to be
  110. * static Qlist quicklist[MACHMAX][NQUICK+1];
  111. * and define QLIST to be quicklist[machp()->machno]
  112. *
  113. * using quicklist[machp()->machno] runs out of memory soon.
  114. * using quicklist[machp()->machno%4] yields times worse than using quicklist!
  115. */
  116. #define QLIST quicklist
  117. static void *
  118. qmallocalign(usize nbytes, usize align, i32 offset, usize span)
  119. {
  120. Qlist *qlist;
  121. usize aligned;
  122. Header **pp, *p, *q, *r;
  123. u32 naligned, nunits, n;
  124. if(nbytes == 0 || offset != 0 || span != 0)
  125. return nil;
  126. if(!ISPOWEROF2(align) || align < sizeof(Header))
  127. return nil;
  128. qstats[QSmalign]++;
  129. nunits = NUNITS(nbytes);
  130. if(nunits <= NQUICK){
  131. /*
  132. * Look for a conveniently aligned block
  133. * on one of the quicklists.
  134. */
  135. qlist = &QLIST[nunits];
  136. QLOCK(&qlist->lk);
  137. pp = &qlist->first;
  138. for(p = *pp; p != nil; p = p->s.next){
  139. if(ALIGNED(p + 1, align)){
  140. *pp = p->s.next;
  141. p->s.next = &checkval;
  142. QUNLOCK(&qlist->lk);
  143. qstats[QSmalignquick]++;
  144. return p + 1;
  145. }
  146. pp = &p->s.next;
  147. }
  148. QUNLOCK(&qlist->lk);
  149. }
  150. MLOCK;
  151. if(nunits > tailsize){
  152. /* hard way */
  153. if((q = rover) != nil){
  154. do {
  155. p = q->s.next;
  156. if(p->s.size < nunits)
  157. continue;
  158. aligned = ALIGNED(p + 1, align);
  159. naligned = NUNITS(align) - 1;
  160. if(!aligned && p->s.size < nunits + naligned)
  161. continue;
  162. /*
  163. * This block is big enough, remove it
  164. * from the list.
  165. */
  166. q->s.next = p->s.next;
  167. rover = q;
  168. qstats[QSmalignrover]++;
  169. /*
  170. * Free any runt in front of the alignment.
  171. */
  172. if(!aligned){
  173. r = p;
  174. p = ALIGNHDR(p + 1, align) - 1;
  175. n = p - r;
  176. p->s.size = r->s.size - n;
  177. r->s.size = n;
  178. r->s.next = &checkval;
  179. qfreeinternal(r + 1);
  180. qstats[QSmalignfront]++;
  181. }
  182. /*
  183. * Free any residue after the aligned block.
  184. */
  185. if(p->s.size > nunits){
  186. r = p + nunits;
  187. r->s.size = p->s.size - nunits;
  188. r->s.next = &checkval;
  189. qstats[QSmalignback]++;
  190. qfreeinternal(r + 1);
  191. p->s.size = nunits;
  192. }
  193. p->s.next = &checkval;
  194. MUNLOCK;
  195. return p + 1;
  196. } while((q = p) != rover);
  197. }
  198. if((n = morecore(nunits)) == 0){
  199. MUNLOCK;
  200. return nil;
  201. }
  202. tailsize += n;
  203. }
  204. q = ALIGNHDR(tailptr + 1, align);
  205. if(q == tailptr + 1){
  206. tailalloc(p, nunits);
  207. qstats[QSmaligntail]++;
  208. } else {
  209. naligned = NUNITS(align) - 1;
  210. if(tailsize < nunits + naligned){
  211. /*
  212. * There are at least nunits,
  213. * get enough for alignment.
  214. */
  215. if((n = morecore(naligned)) == 0){
  216. MUNLOCK;
  217. return nil;
  218. }
  219. tailsize += n;
  220. }
  221. /*
  222. * Save the residue before the aligned allocation
  223. * and free it after the tail pointer has been bumped
  224. * for the main allocation.
  225. */
  226. n = q - tailptr - 1;
  227. tailalloc(r, n);
  228. tailalloc(p, nunits);
  229. qstats[QSmalignnottail]++;
  230. qfreeinternal(r + 1);
  231. }
  232. MUNLOCK;
  233. return p + 1;
  234. }
  235. /* Force it to allocate on 64 byte boundaries. I want to guarantee this happens as
  236. * I don't want to take alignment traps. */
  237. static void *
  238. qmalloc(usize nbytes)
  239. {
  240. return qmallocalign(nbytes, 64, 0, 0);
  241. }
  242. static void
  243. qfreeinternal(void *ap)
  244. {
  245. Qlist *qlist;
  246. Header *p, *q;
  247. u32 nunits;
  248. if(ap == nil)
  249. return;
  250. qstats[QSfree]++;
  251. p = (Header *)ap - 1;
  252. if((nunits = p->s.size) == 0 || p->s.next != &checkval)
  253. panic("malloc: corrupt allocation arena\n");
  254. if(tailptr != nil && p + nunits == tailptr){
  255. /* block before tail */
  256. tailptr = p;
  257. tailsize += nunits;
  258. qstats[QSfreetail]++;
  259. return;
  260. }
  261. if(nunits <= NQUICK){
  262. qlist = &QLIST[nunits];
  263. QLOCK(&qlist->lk);
  264. p->s.next = qlist->first;
  265. qlist->first = p;
  266. QUNLOCK(&qlist->lk);
  267. qstats[QSfreequick]++;
  268. return;
  269. }
  270. if((q = rover) == nil){
  271. q = &misclist;
  272. q->s.size = 0;
  273. q->s.next = q;
  274. }
  275. for(; !(p > q && p < q->s.next); q = q->s.next)
  276. if(q >= q->s.next && (p > q || p < q->s.next))
  277. break;
  278. if(p + p->s.size == q->s.next){
  279. p->s.size += q->s.next->s.size;
  280. p->s.next = q->s.next->s.next;
  281. qstats[QSfreenext]++;
  282. } else
  283. p->s.next = q->s.next;
  284. if(q + q->s.size == p){
  285. q->s.size += p->s.size;
  286. q->s.next = p->s.next;
  287. qstats[QSfreeprev]++;
  288. } else
  289. q->s.next = p;
  290. rover = q;
  291. }
  292. u32
  293. msize(void *ap)
  294. {
  295. Header *p;
  296. u32 nunits;
  297. if(ap == nil)
  298. return 0;
  299. p = (Header *)ap - 1;
  300. if((nunits = p->s.size) == 0 || p->s.next != &checkval)
  301. panic("malloc: corrupt allocation arena\n");
  302. return (nunits - 1) * sizeof(Header);
  303. }
  304. static void
  305. mallocreadfmt(char *s, char *e)
  306. {
  307. char *p;
  308. Header *q;
  309. int i, n, t;
  310. Qlist *qlist;
  311. p = seprint(s, e,
  312. "%llu memory\n"
  313. "%d pagesize\n"
  314. "%llu kernel\n",
  315. (u64)conf.npage * PGSZ,
  316. PGSZ,
  317. (u64)conf.npage - conf.upages);
  318. t = 0;
  319. for(i = 0; i <= NQUICK; i++){
  320. n = 0;
  321. qlist = &QLIST[i];
  322. QLOCK(&qlist->lk);
  323. for(q = qlist->first; q != nil; q = q->s.next){
  324. // if(q->s.size != i)
  325. // p = seprint(p, e, "q%d\t%#p\t%u\n",
  326. // i, q, q->s.size);
  327. n++;
  328. }
  329. QUNLOCK(&qlist->lk);
  330. // if(n != 0)
  331. // p = seprint(p, e, "q%d %d\n", i, n);
  332. t += n * i * sizeof(Header);
  333. }
  334. p = seprint(p, e, "quick: %u bytes total\n", t);
  335. MLOCK;
  336. if((q = rover) != nil){
  337. i = t = 0;
  338. do {
  339. t += q->s.size;
  340. i++;
  341. // p = seprint(p, e, "m%d\t%#p\n", q->s.size, q);
  342. } while((q = q->s.next) != rover);
  343. p = seprint(p, e, "rover: %d blocks %u bytes total\n",
  344. i, t * sizeof(Header));
  345. }
  346. p = seprint(p, e, "total allocated %lu, %u remaining\n",
  347. (tailptr - tailbase) * sizeof(Header), tailnunits * sizeof(Header));
  348. for(i = 0; i < nelem(qstats); i++){
  349. if(qstats[i] == 0)
  350. continue;
  351. p = seprint(p, e, "%s %u\n", qstatstr[i], qstats[i]);
  352. }
  353. MUNLOCK;
  354. }
  355. i32
  356. mallocreadsummary(Chan *c, void *a, i32 n, i32 offset)
  357. {
  358. char *alloc;
  359. alloc = malloc(16 * READSTR);
  360. mallocreadfmt(alloc, alloc + 16 * READSTR);
  361. panic("mallocreadsummary"); //n = readstr(offset, a, n, alloc);
  362. free(alloc);
  363. return n;
  364. }
  365. void
  366. mallocsummary(void)
  367. {
  368. Header *q;
  369. int i, n, t;
  370. Qlist *qlist;
  371. t = 0;
  372. for(i = 0; i <= NQUICK; i++){
  373. n = 0;
  374. qlist = &QLIST[i];
  375. QLOCK(&qlist->lk);
  376. for(q = qlist->first; q != nil; q = q->s.next){
  377. if(q->s.size != i)
  378. DBG("q%d\t%#p\t%u\n", i, q, q->s.size);
  379. n++;
  380. }
  381. QUNLOCK(&qlist->lk);
  382. t += n * i * sizeof(Header);
  383. }
  384. print("quick: %u bytes total\n", t);
  385. MLOCK;
  386. if((q = rover) != nil){
  387. i = t = 0;
  388. do {
  389. t += q->s.size;
  390. i++;
  391. } while((q = q->s.next) != rover);
  392. }
  393. MUNLOCK;
  394. if(i != 0){
  395. print("rover: %d blocks %u bytes total\n",
  396. i, t * sizeof(Header));
  397. }
  398. print("total allocated %lu, %u remaining\n",
  399. (tailptr - tailbase) * sizeof(Header), tailnunits * sizeof(Header));
  400. for(i = 0; i < nelem(qstats); i++){
  401. if(qstats[i] == 0)
  402. continue;
  403. print("%s %u\n", qstatstr[i], qstats[i]);
  404. }
  405. }
  406. void
  407. free(void *ap)
  408. {
  409. MLOCK;
  410. qfreeinternal(ap);
  411. MUNLOCK;
  412. }
  413. void *
  414. malloc(u32 size)
  415. {
  416. void *v;
  417. if((v = qmalloc(size)) != nil)
  418. memset(v, 0, size);
  419. return v;
  420. }
  421. void *
  422. mallocz(u32 size, int clr)
  423. {
  424. void *v;
  425. if((v = malloc(size)) != nil && clr)
  426. memset(v, 0, size);
  427. return v;
  428. }
  429. void *
  430. mallocalign(u32 nbytes, u32 align, i32 offset, u32 span)
  431. {
  432. void *v;
  433. /*
  434. * Should this memset or should it be left to the caller?
  435. */
  436. if((v = qmallocalign(nbytes, align, offset, span)) != nil)
  437. memset(v, 0, nbytes);
  438. return v;
  439. }
  440. void *
  441. smalloc(u32 size)
  442. {
  443. Proc *up = externup();
  444. void *v;
  445. while((v = mallocz(size, 1)) == nil)
  446. tsleep(&up->sleep, return0, 0, 100);
  447. return v;
  448. }
  449. void *
  450. realloc(void *ap, u32 size)
  451. {
  452. void *v;
  453. Header *p;
  454. u32 osize;
  455. u32 nunits, ounits;
  456. /*
  457. * Easy stuff:
  458. * free and return nil if size is 0
  459. * (implementation-defined behaviour);
  460. * behave like malloc if ap is nil;
  461. * check for arena corruption;
  462. * do nothing if units are the same.
  463. */
  464. if(size == 0){
  465. MLOCK;
  466. qfreeinternal(ap);
  467. MUNLOCK;
  468. return nil;
  469. }
  470. if(ap == nil)
  471. return qmalloc(size);
  472. p = (Header *)ap - 1;
  473. if((ounits = p->s.size) == 0 || p->s.next != &checkval)
  474. panic("realloc: corrupt allocation arena\n");
  475. if((nunits = NUNITS(size)) <= ounits)
  476. return ap;
  477. /*
  478. * Slightly harder:
  479. * if this allocation abuts the tail, try to just
  480. * adjust the tailptr.
  481. */
  482. MLOCK;
  483. if(tailptr != nil && p + ounits == tailptr){
  484. if(ounits > nunits){
  485. p->s.size = nunits;
  486. tailsize += ounits - nunits;
  487. MUNLOCK;
  488. return ap;
  489. }
  490. if(tailsize >= nunits - ounits){
  491. p->s.size = nunits;
  492. tailsize -= nunits - ounits;
  493. MUNLOCK;
  494. return ap;
  495. }
  496. }
  497. MUNLOCK;
  498. /*
  499. * Worth doing if it's a small reduction?
  500. * Do it anyway if <= NQUICK?
  501. if((ounits-nunits) < 2)
  502. return ap;
  503. */
  504. /*
  505. * Too hard (or can't be bothered):
  506. * allocate, copy and free.
  507. * What does the standard say for failure here?
  508. */
  509. if((v = qmalloc(size)) != nil){
  510. osize = (ounits - 1) * sizeof(Header);
  511. if(size < osize)
  512. osize = size;
  513. memmove(v, ap, osize);
  514. MLOCK;
  515. qfreeinternal(ap);
  516. MUNLOCK;
  517. }
  518. return v;
  519. }
  520. void
  521. setmalloctag(void *v, u32 i)
  522. {
  523. }
  524. void
  525. mallocinit(void)
  526. {
  527. if(tailptr != nil)
  528. return;
  529. tailbase = UINT2PTR(sys->vmunused);
  530. tailptr = tailbase;
  531. tailnunits = NUNITS(sys->vmend - sys->vmunused);
  532. print("base %#p ptr %#p nunits %u\n", tailbase, tailptr, tailnunits);
  533. }
  534. static int
  535. morecore(u32 nunits)
  536. {
  537. /*
  538. * First (simple) cut.
  539. * Pump it up when you don't really need it.
  540. * Pump it up until you can feel it.
  541. */
  542. if(nunits < NUNITS(128 * KiB))
  543. nunits = NUNITS(128 * KiB);
  544. if(nunits > tailnunits)
  545. nunits = tailnunits;
  546. tailnunits -= nunits;
  547. return nunits;
  548. }