mm_init()

page_cgroup_init_flatmem(); 空函数,不产生任何实际指令.

mem_init();
pci_iommu_alloc();
    dma32_free_bootmem(); // 空函数,不产生任何实际指令
    sort_iommu_table(__iommu_table, __iommu_table_end);
        // @see arch/x86/include/asm/iommu_table.h
        // @see http://developer.amd.com/community/blog/2008/09/01/iommu/
        // @see https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit
        // @see https://en.wikipedia.org/wiki/List_of_IOMMU-supporting_hardware
        // @see http://support.amd.com/TechDocs/48882_IOMMU.pdf
        // @see http://osidays.com/osidays/wp-content/uploads/2014/12/Final_OSI2014_IOMMU_DetailedView_Sanil_Anurup.pdf
        __iommu_table是通过IOMMU_INIT_POST(add 2 entries),IOMMU_INIT_FINISH(add 3 entries),IOMMU_INIT(add one entry)编译出来的.
        sizeof(struct iommu_table_entry) = 40
        __iommu_table[6] = {
            IOMMU_INIT_FINISH(pci_xen_swiotlb_detect, 0, pci_xen_swiotlb_init, 0); // pci-swiotlb-xen.c
            IOMMU_INIT(pci_swiotlb_detect_4gb, pci_swiotlb_detect_override, pci_swiotlb_init, pci_swiotlb_late_init); // pci-swiotlb.c
            IOMMU_INIT_FINISH(pci_swiotlb_detect_override, pci_xen_swiotlb_detect, pci_swiotlb_init, pci_swiotlb_late_init); // pci-swiotlb.c
            IOMMU_INIT_POST(gart_iommu_hole_init); // pci-gart_64.c
            IOMMU_INIT_POST(detect_calgary); // pci-calgary_64.c
            IOMMU_INIT_FINISH(amd_iommu_detect, gart_iommu_hole_init, 0, 0); // amd_iommu_init.c
        }
        // 大概看了几眼
        //  1. PCI System Architecture
        //  2. PCI_Express_Base_Specification_Revision_3.0
        //  3. AMD I/O Virtualization Technology (IOMMU) Specification
        // 后,虽然对PCI,PCIe,IOMMU有了模糊的认识,但还是不清楚是怎么工作起来的.
        #define IOMMU_FINISH_IF_DETECTED (1<<0)
        #define IOMMU_DETECTED		 (1<<1)
        struct iommu_table_entry {
	        initcall_t	detect;
	        initcall_t	depend;
	        void		(*early_init)(void); /* No memory allocate available. */
	        void		(*late_init)(void); /* Yes, can allocate memory. */
	        int		flags;
        };
        __iommu_table[6] = { // 按depend关系排个序
            // detect                     depend                        early_init            late_init             flags(1-finish, 2-detected)
            {pci_xen_swiotlb_detect,      0,                           pci_xen_swiotlb_init, 0,                     1},
            {pci_swiotlb_detect_override, pci_xen_swiotlb_detect,      pci_swiotlb_init,     pci_swiotlb_late_init, 1},
            {pci_swiotlb_detect_4gb,      pci_swiotlb_detect_override, pci_swiotlb_init,     pci_swiotlb_late_init, 0},
            {gart_iommu_hole_init,        pci_swiotlb_detect_4gb,      0,                    0,                     0},
            {detect_calgary,              pci_swiotlb_detect_4gb,      0,                    0,                     0},
            {amd_iommu_detect,            gart_iommu_hole_init,        0,                    0,                     1}
        }
    check_iommu_entries(__iommu_table, __iommu_table_end); // 仅仅是检查下,别有循环依赖就好,从上边的table我们清楚的看到,没有循环依赖
    接下来一个for循环,调用每个entry的detect(),如果detect()返回值大于0,则标记下IOMMU_DETECTED,并执行early_init,如果这个entry标明了finish,就结束了.
    虽然还不清楚detect和early_init的逻辑,但基本上可以确定,这是在看硬件配置上有没有IOMMU设备,如果有就early_init.
    // pci_swiotlb_detect_4gb, 如果内存大于4GB并且没有明确指明no_iommu,那么这个函数就返回1了,此时就要执行pci_swiotlb_init做early_init了.
    // 不过现在我们只有128M内存,这个函数返回0
    // detect_calgary在BIOS EBDA内存块里寻找一个叫rio_table_hdr的东西,如果找到了,则进一步用read_pci_config去找IOMMU device.
    // Documentation/x86/x86_64/boot-options.txt 有对iommu的介绍, calgary iommu是IBM服务器的
    // amd_iommu_detect是去读取acpi table判断是否有iommu的,bochs没有
    最终,没有detect到iommu,所以这个函数什么也没做.不过如果有了6G内存的bochs,就要用soft iommu了.

