专用缓冲区 kmem_cache_free 缓冲区的释放
static inline void kmem_cache_free_one(kmem_cache_t *cachep, void *objp) { slab_t* slabp; CHECK_PAGE(virt_to_page(objp)); slabp = GET_PAGE_SLAB(virt_to_page(objp)); { unsigned int objnr = (objp-slabp->s_mem)/cachep->objsize; slab_bufctl(slabp)[objnr] = slabp->free; slabp->free = objnr; } STATS_DEC_ACTIVE(cachep); if (slabp->inuse-- == cachep->num) goto moveslab_partial; if (!slabp->inuse) goto moveslab_free; return; moveslab_partial: { struct list_head *t = cachep->firstnotfull; cachep->firstnotfull = &slabp->list; if (slabp->list.next == t) return; list_del(&slabp->list); list_add_tail(&slabp->list, t); return; } moveslab_free: { struct list_head *t = cachep->firstnotfull->prev; list_del(&slabp->list); list_add_tail(&slabp->list, &cachep->slabs); if (cachep->firstnotfull == &slabp->list) cachep->firstnotfull = t->next; return; } }
1) slabp = GET_PAGE_SLAB(virt_to_page(objp)); 通过要释放的物理地址,可以获取到对应的 struct page结构, 通过page结构的list可以找到objp所属的slab。 2) unsigned int objnr = (objp-slabp->s_mem)/cachep->objsize; slab_bufctl(slabp)[objnr] = slabp->free; 计算objp在slab中的下标。 更新slab中的空闲链接数组。 3) if (slabp->inuse-- == cachep->num) goto moveslab_partial; 更新inuse计数器。
通用缓冲区的分配 kmalloc
void * kmalloc (size_t size, int flags) { cache_sizes_t *csizep = cache_sizes; for (; csizep->cs_size; csizep++) { if (size > csizep->cs_size) continue; return __kmem_cache_alloc(flags & GFP_DMA ? csizep->cs_dmacachep : csizep->cs_cachep, flags); } return NULL; }
1) cache_sizes 是一个cache_sizes_t的结构数组。 typedef struct cache_sizes { size_t cs_size; kmem_cache_t *cs_cachep; kmem_cache_t *cs_dmacachep; } cache_sizes_t; 2) for 寻转在cache_sizes数组中找到第一个 csizep->cs_size大于size的kmem_cache_t类型的指针。 3) 然后通过 kmem_cache_alloc 分配。
kmem_cache_reap slab的回收
void kmem_cache_reap(int gfp_mask) { searchp = clock_searchp; do { unsigned int pages; struct list_head* p; unsigned int full_free; if (searchp->flags & SLAB_NO_REAP) goto next; spin_lock_irq(&searchp->spinlock); if (searchp->growing) goto next_unlock; if (searchp->dflags & DFLGS_GROWN) { searchp->dflags &= ~DFLGS_GROWN; goto next_unlock; } full_free = 0; p = searchp->slabs.prev; while (p != &searchp->slabs) { slabp = list_entry(p, slab_t, list); if (slabp->inuse) break; full_free++; p = p->prev; } pages = full_free * (1<<searchp->gfporder); if (searchp->ctor) pages = (pages*4+1)/5; if (searchp->gfporder) pages = (pages*4+1)/5; if (pages > best_pages) { best_cachep = searchp; best_len = full_free; best_pages = pages; if (full_free >= REAP_PERFECT) { clock_searchp = list_entry(searchp->next.next, kmem_cache_t,next); goto perfect; } } searchp = list_entry(searchp->next.next,kmem_cache_t,next); } while (--scan && searchp != clock_searchp); clock_searchp = searchp; if (!best_cachep) goto out; spin_lock_irq(&best_cachep->spinlock); perfect: best_len = (best_len*4 + 1)/5; for (scan = 0; scan < best_len; scan++) { struct list_head *p; if (best_cachep->growing) break; p = best_cachep->slabs.prev; if (p == &best_cachep->slabs) break; slabp = list_entry(p,slab_t,list); if (slabp->inuse) break; list_del(&slabp->list); if (best_cachep->firstnotfull == &slabp->list) best_cachep->firstnotfull = &best_cachep->slabs; STATS_INC_REAPED(best_cachep); spin_unlock_irq(&best_cachep->spinlock); kmem_slab_destroy(best_cachep, slabp); spin_lock_irq(&best_cachep->spinlock); } spin_unlock_irq(&best_cachep->spinlock); out: up(&cache_chain_sem); return; }
1) searchp = clock_searchp; 并不是每次扫描所有的slab队列。clock_searchp 会记住上一次扫描的位置。 2) kmem_slab_destroy(best_cachep, slabp);