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