numa_free_all_bootmem();
    free_all_bootmem_node(); // mm/nobootmem.c#0136
        register_page_bootmem_info_node(pgdat); // 空函数
    free_all_memory_core_early();
        get_free_all_memory_range();
        这里用到了 memblock 和 early_node_map
        memblock.memory来自e820的RAM部分,memblock.reserved是程序在运行过程中已经使用了的内存
        // bochs: writemem "/tmp/memblock.memdump" 0xffffffff81b3e9a0 64
        // bochs: writemem "/tmp/memblock.memory.memdump" 0xffffffff81b3f200 2048
        // bochs: writemem "/tmp/memblock.reserved.memdump" 0xffffffff81b3e9e0 2048
        /*
        $ ./print_memblock /tmp/memblock.memdump /tmp/memblock.memory.memdump /tmp/memblock.reserved.memdump
        sizeof(struct memblock) = 64
        sizeof memblock.memory/reserved = INIT_MEMBLOCK_REGIONS * sizeof(struct memblock_region) = 2048
        memblock.current_limit  = 0x7ff0000
        memblock.memory_size    = 0x7f7f000
        memblock.memory.cnt     = 0x2
        memblock.memory.max     = 0x80
        memblock.memory.regions = 0xffffffff81b3f200
        memblock.reserved.cnt     = 0x1a
        memblock.reserved.max     = 0x80
        memblock.reserved.regions = 0xffffffff81b3e9e0
        --memory regions--
        0: start=0x10000, end=0x9f000, size=0x8f000
        1: start=0x100000, end=0x7ff0000, size=0x7ef0000
        --reserved regions--
        0: start=0x9a000, end=0x9f000, size=0x5000          // TRAMPOLINE
        1: start=0x9fc00, end=0x100000, size=0x60400        // EBDA
        2: start=0x1000000, end=0x1d04049, size=0xd04049    // Kernel TEXT DATA BSS and extend_brk 49 bytes
        3: start=0x7200000, end=0x7400000, size=0x200000    // map_map[0] 第1个section的map
        4: start=0x796e000, end=0x79eb000, size=0x7d000     // RAMDISK
        5: start=0x7be6000, end=0x7be8000, size=0x2000      // 0xffea...的pud和pmd
        6: start=0x7c00000, end=0x7c1c000, size=0x1c000     // static percpu area
        7: start=0x7fb6000, end=0x7fee000, size=0x38000     // **pid_hash (0x7fb6000 0x1000)**
                                                            // level2_fixmap_pgt[507] -> NEW PTE (0x7fb7000 0x1000) map __vsyscall_0
                                                            // ZONE_DMA32 wait_table (0x7fb8000 0x18000)
                                                            // ZONE_DMA wait_table (0x7fd0000 0x18000)
                                                            // node_data[0] pglist_data (0x7fe8000 0x5000)
                                                            // THE PTE (0x7fed000 0x1000)
        8: start=0x7fee640, end=0x7fee890, size=0x250       // dchunk (0x7fee640 0x80)
                                                            // schunk (0x7fee6c0 0x80)
                                                            // pcpu_slot (0x7fee740 0x150)
        9: start=0x7fee8c0, end=0x7fee8c8, size=0x8         // pcpu_unit_offsets
        10: start=0x7fee900, end=0x7fee904, size=0x4        // pcpu_unit_map
        11: start=0x7fee940, end=0x7fee948, size=0x8        // pcpu_group_sizes
        12: start=0x7fee980, end=0x7fee988, size=0x8        // pcpu_group_offsets
        13: start=0x7fee9c0, end=0x7fee9d4, size=0x14        // static_command_line
        14: start=0x7feea00, end=0x7feea14, size=0x14        // saved_command_line
        15: start=0x7feea40, end=0x7feea60, size=0x20        // nosave_region
        16: start=0x7feea80, end=0x7feeae8, size=0x68       // e820_saved firmware_map_entry
        17: start=0x7feeb00, end=0x7feeb68, size=0x68       // e820_saved firmware_map_entry
        18: start=0x7feeb80, end=0x7feebe8, size=0x68       // e820_saved firmware_map_entry
        19: start=0x7feec00, end=0x7feec68, size=0x68       // e820_saved firmware_map_entry
        20: start=0x7feec80, end=0x7feece8, size=0x68       // e820_saved firmware_map_entry
        21: start=0x7feed00, end=0x7feed68, size=0x68       // e820_saved firmware_map_entry
        22: start=0x7feed80, end=0x7feef08, size=0x188      // e820_res
        23: start=0x7feef40, end=0x7feef83, size=0x43       // ioapic_resources
        24: start=0x7feefc0, end=0x7feefd8, size=0x18       // 第1个section的usemap
        25: start=0x7fef000, end=0x7ff0000, size=0x1000     // mem_section[0]
        */
        early_node_map 里的两个 active_region 是
        /*
            0x10 - 0x9f
            0x100 - 0x7ff0
        */
        memblock.reserved.cnt = 0x1a = 26
        count_early_node_map(0) = 2
        count = (26 + 2) * 2 = 28 * 2 = 56
        /*
            struct range {
                u64 start;
                u64 end;
            }
        */
        在 memblock 里找一块内存, 大小为 54 * sizeof (struct range) = 54 * 16 = 0x380
        range = 0x7fee2c0
        然后计算下可用的RAM块都有哪些,保存到range里:
        /*
            start       end
            0x10    -   0x9a    // 9a   - 9f        TRAMPOLINE
            0x100   -   0x1000  // 1000 - 1d05      Kernel TEXT DATA BSS and extend_brk 49 bytes
            0x1d05  -   0x7200  // 7200 - 7400      map_map[0] 第1个section的map
            0x7400  -   0x796e  // 796e - 79eb      RAMDISK
            0x79eb  -   0x7be6  // 7be6 - 7be8      0xffea...的pud和pmd
            0x7be8  -   0x7c00  // 7c00 - 7c1c      static percpu area
            0x7c1c  -   0x7fb6  // 后边的内存即使有空隙也不够1个page
         */
         __free_pages_memory(start, end);
            // 到这里一定得再看看 understanding kernel 的 The Buddy System Algorithm, 这个算法有两个关键点:
            // 1) All free page frames are grouped into 11 lists of blocks that contain groups of
            //      1, 2, 4, 8, 16, 32, 64, 128, 256, 512, and 1024
            //    contiguous page frames, respectively.
            // 2) The physical address of the first page frame of a block is a multiple of the group size
            // 现在给一个range,要把这个range里的page加到相应的list里,有很多种办法,
            // 比如一个page接一个page的添加,然后按照Buddy System Algorithm一点点的向上合并,但这个办法有点不够高效
            // 还可以选择一个中间值,比如64,然后每次64个page的添加,但为了满足算法第2点的要求,range里第一个64个page块的开始点有要求,必须是64的倍数
            // 这就需要掐头去尾(如下图),头尾部分一个page一个page的添加,当中部分每64个page的添加

            start        64*n                                64*m                end
            |     part1    |           part2                   |    part3         |

            // 具体的添加逻辑就是 __free_pages_bootmem(page, order)
            // order = 0 就是添加头尾
            // order = 6 就是添加当中部分
            // 但有一点不明白的就是, zone.free_area[MAX_ORDER] 的 MAX_ORDER 是定义死的 11, 这里选取中间值时是根据BITS_PER_LONG选取的,这是怎么个关系?

            order = ilog2(BITS_PER_LONG) = ilog2(64) = 6
            part1和part3部分的每个page __free_pages_bootmem(page, 0)
            part2部分每64个page,      __free_pages_bootmem(page, 6)

            __free_pages_bootmem(page, order)
            // __free_pages_bootmem里不管order值是几,先把指定的page(s)清除掉reserved标记
            // 前边setup_arch里free_area_init_core时,memmap_init把每个page都标记为reserved了
            // 然后调用 set_page_count(page, 0)把page->_count设为0,再调用 set_page_refcounted(page)把page->_count设为1
            // set_page_refcounted的注释里说 Turn a non-refcounted page (->_count == 0) into refcounted with a count of one.
            // 还是不明白把page->_count设为1的用处
            // 然后关键的逻辑就在 __free_pages(page, order) 里了. __free_page(page) => __free_pages(page, 0)
            __free_pages(page, order)
            // 如果put_page_testzero(page)返回true,那么才会处理这个page(s)
            // put_page_testzero的注释写的很清楚 Drop a ref, return true if the refcount fell to zero (the page has no users)
            // 前边我们知道 page->_count 被 set_page_refcounted 设为1了,所以现在这个函数返回值都是true
            // 如果order=0,则 free_hot_cold_page(page, 0) => free hot page, 否则 __free_pages_ok(page, order)
            // 关于hot code,可以看 understanding kernel 的 The Per-CPU Page Frame Cache 部分
            // 在 memap_init 时, megratetype被设成了MIGRATE_MOVABLE
            // zone->pageset 在 free_area_init_core 的 zone_pcp_init 里被指向了 boot_pageset, 而boot_pageset是个 per cpu 变量,
            // setup_per_cpu_areas之后,每个cpu都有一个自己的boot_pageset,地址是%gs:boot_pageset,对我们的情况,就是0x7c10900
            // 在bochs里查看内存知道,当前pcp->high = 0, pcp->batch = 1,
            // 所以每向pcp里加入一个page,就会调用 free_pcppages_bulk => free_one_page 把这个page加到free_area里
            // __free_pages_ok直接调用free_one_page把pages(对目前的情况是64个page)加到free_area里
            // free_one_page的逻辑如 understanding kernel 里所述,就是一点点向上合并的

            // 下边我们根据算法描述,用php写一个小程序,把每个range分成上述3部分,然后加到free_area里
            // 由于free_area是在zone里的,range2freearea.php每次只计算一个zone的range
            php range2freearea.php dma-ranges.php
            php range2freearea.php dma32-ranges.php
            // 怎么能打印出node_data.zone.free_area[MAX_ORDER].free_list[MIGRATE_PCPTYPES]里对应的pfn呢?
            struct free_area {
                struct list_head free_list[MIGRATE_PCPTYPES];
                unsigned long nr_free;
            }
            如果list_head指向自身,那么这个free_list是空的,但是我们自己写程序的话,mmap后,指的地址肯定和bochs里的不一样了,
            这就判断不出来free_list是空的还是仅有一项(free_list是一个循环链接)
            我们可以这样判断,struct page的地址是0xffffea....这样的,如果free_list指向的地址是这个范围的,那么就不是空的,
            如果指向的地址是0xffff88...这样的,就是空的
            然后,sizeof(struct page) = 0x38, 而page->lru在struct page的最后,那么 ((&page->lru - 0x28) - 0xffffea00...) / 0x38 就是 pfn 了
            // bochs: writemem "/tmp/node_data.memdump" 0xffff880007fe8000 0x5000
            // bochs: writemem "/tmp/section_mem_map.memdump" 0xffff880007200000 0x200000
            ./print_node_data /tmp/node_data.memdump /tmp/section_mem_map.memdump

