三里屯摇滚 发表于 2012-01-10 10:33

Linux内存管理之slab机制(初始化) 3...........

Linux内存管理之slab机制(初始化) 3...........









第二阶段代码分析Start_kernel()->kmem_cache_init_late()

view plaincopy to clipboardprint?/*Slab系统初始化分两个部分,先初始化一些基本的,待系统初始化工作进行的差不多时,再配置一些特殊功能。*/
void __init kmem_cache_init_late(void)
{
    struct kmem_cache *cachep;
    /* 初始化阶段local cache的大小是固定的,要根据对象大小重新计算 */
    /* 6) resize the head arrays to their final sizes */
    mutex_lock(&cache_chain_mutex);
    list_for_each_entry(cachep, &cache_chain, next)
      if (enable_cpucache(cachep, GFP_NOWAIT))
            BUG();
    mutex_unlock(&cache_chain_mutex);

    /* Done! */
    /* 大功告成,general cache终于全部建立起来了 */
    g_cpucache_up = FULL;

    /* Annotate slab for lockdep -- annotate the malloc caches */
    init_lock_keys();

    /*
   * Register a cpu startup notifier callback that initializes
   * cpu_cache_get for all new cpus
   */
   /* 注册cpu up回调函数,cpu up时配置local cache */
    register_cpu_notifier(&cpucache_notifier);

    /*
   * The reap timers are started later, with a module init call: That part
   * of the kernel is not yet operational.
   */
}
/*Slab系统初始化分两个部分,先初始化一些基本的,待系统初始化工作进行的差不多时,再配置一些特殊功能。*/
void __init kmem_cache_init_late(void)
{
        struct kmem_cache *cachep;
        /* 初始化阶段local cache的大小是固定的,要根据对象大小重新计算 */
        /* 6) resize the head arrays to their final sizes */
        mutex_lock(&cache_chain_mutex);
        list_for_each_entry(cachep, &cache_chain, next)
                if (enable_cpucache(cachep, GFP_NOWAIT))
                        BUG();
        mutex_unlock(&cache_chain_mutex);

        /* Done! */
        /* 大功告成,general cache终于全部建立起来了 */
        g_cpucache_up = FULL;

        /* Annotate slab for lockdep -- annotate the malloc caches */
        init_lock_keys();

        /*
       * Register a cpu startup notifier callback that initializes
       * cpu_cache_get for all new cpus
       */
       /* 注册cpu up回调函数,cpu up时配置local cache */
        register_cpu_notifier(&cpucache_notifier);

        /*
       * The reap timers are started later, with a module init call: That part
       * of the kernel is not yet operational.
       */
}view plaincopy to clipboardprint?/* Called with cache_chain_mutex held always */
/*local cache 初始化*/
static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp)
{
    int err;
    int limit, shared;

    /*
   * The head array serves three purposes:
   * - create a LIFO ordering, i.e. return objects that are cache-warm
   * - reduce the number of spinlock operations.
   * - reduce the number of linked list operations on the slab and
   *   bufctl chains: array operations are cheaper.
   * The numbers are guessed, we should auto-tune as described by
   * Bonwick.
   */ /* 根据对象大小计算local cache中对象数目上限 */
    if (cachep->buffer_size > 131072)
      limit = 1;
    else if (cachep->buffer_size > PAGE_SIZE)
      limit = 8;
    else if (cachep->buffer_size > 1024)
      limit = 24;
    else if (cachep->buffer_size > 256)
      limit = 54;
    else
      limit = 120;

    /*
   * CPU bound tasks (e.g. network routing) can exhibit cpu bound
   * allocation behaviour: Most allocs on one cpu, most free operations
   * on another cpu. For these cases, an efficient object passing between
   * cpus is necessary. This is provided by a shared array. The array
   * replaces Bonwick's magazine layer.
   * On uniprocessor, it's functionally equivalent (but less efficient)
   * to a larger limit. Thus disabled by default.
   */
    shared = 0;
    /* 多核系统,设置shared local cache中对象数目 */
    if (cachep->buffer_size <= PAGE_SIZE && num_possible_cpus() > 1)
      shared = 8;

#if DEBUG   
    /*
   * With debugging enabled, large batchcount lead to excessively long
   * periods with disabled local interrupts. Limit the batchcount
   */
    if (limit > 32)
      limit = 32;
#endif   
    /* 配置local cache */
    err = do_tune_cpucache(cachep, limit, (limit + 1) / 2, shared, gfp);
    if (err)
      printk(KERN_ERR "enable_cpucache failed for %s, error %d.\n",
               cachep->name, -err);
    return err;
}
/* Called with cache_chain_mutex held always */
/*local cache 初始化*/
static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp)
{
        int err;
        int limit, shared;

        /*
       * The head array serves three purposes:
       * - create a LIFO ordering, i.e. return objects that are cache-warm
       * - reduce the number of spinlock operations.
       * - reduce the number of linked list operations on the slab and
       *   bufctl chains: array operations are cheaper.
       * The numbers are guessed, we should auto-tune as described by
       * Bonwick.
       */ /* 根据对象大小计算local cache中对象数目上限 */
        if (cachep->buffer_size > 131072)
                limit = 1;
        else if (cachep->buffer_size > PAGE_SIZE)
                limit = 8;
        else if (cachep->buffer_size > 1024)
                limit = 24;
        else if (cachep->buffer_size > 256)
                limit = 54;
        else
                limit = 120;

        /*
       * CPU bound tasks (e.g. network routing) can exhibit cpu bound
       * allocation behaviour: Most allocs on one cpu, most free operations
       * on another cpu. For these cases, an efficient object passing between
       * cpus is necessary. This is provided by a shared array. The array
       * replaces Bonwick's magazine layer.
       * On uniprocessor, it's functionally equivalent (but less efficient)
       * to a larger limit. Thus disabled by default.
       */
        shared = 0;
        /* 多核系统,设置shared local cache中对象数目 */
        if (cachep->buffer_size <= PAGE_SIZE && num_possible_cpus() > 1)
                shared = 8;

#if DEBUG
        /*
       * With debugging enabled, large batchcount lead to excessively long
       * periods with disabled local interrupts. Limit the batchcount
       */
        if (limit > 32)
                limit = 32;
#endif
        /* 配置local cache */
        err = do_tune_cpucache(cachep, limit, (limit + 1) / 2, shared, gfp);
        if (err)
                printk(KERN_ERR "enable_cpucache failed for %s, error %d.\n",
                     cachep->name, -err);
        return err;
}view plaincopy to clipboardprint?/* Always called with the cache_chain_mutex held */
/*配置local cache、shared local cache和slab三链*/
static int do_tune_cpucache(struct kmem_cache *cachep, int limit,
                int batchcount, int shared, gfp_t gfp)
{
    struct ccupdate_struct *new;
    int i;

    new = kzalloc(sizeof(*new), gfp);
    if (!new)
      return -ENOMEM;
    /* 为每个cpu分配新的struct array_cache对象 */
    for_each_online_cpu(i) {
      new->new = alloc_arraycache(cpu_to_node(i), limit,
                        batchcount, gfp);
      if (!new->new) {
            for (i--; i >= 0; i--)
                kfree(new->new);
            kfree(new);
            return -ENOMEM;
      }
    }
    new->cachep = cachep;
    /* 用新的struct array_cache对象替换旧的struct array_cache对象
    ,在支持cpu热插拔的系统上,离线cpu可能没有释放local cache
    ,使用的仍是旧local cache,参见__kmem_cache_destroy函数
    。虽然cpu up时要重新配置local cache,也无济于事。考虑下面的情景
    :共有Cpu A和Cpu B,Cpu B down后,destroy Cache X,由于此时Cpu B是down状态
    ,所以Cache X中Cpu B的local cache未释放,过一段时间Cpu B又up了
    ,更新cache_chain 链中所有cache的local cache,但此时Cache X对象已经释放回
    cache_cache中了,其Cpu B local cache并未被更新。又过了一段时间
    ,系统需要创建新的cache,将Cache X对象分配出去,其Cpu B仍然是旧的
    local cache,需要进行更新。
    */
    on_each_cpu(do_ccupdate_local, (void *)new, 1);

    check_irq_on();
    cachep->batchcount = batchcount;
    cachep->limit = limit;
    cachep->shared = shared;
    /* 释放旧的local cache */
    for_each_online_cpu(i) {
      struct array_cache *ccold = new->new;
      if (!ccold)
            continue;
      spin_lock_irq(&cachep->nodelists->list_lock);
      /* 释放旧local cache中的对象 */
      free_block(cachep, ccold->entry, ccold->avail, cpu_to_node(i));
      spin_unlock_irq(&cachep->nodelists->list_lock);
      /* 释放旧的struct array_cache对象 */
      kfree(ccold);
    }
    kfree(new);
    /* 初始化shared local cache 和slab三链 */
    return alloc_kmemlist(cachep, gfp);
}
/* Always called with the cache_chain_mutex held */
/*配置local cache、shared local cache和slab三链*/
static int do_tune_cpucache(struct kmem_cache *cachep, int limit,
                                int batchcount, int shared, gfp_t gfp)
{
        struct ccupdate_struct *new;
        int i;

        new = kzalloc(sizeof(*new), gfp);
        if (!new)
                return -ENOMEM;
        /* 为每个cpu分配新的struct array_cache对象 */
        for_each_online_cpu(i) {
                new->new = alloc_arraycache(cpu_to_node(i), limit,
                                                batchcount, gfp);
                if (!new->new) {
                        for (i--; i >= 0; i--)
                                kfree(new->new);
                        kfree(new);
                        return -ENOMEM;
                }
        }
        new->cachep = cachep;
        /* 用新的struct array_cache对象替换旧的struct array_cache对象
        ,在支持cpu热插拔的系统上,离线cpu可能没有释放local cache
        ,使用的仍是旧local cache,参见__kmem_cache_destroy函数
        。虽然cpu up时要重新配置local cache,也无济于事。考虑下面的情景
        :共有Cpu A和Cpu B,Cpu B down后,destroy Cache X,由于此时Cpu B是down状态
        ,所以Cache X中Cpu B的local cache未释放,过一段时间Cpu B又up了
        ,更新cache_chain 链中所有cache的local cache,但此时Cache X对象已经释放回
        cache_cache中了,其Cpu B local cache并未被更新。又过了一段时间
        ,系统需要创建新的cache,将Cache X对象分配出去,其Cpu B仍然是旧的
        local cache,需要进行更新。
        */
        on_each_cpu(do_ccupdate_local, (void *)new, 1);

        check_irq_on();
        cachep->batchcount = batchcount;
        cachep->limit = limit;
        cachep->shared = shared;
        /* 释放旧的local cache */
        for_each_online_cpu(i) {
                struct array_cache *ccold = new->new;
                if (!ccold)
                        continue;
                spin_lock_irq(&cachep->nodelists->list_lock);
                /* 释放旧local cache中的对象 */
                free_block(cachep, ccold->entry, ccold->avail, cpu_to_node(i));
                spin_unlock_irq(&cachep->nodelists->list_lock);
                /* 释放旧的struct array_cache对象 */
                kfree(ccold);
        }
        kfree(new);
        /* 初始化shared local cache 和slab三链 */
        return alloc_kmemlist(cachep, gfp);
} 更新本地cacheview plaincopy to clipboardprint?/*更新每个cpu的struct array_cache对象*/
static void do_ccupdate_local(void *info)
{
    struct ccupdate_struct *new = info;
    struct array_cache *old;

    check_irq_off();
    old = cpu_cache_get(new->cachep);
   /* 指向新的struct array_cache对象 */
    new->cachep->array = new->new;
      /* 保存旧的struct array_cache对象 */
    new->new = old;
}
/*更新每个cpu的struct array_cache对象*/
static void do_ccupdate_local(void *info)
{
        struct ccupdate_struct *new = info;
        struct array_cache *old;

        check_irq_off();
        old = cpu_cache_get(new->cachep);
       /* 指向新的struct array_cache对象 */
        new->cachep->array = new->new;
            /* 保存旧的struct array_cache对象 */
        new->new = old;
}view plaincopy to clipboardprint?/*初始化shared local cache和slab三链,初始化完成后,slab三链中没有任何slab*/
static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp)
{
    int node;
    struct kmem_list3 *l3;
    struct array_cache *new_shared;
    struct array_cache **new_alien = NULL;

    for_each_online_node(node) {
         /* NUMA相关 */
                if (use_alien_caches) {
                        new_alien = alloc_alien_cache(node, cachep->limit, gfp);
                        if (!new_alien)
                              goto fail;
                }

      new_shared = NULL;
      if (cachep->shared) {
            /* 分配shared local cache */
            new_shared = alloc_arraycache(node,
                cachep->shared*cachep->batchcount,
                  0xbaadf00d, gfp);
            if (!new_shared) {
                free_alien_cache(new_alien);
                goto fail;
            }
      }
      /* 获得旧的slab三链 */
      l3 = cachep->nodelists;
      if (l3) {
            /* 就slab三链指针不为空,需要先释放旧的资源 */
            struct array_cache *shared = l3->shared;

            spin_lock_irq(&l3->list_lock);
            /* 释放旧的shared local cache中的对象 */
            if (shared)
                free_block(cachep, shared->entry,
                        shared->avail, node);
            /* 指向新的shared local cache */
            l3->shared = new_shared;
            if (!l3->alien) {
                l3->alien = new_alien;
                new_alien = NULL;
            }/* 计算cache中空闲对象的上限 */
            l3->free_limit = (1 + nr_cpus_node(node)) *
                  cachep->batchcount + cachep->num;
            spin_unlock_irq(&l3->list_lock);
            /* 释放旧shared local cache的struct array_cache对象 */
            kfree(shared);
            free_alien_cache(new_alien);
            continue;/*访问下一个节点*/
      }
         /* 如果没有旧的l3,分配新的slab三链 */
      l3 = kmalloc_node(sizeof(struct kmem_list3), gfp, node);
      if (!l3) {
            free_alien_cache(new_alien);
            kfree(new_shared);
            goto fail;
      }
         /* 初始化slab三链 */
      kmem_list3_init(l3);
      l3->next_reap = jiffies + REAPTIMEOUT_LIST3 +
                ((unsigned long)cachep) % REAPTIMEOUT_LIST3;
      l3->shared = new_shared;
      l3->alien = new_alien;
      l3->free_limit = (1 + nr_cpus_node(node)) *
                  cachep->batchcount + cachep->num;
      cachep->nodelists = l3;
    }
    return 0;

fail:
    if (!cachep->next.next) {
      /* Cache is not active yet. Roll back what we did */
      node--;
      while (node >= 0) {
            if (cachep->nodelists) {
                l3 = cachep->nodelists;

                kfree(l3->shared);
                free_alien_cache(l3->alien);
                kfree(l3);
                cachep->nodelists = NULL;
            }
            node--;
      }
    }
    return -ENOMEM;
}
/*初始化shared local cache和slab三链,初始化完成后,slab三链中没有任何slab*/
static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp)
{
        int node;
        struct kmem_list3 *l3;
        struct array_cache *new_shared;
        struct array_cache **new_alien = NULL;

        for_each_online_node(node) {
               /* NUMA相关 */
                if (use_alien_caches) {
                        new_alien = alloc_alien_cache(node, cachep->limit, gfp);
                        if (!new_alien)
                              goto fail;
                }

                new_shared = NULL;
                if (cachep->shared) {
                        /* 分配shared local cache */
                        new_shared = alloc_arraycache(node,
                                cachep->shared*cachep->batchcount,
                                        0xbaadf00d, gfp);
                        if (!new_shared) {
                                free_alien_cache(new_alien);
                                goto fail;
                        }
                }
                /* 获得旧的slab三链 */
                l3 = cachep->nodelists;
                if (l3) {
                        /* 就slab三链指针不为空,需要先释放旧的资源 */
                        struct array_cache *shared = l3->shared;

                        spin_lock_irq(&l3->list_lock);
                        /* 释放旧的shared local cache中的对象 */
                        if (shared)
                                free_block(cachep, shared->entry,
                                                shared->avail, node);
                        /* 指向新的shared local cache */
                        l3->shared = new_shared;
                        if (!l3->alien) {
                                l3->alien = new_alien;
                                new_alien = NULL;
                        }/* 计算cache中空闲对象的上限 */
                        l3->free_limit = (1 + nr_cpus_node(node)) *
                                        cachep->batchcount + cachep->num;
                        spin_unlock_irq(&l3->list_lock);
                        /* 释放旧shared local cache的struct array_cache对象 */
                        kfree(shared);
                        free_alien_cache(new_alien);
                        continue;/*访问下一个节点*/
                }
               /* 如果没有旧的l3,分配新的slab三链 */
                l3 = kmalloc_node(sizeof(struct kmem_list3), gfp, node);
                if (!l3) {
                        free_alien_cache(new_alien);
                        kfree(new_shared);
                        goto fail;
                }
               /* 初始化slab三链 */
                kmem_list3_init(l3);
                l3->next_reap = jiffies + REAPTIMEOUT_LIST3 +
                                ((unsigned long)cachep) % REAPTIMEOUT_LIST3;
                l3->shared = new_shared;
                l3->alien = new_alien;
                l3->free_limit = (1 + nr_cpus_node(node)) *
                                        cachep->batchcount + cachep->num;
                cachep->nodelists = l3;
        }
        return 0;

fail:
        if (!cachep->next.next) {
                /* Cache is not active yet. Roll back what we did */
                node--;
                while (node >= 0) {
                        if (cachep->nodelists) {
                                l3 = cachep->nodelists;

                                kfree(l3->shared);
                                free_alien_cache(l3->alien);
                                kfree(l3);
                                cachep->nodelists = NULL;
                        }
                        node--;
                }
        }
        return -ENOMEM;
} 看一个辅助函数view plaincopy to clipboardprint?/*分配struct array_cache对象。*/
static struct array_cache *alloc_arraycache(int node, int entries,
                        int batchcount, gfp_t gfp)
{
    /* struct array_cache后面紧接着的是entry数组,合在一起申请内存 */
    int memsize = sizeof(void *) * entries + sizeof(struct array_cache);
    struct array_cache *nc = NULL;
    /* 分配一个local cache对象,kmalloc从general cache中分配 */
    nc = kmalloc_node(memsize, gfp, node);
    /*
   * The array_cache structures contain pointers to free object.
   * However, when such objects are allocated or transfered to another
   * cache the pointers are not cleared and they could be counted as
   * valid references during a kmemleak scan. Therefore, kmemleak must
   * not scan such objects.
   */
    kmemleak_no_scan(nc);
   /* 初始化local cache */
    if (nc) {
      nc->avail = 0;
      nc->limit = entries;
      nc->batchcount = batchcount;
      nc->touched = 0;
      spin_lock_init(&nc->lock);
    }
    return nc;
}
/*分配struct array_cache对象。*/
static struct array_cache *alloc_arraycache(int node, int entries,
                                          int batchcount, gfp_t gfp)
{
        /* struct array_cache后面紧接着的是entry数组,合在一起申请内存 */
        int memsize = sizeof(void *) * entries + sizeof(struct array_cache);
        struct array_cache *nc = NULL;
        /* 分配一个local cache对象,kmalloc从general cache中分配 */
        nc = kmalloc_node(memsize, gfp, node);
        /*
       * The array_cache structures contain pointers to free object.
       * However, when such objects are allocated or transfered to another
       * cache the pointers are not cleared and they could be counted as
       * valid references during a kmemleak scan. Therefore, kmemleak must
       * not scan such objects.
       */
        kmemleak_no_scan(nc);
       /* 初始化local cache */
        if (nc) {
                nc->avail = 0;
                nc->limit = entries;
                nc->batchcount = batchcount;
                nc->touched = 0;
                spin_lock_init(&nc->lock);
        }
        return nc;
} 源代码中涉及了slab的分配、释放等操作在后面分析中陆续总结。slab相关数据结构、工作机制以及整体框架在分析完了slab的创建、释放工作后再做总结,这样可能会对slab机制有更好的了解。当然,从代码中看运行机制会更有说服了,也是一种习惯。

在我心中舞动 发表于 2012-01-10 10:33

谢谢分享
页: [1]
查看完整版本: Linux内存管理之slab机制(初始化) 3...........