上篇讲了Slab中的数据结构,这篇该讲Slab中的操作了。
既然是内存管理,那操作无非就两点:allocate 和 free。
1. 申请一个object
在Slab中,申请一个object是通过函数 kmem_cache_alloc() 来完成的。
3618 void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
3619 {
3620 return __cache_alloc(cachep, flags, __builtin_return_address(0));
3621 }
该函数有两个参数:
cachep指定了要从哪个cache中申请object.
flags: 如果该cache中所有的slab都已经满了,那就不得不向buddy system求救。flags指定了此时从buddy system中申请page frame时要使用的标志。
其实函数 kmem_cache_alloc() 只是一个wapper,经过层层调用,除去必要的锁操作,最终真正干活的是函数 ____cache_alloc()。
3190 static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
3191 {
3192 void *objp;
3193 struct array_cache *ac;
3194
3195 check_irq_off();
3196
3197 ac = cpu_cache_get(cachep);
3198 if (likely(ac->avail)) {
3199 STATS_INC_ALLOCHIT(cachep);
3200 ac->touched = 1;
3201 objp = ac->entry[--ac->avail];
3202 } else {
3203 STATS_INC_ALLOCMISS(cachep);
3204 objp = cache_alloc_refill(cachep, flags);
3205 }
3206 return objp;
3207 }
该函数看起来唬人,其实其武功招式总结起来就三招,人称“分配三板斧”。
第一板斧:per-cpu object cache.
3197 ~ 3201: 首先找到当前CPU的object cache,然后检查cache中有没有可用的object,有的话就拿出一个来。然后完活收工。
定位object cache是通过以下函数完成的:
760 static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep)
761 {
762 return cachep->array[smp_processor_id()];
763 }
上篇讲过,object cache实际上是一个LIFO的栈结构,其中成员变量 avail 即代表了可用object的个数,又作为索引值指出了栈顶位置。
所以从object cache中拿出一个object的操作,可以通过 3201 行的语句来完成。
3201 objp = ac->entry[--ac->avail];
第二板斧:Slab中的空闲object
如果object cache中没有可用的object,那就要先填充object cache。如果不考虑 shared object cache,填充cache的object来自于Slab页面。
该操作由函数 cache_alloc_refill() 完成。
2957 static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
2958 {
2959 int batchcount;
2960 struct kmem_list3 *l3;
2961 struct array_cache *ac;
2962 int node;
2963
2964 node = numa_node_id();
2965
2966 check_irq_off();
2967 ac = cpu_cache_get(cachep);
2968 retry:
2969 batchcount = ac->batchcount;
2978 l3 = cachep->nodelists[node];
2979
2980 BUG_ON(ac->avail > 0 || !l3);
2981 spin_lock(&l3->list_lock);
2982
2983 /* See if we can refill from the shared array */
2984 if (l3->shared && transfer_objects(ac, l3->shared, batchcount))
2985 goto alloc_done;
2986
2987 while (batchcount > 0) {
2988 struct list_head *entry;
2989 struct slab *slabp;
2990 /* Get slab alloc is to come from. */
2991 entry = l3->slabs_partial.next;
2992 if (entry == &l3->slabs_partial) {
2993 l3->free_touched = 1;
2994 entry = l3->slabs_free.next;
2995 if (entry == &l3->slabs_free)
2996 goto must_grow;
2997 }
2998
2999 slabp = list_entry(entry, struct slab, list);
3000 check_slabp(cachep, slabp);
3001 check_spinlock_acquired(cachep);
3002
3003 /*
3004 * The slab was either on partial or free list so
3005 * there must be at least one object available for
3006 * allocation.
3007 */
3008 BUG_ON(slabp->inuse < 0 || slabp->inuse >= cachep->num);
3009
3010 while (slabp->inuse < cachep->num && batchcount--) {
3011 STATS_INC_ALLOCED(cachep);
3012 STATS_INC_ACTIVE(cachep);
3013 STATS_SET_HIGH(cachep);
3014
3015 ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,
3016 node);
3017 }
3018 check_slabp(cachep, slabp);
3019
3020 /* move slabp to correct slabp list: */
3021 list_del(&slabp->list);
3022 if (slabp->free == BUFCTL_END)
3023 list_add(&slabp->list, &l3->slabs_full);
3024 else
3025 list_add(&slabp->list, &l3->slabs_partial);
3026 }
3027
3028 must_grow:
3029 l3->free_objects -= ac->avail;
3030 alloc_done:
3031 spin_unlock(&l3->list_lock);
3032
3033 if (unlikely(!ac->avail)) {
3034 int x;
3035 x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);
3036
3037 /* cache_grow can reenable interrupts, then ac could change. */
3038 ac = cpu_cache_get(cachep);
3039 if (!x && ac->avail == 0) /* no objects in sight? abort */
3040 return NULL;
3041
3042 if (!ac->avail) /* objects refilled by interrupt? */
3043 goto retry;
3044 }
3045 ac->touched = 1;
3046 return ac->entry[--ac->avail];
3047 }
2984行先检查一下能否从shared object cache中拿一些object填充到我们的object cache中。该操作其实就是一个memory copy的过程。
2987 ~ 3026 是从Slab页面中填充object cache的操作。
该操作首先遍历所有partial的slab,然后再遍历所有free的slab。对每个slab,拿出其中的free objects,填充到object cache中。
从slab中拿出一个free object的操作由函数 slab_get_obj() 完成。
2695 static void *slab_get_obj(struct kmem_cache *cachep, struct slab *slabp,
2696 int nodeid)
2697 {
2698 void *objp = index_to_obj(cachep, slabp, slabp->free);
2699 kmem_bufctl_t next;
2700
2701 slabp->inuse++;
2702 next = slab_bufctl(slabp)[slabp->free];
2707 slabp->free = next;
2708
2709 return objp;
2710 }
我们上篇讲过,slab中所有空闲的object 的描述符组成了一个简单的链表。该函数从这个链表中取下头部的一个空闲object。
把拿出的空闲object填充到object cache的操作是由3015行完成的。
3015 ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,
3016 node);
第三板斧:buddy system
如果所有的slab都满了,即每个slab都没有空闲object了,那怎么办?这个时候就得求助于buddy system了。
从buddy system中申请新的page frame,创建新的slab,从而创建出一批新的object。该操作由函数 cache_grow() 完成。
2760 static int cache_grow(struct kmem_cache *cachep,
2761 gfp_t flags, int nodeid, void *objp)
2762 {
2763 struct slab *slabp;
2764 size_t offset;
2765 gfp_t local_flags;
2766 struct kmem_list3 *l3;
2767
2768 /*
2769 * Be lazy and only check for valid flags here, keeping it out of the
2770 * critical path in kmem_cache_alloc().
2771 */
2772 BUG_ON(flags & GFP_SLAB_BUG_MASK);
2773 local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
2774
2775 /* Take the l3 list lock to change the colour_next on this node */
2776 check_irq_off();
2777 l3 = cachep->nodelists[nodeid];
2778 spin_lock(&l3->list_lock);
2779
2780 /* Get colour for the slab, and cal the next value. */
2781 offset = l3->colour_next;
2782 l3->colour_next++;
2783 if (l3->colour_next >= cachep->colour)
2784 l3->colour_next = 0;
2785 spin_unlock(&l3->list_lock);
2786
2787 offset *= cachep->colour_off;
2780 ~ 2787,该函数首先确定新建slab的color值。
上篇讲过,结构体kmem_list3的成员变量colour_next,保存了下一个要使用的color值。cache描述符中的colour,保存了可用的color 数。cache描述符中的colour_off, 保存了对齐字节数。
2781 ~ 2784行把colour_next的值赋值给变量offset,然后更新colour_next的值。如果colour_next的值达到了最大值,则重置为0。
2787行用color值乘以字节对齐数,算出来的就是slab开始位置的偏移量。
确定了color值后,下一步就是要向buddy system申请page frame了。
2804 if (!objp)
2805 objp = kmem_getpages(cachep, local_flags, nodeid);
2806 if (!objp)
2807 goto failed;
为新slab申请page frame的操作由函数 kmem_getpages() 完成。
1653 static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
1654 {
1655 struct page *page;
1656 int nr_pages;
1657 int i;
1667 flags |= cachep->gfpflags;
1668 if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
1669 flags |= __GFP_RECLAIMABLE;
1670
1671 page = alloc_pages_node(nodeid, flags, cachep->gfporder);
1672 if (!page)
1673 return NULL;
1674
1675 nr_pages = (1 << cachep->gfporder);
1682 for (i = 0; i < nr_pages; i++)
1683 __SetPageSlab(page + i);
1684 return page_address(page);
1685 }
函数 kmem_getpages() 首先确定分配flags。至此,调用 kmem_cache_alloc() 时传递的参数 flags 终于被用到了。但是这个flags还要再加上cache本身的flag,才会被用来申请page frame。
然后1671行调用了我们前面讲的alloc_pages函数,从buddy system中申请page frame。申请的page frame order为cachep->gfporder。
1682 ~ 1683行,为每个申请到的page frame的描述符设置 PG_slab 标志。
最后返回第一个page frame的线性地址。
好,申请到page frame了,那下一步做什么?让我们回到函数 cache_grow() 继续跟踪。
2810 slabp = alloc_slabmgmt(cachep, objp, offset,
2811 local_flags & ~GFP_CONSTRAINT_MASK, nodeid);
2812 if (!slabp)
2813 goto opps1;
2810 ~ 2813为新建的slab 分配并初始化slab描述符,由函数 alloc_slabmgmt() 完成。
2609 static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
2610 int colour_off, gfp_t local_flags,
2611 int nodeid)
2612 {
2613 struct slab *slabp;
2614
2615 if (OFF_SLAB(cachep)) {
2616 /* Slab management obj is off-slab. */
2617 slabp = kmem_cache_alloc_node(cachep->slabp_cache,
2618 local_flags & ~GFP_THISNODE, nodeid);
2619 if (!slabp)
2620 return NULL;
2621 } else {
2622 slabp = objp + colour_off;
2623 colour_off += cachep->slab_size;
2624 }
2625 slabp->inuse = 0;
2626 slabp->colouroff = colour_off;
2627 slabp->s_mem = objp + colour_off;
2628 slabp->nodeid = nodeid;
2629 return slabp;
2630 }
2615 ~ 2620:针对external slab,从cachep->slabp_cache中分配slab描述符(以及object描述符)。
2622 ~ 2623:针对internal slab,在slab页面内指定slab描述符的地址。考虑进去slab coloring, slab描述符放在 (color*aln) 位置处。
2625 ~ 2628:初始化新建的slab描述符。正如上篇讲到的,第一个object的偏移量定义为(color * aln) + dsize。
有了slab描述符后,让我们回到函数 cache_grow() 继续跟踪。
2816 slab_map_pages(cachep, slabp, objp);
2817
2818 cache_init_objs(cachep, slabp);
2819
2816行建立page frame与cache和slab描述符之间的关联。
2737 static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
2738 void *addr)
2739 {
2740 int nr_pages;
2741 struct page *page;
2742
2743 page = virt_to_page(addr);
2744
2745 nr_pages = 1;
2746 if (likely(!PageCompound(page)))
2747 nr_pages <<= cache->gfporder;
2748
2749 do {
2750 page_set_cache(page, cache);
2751 page_set_slab(page, slab);
2752 page++;
2753 } while (--nr_pages);
2754 }
建立关联的方式就是重用了page描述符中的成员变量lru。
583 static inline void page_set_cache(struct page *page, struct kmem_cache *cache)
584 {
585 page->lru.next = (struct list_head *)cache;
586 }
595 static inline void page_set_slab(struct page *page, struct slab *slab)
596 {
597 page->lru.prev = (struct list_head *)slab;
598 }
2818行初始化新建slab中的objects.
2637 static void cache_init_objs(struct kmem_cache *cachep,
2638 struct slab *slabp)
2639 {
2640 int i;
2641
2642 for (i = 0; i < cachep->num; i++) {
2643 void *objp = index_to_obj(cachep, slabp, i);
2676 if (cachep->ctor)
2677 cachep->ctor(cachep, objp);
2679 slab_bufctl(slabp)[i] = i + 1;
2680 }
2681 slab_bufctl(slabp)[i - 1] = BUFCTL_END;
2682 slabp->free = 0;
2683 }
这个初始化做了两件事:
如果该cache定义了构造函数,则对每个新建的object执行构造函数。
初始化object描述符,构造出空闲object链表。
建立了关联,初始化了object,继续跟踪 cache_grow()。
2823 spin_lock(&l3->list_lock);
2824
2825 /* Make slab active. */
2826 list_add_tail(&slabp->list, &(l3->slabs_free));
2827 STATS_INC_GROWN(cachep);
2828 l3->free_objects += cachep->num;
2829 spin_unlock(&l3->list_lock);
2830 return 1;
2831 opps1:
2832 kmem_freepages(cachep, objp);
2833 failed:
2834 if (local_flags & __GFP_WAIT)
2835 local_irq_disable();
2836 return 0;
2837 }
把新建的slab添加到cache的free slab链表中,更新kmem_list3中free_objects的值,然后完活收工。
第三板斧使出来之后,该cache新增了一slab的free object。于是重新使用第二板斧。
2. 释放一个object
释放一个object的API是函数 kmem_cache_free().
3760 void kmem_cache_free(struct kmem_cache *cachep, void *objp)
3761 {
3762 unsigned long flags;
3763
3764 local_irq_save(flags);
3765 debug_check_no_locks_freed(objp, obj_size(cachep));
3766 __cache_free(cachep, objp);
3767 local_irq_restore(flags);
3768 }
该函数也是一个wrapper,最终干活的是函数 __cache_free()。
3582 static inline void __cache_free(struct kmem_cache *cachep, void *objp)
3583 {
3584 struct array_cache *ac = cpu_cache_get(cachep);
3598
3599 if (likely(ac->avail < ac->limit)) {
3600 STATS_INC_FREEHIT(cachep);
3601 ac->entry[ac->avail++] = objp;
3602 return;
3603 } else {
3604 STATS_INC_FREEMISS(cachep);
3605 cache_flusharray(cachep, ac);
3606 ac->entry[ac->avail++] = objp;
3607 }
3608 }
相对应的,该函数也是三板斧,人称“释放三板斧”,而且这三板斧与“申请三板斧”一一对应,真是一对生死冤家。
第一板斧:per-cpu object cache
3599 ~ 3602,如果当前CPU的object cache没有满,则直接把要释放的object放到object cache中,然后完活收工。
第二板斧:Slab页面
如果object cache满了,则要刷一批object到slab页面中。该操作由函数 cache_flusharray() 完成。
3527 static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
3528 {
3529 int batchcount;
3530 struct kmem_list3 *l3;
3531 int node = numa_node_id();
3532
3533 batchcount = ac->batchcount;
3537 check_irq_off();
3538 l3 = cachep->nodelists[node];
3539 spin_lock(&l3->list_lock);
3540 if (l3->shared) {
3541 struct array_cache *shared_array = l3->shared;
3542 int max = shared_array->limit - shared_array->avail;
3543 if (max) {
3544 if (batchcount > max)
3545 batchcount = max;
3546 memcpy(&(shared_array->entry[shared_array->avail]),
3547 ac->entry, sizeof(void *) * batchcount);
3548 shared_array->avail += batchcount;
3549 goto free_done;
3550 }
3551 }
3552
3553 free_block(cachep, ac->entry, batchcount, node);
3554 free_done:
3573 spin_unlock(&l3->list_lock);
3574 ac->avail -= batchcount;
3575 memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail);
3576 }
3540 ~ 3551:如果shared object cache没有满额,则转移一批object到shared cache中。
3553行:利用函数 free_block() 刷一批object到slab页面中。
3575行:把object cache中剩下的object上移。为了保证object cache是LIFO的结构,会把最早放入object cache的那些object刷掉,因此需要把剩下的object向上移动。
3483 static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
3484 int node)
3485 {
3486 int i;
3487 struct kmem_list3 *l3;
3488
3489 for (i = 0; i < nr_objects; i++) {
3490 void *objp = objpp[i];
3491 struct slab *slabp;
3492
3493 slabp = virt_to_slab(objp);
3494 l3 = cachep->nodelists[node];
3495 list_del(&slabp->list);
3496 check_spinlock_acquired_node(cachep, node);
3497 check_slabp(cachep, slabp);
3498 slab_put_obj(cachep, slabp, objp, node);
3499 STATS_DEC_ACTIVE(cachep);
3500 l3->free_objects++;
3501 check_slabp(cachep, slabp);
3502
3503 /* fixup slab chains */
3504 if (slabp->inuse == 0) {
3505 if (l3->free_objects > l3->free_limit) {
3506 l3->free_objects -= cachep->num;
3507 /* No need to drop any previously held
3508 * lock here, even if we have a off-slab slab
3509 * descriptor it is guaranteed to come from
3510 * a different cache, refer to comments before
3511 * alloc_slabmgmt.
3512 */
3513 slab_destroy(cachep, slabp);
3514 } else {
3515 list_add(&slabp->list, &l3->slabs_free);
3516 }
3517 } else {
3518 /* Unconditionally move a slab to the end of the
3519 * partial list on free - maximum time for the
3520 * other objects to be freed, too.
3521 */
3522 list_add_tail(&slabp->list, &l3->slabs_partial);
3523 }
3524 }
3525 }
3493行根据object的地址,确定出slab描述符的地址。这里就用到我们在创建slab时建立的关联了。
612 static inline struct slab *virt_to_slab(const void *obj)
613 {
614 struct page *page = virt_to_head_page(obj);
615 return page_get_slab(page);
616 }
600 static inline struct slab *page_get_slab(struct page *page)
601 {
602 BUG_ON(!PageSlab(page));
603 return (struct slab *)page->lru.prev;
604 }
3498行把一个object放回到slab中。
2712 static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,
2713 void *objp, int nodeid)
2714 {
2715 unsigned int objnr = obj_to_index(cachep, slabp, objp);
2727 slab_bufctl(slabp)[objnr] = slabp->free;
2728 slabp->free = objnr;
2729 slabp->inuse--;
2730 }
3504 ~ 3523:放回一个object后,就要检查该slab的状态:
partial:把slab挂到slabs_partial链表上。
free:检查cache中free object的数量有没有超过限制。
没有超过:把slab挂到slabs_free链表中。
超过了:删掉该slab,把其page frame还给buddy system。
第三板斧:buddy system
删除一个slab的操作是由函数 slab_destroy() 完成的。
1944 static void slab_destroy(struct kmem_cache *cachep, struct slab *slabp)
1945 {
1946 void *addr = slabp->s_mem - slabp->colouroff;
1947
1957 kmem_freepages(cachep, addr);
1958 if (OFF_SLAB(cachep))
1959 kmem_cache_free(cachep->slabp_cache, slabp);
1961 }
该函数首先释放slab所用的page frame,然后对于external slab,把slab描述符占用的空间释放掉。
释放slab所用page frame的操作是由函数 kmem_freepages() 完成的。
1690 static void kmem_freepages(struct kmem_cache *cachep, void *addr)
1691 {
1692 unsigned long i = (1 << cachep->gfporder);
1693 struct page *page = virt_to_page(addr);
1702 while (i--) {
1703 BUG_ON(!PageSlab(page));
1704 __ClearPageSlab(page);
1705 page++;
1706 }
1709 free_pages((unsigned long)addr, cachep->gfporder);
1710 }
1702 ~ 1706:对slab所用的每个page frame,清除其描述符中的 PG_slab 标志。
1709:调用函数 free_pages() 把slab所用的page frame还给buddy system。
转载于:https://blog.51cto.com/richardguo/1675411