totalram_pages = 0xf8a + 0x6016 = 0x6fa0
absent_pages   = 0x10 + (0x100 - 0x9f) = 0x71
reservedpages  = 0x7ff0 - 0x6fa0 - 0x71 = 0xfdf

after_bootmem = 1

codesize = 0x15e48dc - 0x1000000 = 0x5e48dc
datasize = 0x1acc280 - 0x15e48dc = 0x4e79a4
initsize = 0x1ba8000 - 0x1ace000 = 0xda000

/* Register memory areas for /proc/kcore */
kclist_add(&kcore_vsyscall, (void *)VSYSCALL_START, VSYSCALL_END - VSYSCALL_START, KCORE_OTHER);
                            0xFFFFFFFFFF600000      0x800000

syslog打印出 Memory: (0x6fa0*4=0x1be80)k/(0x7ff0*4=0x1ffc0)k available (
                        (0x5e48dc>>10=0x1792)k kernel code,
                        (0x71*4=0x1c4)k absent,
                        (0xfdf*4=0x3f7c)k reserved,
                        (0x4e79a4>>10=139e)k data,
                        (0xda000>>10=368)k init
                    )

kmem_cache_init(); .config里定义了 CONFIG_SLUB=y, 所以这个函数我们看slub.c里的.
struct kmem_cache {
    //...
    struct kmem_cache_node *node[MAX_NUMNODES]; // MAX_NUMNODES = 64, 显然是要为每个node准备一个kmem_cache_node
};

