00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <types.h>
00031 #include <lib.h>
00032 #include <spinlock.h>
00033 #include <vm.h>
00034
00035
00036
00037
00038
00039
00040 static
00041 void
00042 fill_deadbeef(void *vptr, size_t len)
00043 {
00044 uint32_t *ptr = vptr;
00045 size_t i;
00046
00047 for (i=0; i<len/sizeof(uint32_t); i++) {
00048 ptr[i] = 0xdeadbeef;
00049 }
00050 }
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079 #undef SLOW
00080 #undef SLOWER
00081
00082
00083
00084 #if PAGE_SIZE == 4096
00085
00086 #define NSIZES 8
00087 static const size_t sizes[NSIZES] = { 16, 32, 64, 128, 256, 512, 1024, 2048 };
00088
00089 #define SMALLEST_SUBPAGE_SIZE 16
00090 #define LARGEST_SUBPAGE_SIZE 2048
00091
00092 #elif PAGE_SIZE == 8192
00093 #error "No support for 8k pages (yet?)"
00094 #else
00095 #error "Odd page size"
00096 #endif
00097
00098
00099
00100 struct freelist {
00101 struct freelist *next;
00102 };
00103
00104 struct pageref {
00105 struct pageref *next_samesize;
00106 struct pageref *next_all;
00107 vaddr_t pageaddr_and_blocktype;
00108 uint16_t freelist_offset;
00109 uint16_t nfree;
00110 };
00111
00112 #define INVALID_OFFSET (0xffff)
00113
00114 #define PR_PAGEADDR(pr) ((pr)->pageaddr_and_blocktype & PAGE_FRAME)
00115 #define PR_BLOCKTYPE(pr) ((pr)->pageaddr_and_blocktype & ~PAGE_FRAME)
00116 #define MKPAB(pa, blk) (((pa)&PAGE_FRAME) | ((blk) & ~PAGE_FRAME))
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138 #define NPAGEREFS (PAGE_SIZE / sizeof(struct pageref))
00139 static struct pageref pagerefs[NPAGEREFS];
00140
00141 #define INUSE_WORDS (NPAGEREFS/32)
00142 static uint32_t pagerefs_inuse[INUSE_WORDS];
00143
00144 static
00145 struct pageref *
00146 allocpageref(void)
00147 {
00148 unsigned i,j;
00149 uint32_t k;
00150
00151 for (i=0; i<INUSE_WORDS; i++) {
00152 if (pagerefs_inuse[i]==0xffffffff) {
00153
00154 continue;
00155 }
00156 for (k=1,j=0; k!=0; k<<=1,j++) {
00157 if ((pagerefs_inuse[i] & k)==0) {
00158 pagerefs_inuse[i] |= k;
00159 return &pagerefs[i*32 + j];
00160 }
00161 }
00162 KASSERT(0);
00163 }
00164
00165
00166 return NULL;
00167 }
00168
00169 static
00170 void
00171 freepageref(struct pageref *p)
00172 {
00173 size_t i, j;
00174 uint32_t k;
00175
00176 j = p-pagerefs;
00177 KASSERT(j < NPAGEREFS);
00178 i = j/32;
00179 k = ((uint32_t)1) << (j%32);
00180 KASSERT((pagerefs_inuse[i] & k) != 0);
00181 pagerefs_inuse[i] &= ~k;
00182 }
00183
00184
00185
00186 static struct pageref *sizebases[NSIZES];
00187 static struct pageref *allbase;
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198 static struct spinlock kmalloc_spinlock = SPINLOCK_INITIALIZER;
00199
00200
00201
00202
00203 #ifdef SLOWER
00204 #ifndef SLOW
00205 #define SLOW
00206 #endif
00207 #endif
00208
00209 #ifdef SLOW
00210 static
00211 void
00212 checksubpage(struct pageref *pr)
00213 {
00214 vaddr_t prpage, fla;
00215 struct freelist *fl;
00216 int blktype;
00217 int nfree=0;
00218
00219 KASSERT(spinlock_do_i_hold(&kmalloc_spinlock));
00220
00221 if (pr->freelist_offset == INVALID_OFFSET) {
00222 KASSERT(pr->nfree==0);
00223 return;
00224 }
00225
00226 prpage = PR_PAGEADDR(pr);
00227 blktype = PR_BLOCKTYPE(pr);
00228
00229 KASSERT(pr->freelist_offset < PAGE_SIZE);
00230 KASSERT(pr->freelist_offset % sizes[blktype] == 0);
00231
00232 fla = prpage + pr->freelist_offset;
00233 fl = (struct freelist *)fla;
00234
00235 for (; fl != NULL; fl = fl->next) {
00236 fla = (vaddr_t)fl;
00237 KASSERT(fla >= prpage && fla < prpage + PAGE_SIZE);
00238 KASSERT((fla-prpage) % sizes[blktype] == 0);
00239 KASSERT(fla >= MIPS_KSEG0);
00240 KASSERT(fla < MIPS_KSEG1);
00241 nfree++;
00242 }
00243 KASSERT(nfree==pr->nfree);
00244 }
00245 #else
00246 #define checksubpage(pr) ((void)(pr))
00247 #endif
00248
00249 #ifdef SLOWER
00250 static
00251 void
00252 checksubpages(void)
00253 {
00254 struct pageref *pr;
00255 int i;
00256 unsigned sc=0, ac=0;
00257
00258 KASSERT(spinlock_do_i_hold(&kmalloc_spinlock));
00259
00260 for (i=0; i<NSIZES; i++) {
00261 for (pr = sizebases[i]; pr != NULL; pr = pr->next_samesize) {
00262 checksubpage(pr);
00263 KASSERT(sc < NPAGEREFS);
00264 sc++;
00265 }
00266 }
00267
00268 for (pr = allbase; pr != NULL; pr = pr->next_all) {
00269 checksubpage(pr);
00270 KASSERT(ac < NPAGEREFS);
00271 ac++;
00272 }
00273
00274 KASSERT(sc==ac);
00275 }
00276 #else
00277 #define checksubpages()
00278 #endif
00279
00280
00281
00282 static
00283 void
00284 dumpsubpage(struct pageref *pr)
00285 {
00286 vaddr_t prpage, fla;
00287 struct freelist *fl;
00288 int blktype;
00289 unsigned i, n, index;
00290 uint32_t freemap[PAGE_SIZE / (SMALLEST_SUBPAGE_SIZE*32)];
00291
00292 checksubpage(pr);
00293 KASSERT(spinlock_do_i_hold(&kmalloc_spinlock));
00294
00295
00296 for (i=0; i<sizeof(freemap)/sizeof(freemap[0]); i++) {
00297 freemap[i] = 0;
00298 }
00299
00300 prpage = PR_PAGEADDR(pr);
00301 blktype = PR_BLOCKTYPE(pr);
00302
00303
00304 n = PAGE_SIZE / sizes[blktype];
00305 KASSERT(n <= 32*sizeof(freemap)/sizeof(freemap[0]));
00306
00307 if (pr->freelist_offset != INVALID_OFFSET) {
00308 fla = prpage + pr->freelist_offset;
00309 fl = (struct freelist *)fla;
00310
00311 for (; fl != NULL; fl = fl->next) {
00312 fla = (vaddr_t)fl;
00313 index = (fla-prpage) / sizes[blktype];
00314 KASSERT(index<n);
00315 freemap[index/32] |= (1<<(index%32));
00316 }
00317 }
00318
00319 kprintf("at 0x%08lx: size %-4lu %u/%u free\n",
00320 (unsigned long)prpage, (unsigned long) sizes[blktype],
00321 (unsigned) pr->nfree, n);
00322 kprintf(" ");
00323 for (i=0; i<n; i++) {
00324 int val = (freemap[i/32] & (1<<(i%32)))!=0;
00325 kprintf("%c", val ? '.' : '*');
00326 if (i%64==63 && i<n-1) {
00327 kprintf("\n ");
00328 }
00329 }
00330 kprintf("\n");
00331 }
00332
00333 void
00334 kheap_printstats(void)
00335 {
00336 struct pageref *pr;
00337
00338
00339 spinlock_acquire(&kmalloc_spinlock);
00340
00341 kprintf("Subpage allocator status:\n");
00342
00343 for (pr = allbase; pr != NULL; pr = pr->next_all) {
00344 dumpsubpage(pr);
00345 }
00346
00347 spinlock_release(&kmalloc_spinlock);
00348 }
00349
00350
00351
00352 static
00353 void
00354 remove_lists(struct pageref *pr, int blktype)
00355 {
00356 struct pageref **guy;
00357
00358 KASSERT(blktype>=0 && blktype<NSIZES);
00359
00360 for (guy = &sizebases[blktype]; *guy; guy = &(*guy)->next_samesize) {
00361 checksubpage(*guy);
00362 if (*guy == pr) {
00363 *guy = pr->next_samesize;
00364 break;
00365 }
00366 }
00367
00368 for (guy = &allbase; *guy; guy = &(*guy)->next_all) {
00369 checksubpage(*guy);
00370 if (*guy == pr) {
00371 *guy = pr->next_all;
00372 break;
00373 }
00374 }
00375 }
00376
00377 static
00378 inline
00379 int blocktype(size_t sz)
00380 {
00381 unsigned i;
00382 for (i=0; i<NSIZES; i++) {
00383 if (sz <= sizes[i]) {
00384 return i;
00385 }
00386 }
00387
00388 panic("Subpage allocator cannot handle allocation of size %lu\n",
00389 (unsigned long)sz);
00390
00391
00392 return 0;
00393 }
00394
00395 static
00396 void *
00397 subpage_kmalloc(size_t sz)
00398 {
00399 unsigned blktype;
00400 struct pageref *pr;
00401 vaddr_t prpage;
00402 vaddr_t fla;
00403 struct freelist *volatile fl;
00404 void *retptr;
00405
00406 volatile int i;
00407
00408
00409 blktype = blocktype(sz);
00410 sz = sizes[blktype];
00411
00412 spinlock_acquire(&kmalloc_spinlock);
00413
00414 checksubpages();
00415
00416 for (pr = sizebases[blktype]; pr != NULL; pr = pr->next_samesize) {
00417
00418
00419 KASSERT(PR_BLOCKTYPE(pr) == blktype);
00420 checksubpage(pr);
00421
00422 if (pr->nfree > 0) {
00423
00424 doalloc:
00425
00426 KASSERT(pr->freelist_offset < PAGE_SIZE);
00427 prpage = PR_PAGEADDR(pr);
00428 fla = prpage + pr->freelist_offset;
00429 fl = (struct freelist *)fla;
00430
00431 retptr = fl;
00432 fl = fl->next;
00433 pr->nfree--;
00434
00435 if (fl != NULL) {
00436 KASSERT(pr->nfree > 0);
00437 fla = (vaddr_t)fl;
00438 KASSERT(fla - prpage < PAGE_SIZE);
00439 pr->freelist_offset = fla - prpage;
00440 }
00441 else {
00442 KASSERT(pr->nfree == 0);
00443 pr->freelist_offset = INVALID_OFFSET;
00444 }
00445
00446 checksubpages();
00447
00448 spinlock_release(&kmalloc_spinlock);
00449 return retptr;
00450 }
00451 }
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462 spinlock_release(&kmalloc_spinlock);
00463 prpage = alloc_kpages(1);
00464 if (prpage==0) {
00465
00466 kprintf("kmalloc: Subpage allocator couldn't get a page\n");
00467 return NULL;
00468 }
00469 spinlock_acquire(&kmalloc_spinlock);
00470
00471 pr = allocpageref();
00472 if (pr==NULL) {
00473
00474 spinlock_release(&kmalloc_spinlock);
00475 free_kpages(prpage);
00476 kprintf("kmalloc: Subpage allocator couldn't get pageref\n");
00477 return NULL;
00478 }
00479
00480 pr->pageaddr_and_blocktype = MKPAB(prpage, blktype);
00481 pr->nfree = PAGE_SIZE / sizes[blktype];
00482
00483
00484
00485
00486
00487
00488
00489 fla = prpage;
00490 fl = (struct freelist *)fla;
00491 fl->next = NULL;
00492 for (i=1; i<pr->nfree; i++) {
00493 fl = (struct freelist *)(fla + i*sizes[blktype]);
00494 fl->next = (struct freelist *)(fla + (i-1)*sizes[blktype]);
00495 KASSERT(fl != fl->next);
00496 }
00497 fla = (vaddr_t) fl;
00498 pr->freelist_offset = fla - prpage;
00499 KASSERT(pr->freelist_offset == (pr->nfree-1)*sizes[blktype]);
00500
00501 pr->next_samesize = sizebases[blktype];
00502 sizebases[blktype] = pr;
00503
00504 pr->next_all = allbase;
00505 allbase = pr;
00506
00507
00508 goto doalloc;
00509 }
00510
00511 static
00512 int
00513 subpage_kfree(void *ptr)
00514 {
00515 int blktype;
00516 vaddr_t ptraddr;
00517 struct pageref *pr;
00518 vaddr_t prpage;
00519 vaddr_t fla;
00520 struct freelist *fl;
00521 vaddr_t offset;
00522
00523 ptraddr = (vaddr_t)ptr;
00524
00525 spinlock_acquire(&kmalloc_spinlock);
00526
00527 checksubpages();
00528
00529 for (pr = allbase; pr; pr = pr->next_all) {
00530 prpage = PR_PAGEADDR(pr);
00531 blktype = PR_BLOCKTYPE(pr);
00532
00533
00534 KASSERT(blktype>=0 && blktype<NSIZES);
00535 checksubpage(pr);
00536
00537 if (ptraddr >= prpage && ptraddr < prpage + PAGE_SIZE) {
00538 break;
00539 }
00540 }
00541
00542 if (pr==NULL) {
00543
00544 spinlock_release(&kmalloc_spinlock);
00545 return -1;
00546 }
00547
00548 offset = ptraddr - prpage;
00549
00550
00551 if (offset >= PAGE_SIZE || offset % sizes[blktype] != 0) {
00552 panic("kfree: subpage free of invalid addr %p\n", ptr);
00553 }
00554
00555
00556
00557
00558
00559 fill_deadbeef(ptr, sizes[blktype]);
00560
00561
00562
00563
00564
00565
00566 fla = prpage + offset;
00567 fl = (struct freelist *)fla;
00568 if (pr->freelist_offset == INVALID_OFFSET) {
00569 fl->next = NULL;
00570 } else {
00571 fl->next = (struct freelist *)(prpage + pr->freelist_offset);
00572 }
00573 pr->freelist_offset = offset;
00574 pr->nfree++;
00575
00576 KASSERT(pr->nfree <= PAGE_SIZE / sizes[blktype]);
00577 if (pr->nfree == PAGE_SIZE / sizes[blktype]) {
00578
00579 remove_lists(pr, blktype);
00580 freepageref(pr);
00581
00582 spinlock_release(&kmalloc_spinlock);
00583 free_kpages(prpage);
00584 }
00585 else {
00586 spinlock_release(&kmalloc_spinlock);
00587 }
00588
00589 #ifdef SLOWER
00590 spinlock_acquire(&kmalloc_spinlock);
00591 checksubpages();
00592 spinlock_release(&kmalloc_spinlock);
00593 #endif
00594
00595 return 0;
00596 }
00597
00598
00599
00600
00601 void *
00602 kmalloc(size_t sz)
00603 {
00604 if (sz>=LARGEST_SUBPAGE_SIZE) {
00605 unsigned long npages;
00606 vaddr_t address;
00607
00608
00609 npages = (sz + PAGE_SIZE - 1)/PAGE_SIZE;
00610 address = alloc_kpages(npages);
00611 if (address==0) {
00612 return NULL;
00613 }
00614
00615 return (void *)address;
00616 }
00617
00618 return subpage_kmalloc(sz);
00619 }
00620
00621 void
00622 kfree(void *ptr)
00623 {
00624
00625
00626
00627 if (ptr == NULL) {
00628 return;
00629 } else if (subpage_kfree(ptr)) {
00630 KASSERT((vaddr_t)ptr%PAGE_SIZE==0);
00631 free_kpages((vaddr_t)ptr);
00632 }
00633 }
00634