kmem_size = offsetof(struct kmem_cache, node) // offsetof的意思是kmem_cache从开头到node(不包括node)占有多少内存
            + nr_node_ids * sizeof(struct kmem_cache_node *); // 虽然struct kmem_cache里定义的是MAX_NUMNODES,但真的要分配内存的时候
                                                              // 实际有几个node才分配几个,并不是真的分配MAX_NUMNODES
                                                              // x86_64只有一个dummy node.
          = 0xc0 + 1 * 8 = 0xc8
kmalloc_size = ALIGN(kmem_size, cache_line_size()) = ALIGN(0xc8, x86_cache_alignment=0x40) = 0x100
order = get_order(2 * kmalloc_size) = get_order(0x200) = 0
kmem_cache = (void *)__get_free_pages(GFP_NOWAIT, order); // kmem_cache 是全局变量 static struct kmem_cache *kmem_cache;

// 上边这段代码get了1个page,用来保存两个kmem_cache, bochs追踪可知拿到的这个page是0x7400. 
// (这就让人搞不懂了,这里明明只需要1个page就够了,为什么分配的时候把0x7400这个4M的大块内存给拆了呢?)

kmem_cache_node = (void *)kmem_cache + kmalloc_size; // kmem_cache_node 也是个全局变量 static struct kmem_cache *kmem_cache_node;
// 分配的这1个page,前0x100字节是 kmem_cache, 接下来的 0x100字节是 kmem_cache_node

kmem_cache_open(kmem_cache_node, "kmem_cache_node",
	sizeof(struct kmem_cache_node),
	0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
/*
static int kmem_cache_open(struct kmem_cache *s, const char *name,
    size_t size,
    size_t align, unsigned long flags, void (*ctor)(void *))
*/
    s->name    = "kmem_cache_node";
    s->ctor    = NULL;
    s->objsize = sizeof(struct kmem_cache_node) = 0x40;
    s->align   = 0;
    s->flags   = kmem_cache_flags(size, flags, name, ctor) = SLAB_HWCACHE_ALIGN | SLAB_PANIC; // 如果没开slub_debug的话,这个函数简单的返回flags
    s->reserved = 0;
    calculate_sizes(s, -1);
        struct kmem_cache_node {
            spinlock_t list_lock;
            unsigned long nr_partial;
            struct list_head partial;
            atomic_long_t nr_slabs;
            atomic_long_t total_objects;
            struct list_head full;
        }; // size = 64 = 0x40
        s->inuse = size = 0x40;
    // bochs: writemem "/tmp/kmem_cache.memdump" 0xFFFF880007400100 0xc8
    /* ./print_kmem_cache /tmp/kmem_cache.memdump
        sizeof(struct kmem_cache) = 0xc8
        size    = 0x40
        objsize = 0x40
        offset  = 0
        inuse   = 0x40
        align   = 0x40
        oo      = 0x40
        min     = 0x40
        max     = 0x40
    */
    // 有了 print_kmem_cache, calculate_sizes 好理解多了,看起来是这样子的:
    // 一个object除了其自身实际大小(objsize, inuse)外,还可以嵌入其它信息,所以它最终占有的大小(size)可能要比自身大
    // 根据最终确定的size大小,可以计算出这个object的slab为多大最为合适,然后也就可以计算出这个slab可以放多少个object了
    // 比如struct kmem_cache的大小是0x40,它没有嵌入其它额外信息,也没有reserved,那么它的objsize = inuse = size = 0x40,它的slab大小为1个page, order = 0,可以放下0x40个struct kmem_cache
    // oo,min,max是struct kmem_cache_order_objects类型的,如其名字所示,其实它们不是0x40,而是0x0040,0-15位保存的是objects,16-31位保存的是order
    // @see http://lwn.net/Articles/229984/ 这篇很短的文章把slub介绍的挺好的,不过和当前的代码比起来还是对不上了
    s->min_partial = 6 // 不知道做什么用的
    s->refcount = 1
    s->remote_node_defrag_ratio = 1000;
    init_kmem_cache_nodes(s);
        early_kmem_cache_node_alloc();
            page = new_slab(); // 前边我们把slab的大小(order)嵌入了kmem_cache.oo的16-31位,这里取出来,根据order分配page,如果失败,则按min的分配
                               // 分配成功后,struct page->objects = oo_objects(oo); oo的0-15位保存着objects的数量
                               // struct kmem_cache描述了这个slab的详细构造,这样的slab在一个node里有多少个,总共有多少个objects,用struct kmem_cache_node记录
                               // 每new出一个slab,对应kmem_cache_node的nr_slabs++, total_objects+=objects
                               // 这就相当于面向对象里的 class 和 instance 的关系, struct kmem_cache相当于class,给出类的定义,每一个slab相当于instance
                               // 不过当前的情况是,还没有kmem_cache_node, kmem_cache.node=NULL,所以这次的记数操作当前做不了,后边会补上
                               // page->slab = s;
                               // page->flags |= 1 << PG_slab;
                               // objects之间在object.offset处当成指针串联起来
                               // page->free_list = start;
                               // page->inuse = 0;
                               OK, 到此,一个用作slab的page算是清楚了
                               struct page {
                                    unsigned long flags; // PG_slab 标记表明这个page用作了slab
                                    atomic_t _count;
                                    struct {
                                        u16 inuse;      // inuse表明有多少个object已经分配出去在用了
                                        u16 objects;    // objects表示这个slab总共有多少个object
                                    };
                                    struct kmem_cache *slab;    // 指向 kmem_cache, 也就是这个slab的定义
                                    void *freelist;             // 指向slab里第一个free_list
                                    struct list_head lru;
                                };
            struct kmem_cache_node *n = page->freelist;
            page->freelist = get_freepointer(kmem_cache_node, n);
            page->inuse++;
            kmem_cache_node->node[node] = n;
            init_kmem_cache_node(n, kmem_cache_node);
            inc_slabs_node(kmem_cache_node, node, page->objects);
            // 前边new_slab分配了1个page,0x40个struct kmem_cache_node object,这里取一个object给kmem_cache_node用,并且补上nr_slabs total_objects记数
            add_partial(n, page, 0);
            // 由于前边用一个object,这个slab成为partial了,加到partial list里
    alloc_kmem_cache_cpus();
        s->cpu_slab = __alloc_percpu(sizeof(struct kmem_cache_cpu), 2 * sizeof(void *)); // => pcpu_alloc(size, align, reserved=false);
        init_kmem_cache_cpus(s); // kmem_cache_cpu.tid = cpu number;

    // OK, 到此我们有了第一个slab,这个slab里的object都是struct kmem_cache_node

slab_state = PARTIAL; // slab的状态分为4个, static enum {
                      //      DOWN,       /* Nodes slab functionality available */
                      //      PARTIAL,    /* Kmem_cache_node works */
                      //      UP,         /* Everything works but does not show up in sysfs */
                      //      SYSFS       /* Sysfs up */
                      // }, 经过上边的kmem_cache_open后,我们从DOWN进入到了PARTIAL状态

temp_kmem_cache = kmem_cache;
kmem_cache_open(kmem_cache, "kmem_cache", kmem_size,
	0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
kmem_cache = kmem_cache_alloc(kmem_cache, GFP_NOWAIT);
memcpy(kmem_cache, temp_kmem_cache, kmem_size);
// 可以预见,struct kmem_cache这个object需求量会很大,因为每个类型的slab都需要一个对应的kmem_cache,那现在就先open出来一个kmem_cache的slab
// 第一步还是要构造slab的描述信息,包括object的大小等等
// 然后在init_kmem_cache_nodes()时,现在slab_state已经不是DOWN了,直接从上边分配好的slab里取一个struct kmem_cache_node object就好了
// 把取出来的kmem_cache_node设到kmem_cache里,然后初始化这个kmem_cache_node
// 再alloc_kmem_cache_cpus(), 其实这个函数做的事挺简单的,就是从 dchunk 里给每个cpu alloc出一个struct kmem_cache_cpu,并把对应的cpu number设上

第二次调用kmem_cache_open,让我们基本上理解了slub的设计,这里小结一下:
    假设我们需要一个objectA,slub会分配出一块内存slabA-1,这块内存看作objectA[NR].如果这NR个objectA用完了,slub再分配一块内存slabA-2,还看作objectA[NR].如此类推.
    那现在问题来了,每次分配的时候这块内存应该是多大呢?这个大小的计算就是上边的 calculate_sizes() 做的事,出于其它各种现在还不明白的需求,objectA除了自身以外,
    还需要一些空间来存储meta_info,最终 calculate_sizes() 计算出每次分配时应分配几个page,以及每次分配能分配出来多少个object,也就是NR等于几.
    这些计算出来的信息需要一个数据结构存储,这个数据结构就是 struct kmem_cache.
    还有一个问题是,我们会分配多次,这样就会有很多个slabA-1,slabA-2,...,slabA-N,那到底有多个少,哪些slab里还有free的object可用,这也需要一个数据结构来存储.
    这个数据结构就是 struct kmem_cache_node.
    OK, 理一下依赖关系, struct kmem_cache 用来描述slab的静态信息, struct kmem_cache_node 用来记录slab的实际使用情况.
    显然, kmem_cache 描述的slab会有很多, 每个类型的slab都需要一个 kmem_cache_node, 所以我们先要准备一大堆的 kmem_cache_node 供后边使用.
    这就是前边第一次调用kmem_cache_open做的事. 它分配一块内存,用作struct kmem_cache_node[NR],然后拿出其中一个,记录自身的实际使用情况.(有点鸡生蛋,蛋生鸡的意思)
    一旦kmem_cache_node ready之后,再次调用kmem_cache_open就简单了,一样的调用 calculate_sizes() 计算出来应该分配多大内存,以及其它信息保存到kmem_cache里,再取一个kmem_cache_node用来记录实际使用情况.这就好了.
    真正的alloc内存会等到实际需要的时候才alloc.具体逻辑在slab_alloc()函数里.
    slab_alloc()先检查kmem_cache.cpu_slab->freelist是否指向了可用了object.我们知道alloc_kmem_cache_cpus()里仅仅给每个cpu分配了一个 kmem_cache_cpu 的数据结构,并没有初始化 freelist.
    先说简单的情况,如果freelist指向了可用的object,那就直接返回就完了.否则,调用 __slab_alloc() 函数获取object并初始化cpu_slab.freelist.
    __slab_alloc()的注释说的也很清楚,kmem_cache_node记录着可用的partial slab,如果有,把这个slab设成cpu_slab并返回object.
    如果没有partial_slab,那就要new_slab,调用page allocator根据kmem_cache的描述分配新的内存,初始化page,objects,并把这个新的slab加到kmem_cache_node里去,然后把它设为cpu_slab并返回object.

    struct kmem_cache_node {
        spinlock_t list_lock;
        unsigned long nr_partial;       // 有多少个partial的slab
        struct list_head partial;       // 把所有partial的slab连起来
        atomic_long_t nr_slabs;         // 一共有多少个slab
        atomic_long_t total_objects;    // 所有slab里总共有多少objects
        struct list_head full;
    };

    struct kmem_cache {
        struct kmem_cache_cpu __percpu *cpu_slab;
        unsigned long flags;
        unsigned long min_partial;
        int size;                   /* The size of an object including meta data */
        int objsize;                /* The size of an object without meta data */
        int offset;                 /* Free pointer offset. */
        struct kmem_cache_order_objects oo; // oo其实就是个unsigned long,不过当前只用到了低32位.0-15记录着分配多少objects合适,16-31位记录着分配page时的order
        struct kmem_cache_order_objects max;
        struct kmem_cache_order_objects min;
        gfp_t allocflags;
        int refcount;
        void (*ctor)(void *);   // slab分配出来后,如果需要,可以对里边的每个object做初始化
        int inuse;              /* Offset to metadata */
        int align;
        int reserved;           /* Reserved bytes at the end of slab */
        const char *name;       /* Name (only for display!) */
        struct list_head list;  /* List of slab caches */
        struct kobject kobj;    /* For sysfs */
        int remote_node_defrag_ratio;
        struct kmem_cache_node *node[MAX_NUMNODES];
    };

// 继续看代码,第二次kmem_cache_open new了一个kmem_cache的slab,然后从中取一个,把当前的kmem_cache copy过去. 
// 注意: 当前的kmem_cache是调用page allocator分配出来的, memcpy之后,新的kmem_cache是调用slub allocator分配出来的.
// 同理, 把kmem_cache_node也用slub allocator分配的替换掉
// 前边刚开始 new kmem_cache_node slab 时,从page allocator那里分配的page.slab指向了老的kmem_cache_node,现在要把它们更正过来.这就是kmem_cache_bootstrap_fixup(kmem_cache_node);做的事
// 同理,新的kmem_cache alloc时把page.slab指向了老的,也需要fixup.
// 此时,从page allocator那里分配出来的1个page就没用了,free掉

// 然后调用 create_kmalloc_cache 创建了各种不同大小的 slab, 这个过程和第二次kmem_cache_open比较类似,区别在于在open之前要先从kmem_cache slab里取一个object出来计算好对应大小的 kmem_cache 描述

slab_state = UP; // Everything works but does not show up in sysfs, kmalloc is ready

// 前边创建不同大小的slab时,对应的kmem_cache.name固定为"kmalloc",现在内存分配方便了,把名字修正过来,简单的说,就是从slab里取出object,把正确的字符串copy过去,然后赋值给kmem_cache.name

// 最后,再单独为 dma create一系列不同大小的slab.

// syslog里打印出 SLUB: Genslabs=%d, HWalign=%d, Order=%d-%d, MinObjects=%d, CPUs=%d, Nodes=%d

// 从slab_caches这个list_head开始,可以找到所有不同大小的slab
// static struct kmem_cache *kmalloc_dma_caches[SLUB_PAGE_SHIFT];
// struct kmem_cache *kmalloc_caches[SLUB_PAGE_SHIFT];

追踪bochs执行,再来过一遍:

kmem_size = 0xc8, align过后kmalloc_size = 0x100, order = 0, 调用page_allocator分配一个page
kmem_cache = 0x7400000;
kmem_cache_node = kmem_cache + kmalloc_size = 0x7400000 + 0x100 = 0x7400100

kmem_cache_open(kmem_cache_node, "kmem_cache_node") new 了一个 kmem_cache_node 的 slab
// bochs: writemem "/tmp/kmem_cache.memdump" 0xFFFF880007400100 0xc8
/* ./print_kmem_cache /tmp/kmem_cache.memdump
    sizeof(struct kmem_cache) = 0xc8
    cpu_slab = 0x16d00      // dchunk就是从 0x14d00(static) + 0x2000(reserved) = 0x16d00开始的
    size = 0x40, objsize = 0x40, offset = 0, inuse = 0x40
    oo = 0x40, order = 0, objects = 64
    min = 0x40, order = 0, objects = 64
    align = 0x40
    reserved = 0
    list.prev = (nil), list.next = (nil)
    kmem_cache_node = 0xffff880007401000
*/

kmem_cache_open(kmem_cache, "kmem_cache") new 了一个 kmem_cache 的 slab
// bochs: writemem "/tmp/kmem_cache.memdump" 0xFFFF880007400000 0xc8
/* ./print_kmem_cache /tmp/kmem_cache.memdump
    sizeof(struct kmem_cache) = 0xc8
    cpu_slab = 0x16d20
    size = 0x100, objsize = 0xc8, offset = 0, inuse = 0xc8
    oo = 0x10, order = 0, objects = 16
    min = 0x10, order = 0, objects = 16
    align = 0x40
    reserved = 0
    list.prev = (nil), list.next = (nil)
    kmem_cache_node = 0xffff880007401040
*/
kmem_cache = kmem_cache_alloc(kmem_cache, GFP_NOWAIT);  // slub分配的kmem_cache = 0x7402000, bochs里查看内存会发现 0x7402000 处的值是 0x7402100
memcpy(kmem_cache, temp_kmem_cache, kmem_size);         // kmem_cache的size=0x100,offset=0,所以每个object的开头指向下一个free的object,就是0x7402100了

kmem_cache_node = kmem_cache_alloc(kmem_cache, GFP_NOWAIT); // kmem_cache_node = 0x7402100
memcpy(kmem_cache_node, temp_kmem_cache_node, kmem_size);

// 每个slab对应的kmem_cache.node记录着有多少个slab,node.partial这个链表链接着partial slab对应的page,page.freelist指向slab里第一个free object,page.slab反过来指向kmem_cache
// 一个slab一旦被设为cpu_slab,它就不是partial了,前边kmem_cache_open(kmem_cache, "kmem_cache")过程中,alloc kmem_cache_node时把 kmem_cache_node slab给设成cpu_slab了,所以它已经不在partial里了
kmem_cache_bootstrap_fixup(kmem_cache_node); // 如上所述,kmem_cache_node->node->partial是空的,所以这个函数什么也没做
kmem_cache_bootstrap_fixup(kmem_cache); // 同理,kmem_cache->node->partial也是空的,这个函数也什么都没做

free_pages((unsigned long)temp_kmem_cache, order); // 现在kmem_cache和kmem_cache_node用的都是slub分配的了,free掉刚开始从page allocator那里获取的1个apge

// KMALLOC_MIN_SIZE = 8;
kmalloc_caches[1] = create_kmalloc_cache("kmalloc-96", 96, 0) = 0x7402200; // 由于 KMALLOC_MIN_SIZE 是#define的,前边的一堆代码在编译时就能判断出来不相等,所以直接就丢掉了
kmalloc_caches[2] = create_kmalloc_cache("kmalloc-192", 192, 0) = 0x7402300;
// KMALLOC_SHIFT_LOW = 3; SLUB_PAGE_SHIFT = PAGE_SHIFT + 2 = 12 + 2 = 14;
kmalloc_caches[3] = create_kmalloc_cache("kmalloc", 8, 0) = 0x7402400;
kmalloc_caches[4] = create_kmalloc_cache("kmalloc", 16, 0) = 0x7402500;
kmalloc_caches[5] = create_kmalloc_cache("kmalloc", 32, 0) = 0x7402600;
...
kmalloc_caches[13] = create_kmalloc_cache("kmalloc", 0x2000, 0) = 0x7402e00; // 到此,kmem_cache的第一个slab里16个object已经用了15个了

slab_state = UP;

上边3-13的kmalloc name都设成了"kmalloc",现在更正过来, 虽然1和2的name是对了,也使用slub分配的内存存储name
这些name当中,最短的是"kmalloc-8"=9个字符,最长的是"kmalloc-8192"=12个字符,所以这些name都使用kmalloc-16的slab,这个slab对应的内存是0x7400000,
bochs里查看内存就能看到这些name的字符串

.config里定义了 CONFIG_ZONE_DMA=y
kmalloc_dma_caches[1] = create_kmalloc_cache("dma-kmalloc-96", 96, SLAB_CACHE_DMA) = 0x7402f00; // 到此,kmem_cache的第一个slab里16个object已经用完了
kmalloc_dma_caches[2] = create_kmalloc_cache("dma-kmalloc-192", 192, SLAB_CACHE_DMA) = 0x7403000; // 再new一个slab
kmalloc_dma_caches[3] = create_kmalloc_cache("dma-kmalloc-8", 8, SLAB_CACHE_DMA) = 0x7403100;
kmalloc_dma_caches[4] = create_kmalloc_cache("dma-kmalloc-16", 16, SLAB_CACHE_DMA) = 0x7403200;
...
kmalloc_dma_caches[13] = create_kmalloc_cache("dma-kmalloc-8192", 0x2000, SLAB_CACHE_DMA) = 0x7403b00;

syslog里打印出 SLUB: Genslabs=15, HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1

// 这里值得一说的是kmalloc函数的逻辑,前边我们也看到了,最大的slab是8192个字节,也就是0x2000=8K,如果申请的内存大于8K,那就不能用slab了
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
	if (__builtin_constant_p(size)) {
		if (size > SLUB_MAX_SIZE)
			return kmalloc_large(size, flags); // => 直接从page allocator那里分配

		if (!(flags & SLUB_DMA)) {
			struct kmem_cache *s = kmalloc_slab(size); // 找到合适size的slab

			if (!s)
				return ZERO_SIZE_PTR;

			return kmem_cache_alloc_trace(s, flags, size); // => 从slab中取object返回
		}
	}
	return __kmalloc(size, flags); // => get_slab(size, flags) 如果 SLUB_DMA, 则从 kmalloc_dma_caches 里分配
}

percpu_init_late(); 这个函数的注释里写的很清楚, dchunk和reserved chunk用的map在initdata里,现在slab已经好了,把这个map给重新分配一下. 难道后边initdata会free掉?好像在哪里见到过这样的说法.

pgtable_cache_init(); 空函数,不产生任何实际指令.

vmalloc_init(); 由于vmlist = NULL, 所以这个函数实际上没做什么事.
初始化了 percpu vmap_block_queue, 但这个有什么用,现在还不知道.
vmap_initialized = true;
understanding kernel里有对vmalloc的介绍,大意就是通过pagetable把不连续的内存map成连续的.这个地址范围是:
#define VMALLOC_START    _AC(0xffffc90000000000, UL)
#define VMALLOC_END      _AC(0xffffe8ffffffffff, UL)
#define VMEMMAP_START    _AC(0xffffea0000000000, UL) // 这个地址范围我们已经很熟悉了,每个page对应的struct page就在这个范围里
#define MODULES_VADDR    _AC(0xffffffffa0000000, UL)
#define MODULES_END      _AC(0xffffffffff000000, UL)