TCP的连接套接口哈希表初始化

发布于:2023-01-22 ⋅ 阅读:(128) ⋅ 点赞:(0)

在tcp_init中,初始化已建立连接的套接口哈希列表ehash,其中e代表Established。

void __init tcp_init(void)
{
    tcp_hashinfo.ehash =
        alloc_large_system_hash("TCP established",
                    sizeof(struct inet_ehash_bucket),
                    thash_entries,
                    17, /* one slot per 128 KB of memory */
                    0,
                    NULL,
                    &tcp_hashinfo.ehash_mask,
                    0,
                    thash_entries ? 0 : 512 * 1024);
}
需要注意的是tcp_hashinfo.ehash不仅包括已建立连接的TCP套接口,还包括除了在LISTEN状态的其它所有状态的套接口。

函数alloc_large_system_hash用来分配哈希表,其逻辑如下。首先如果参数中没有指定要分配的表项数量(numentries),则将其赋值为系统的内核页面数nr_kernel_pages,之后再减去特定处理器架构预留的内核页面。之后再将此页面数量按照每兆M字节所对应的页面数进行对其,即如果1M字节包括4个页面,则将numentries以4对其,至此,表项数量与页面数量还是相同的。

接下来是scale比例缩放的处理,scale是以2的幂值给定,与PAGE_SHIFT相同(通常情况下为12,即4K大小页面),如果scale参数值大于PAGE_SHIFT,对于tcp_hashinfo.ehash而言即是这种情况,其传入的scale值为17(128K),二者差值为5(32),右移5位相当于除以32,32个页面的大小为32*PAGE_SHIFT,按照4K页面计算32个页面大小为128K。

对于tcp_hashinfo.ehash哈希,意味着128K内存空间一个hash桶,此时numentries与页面数量不在相同,而是以2^scale次方为单位的数量。

    if (!numentries) {
        /* round applicable memory size up to nearest megabyte */
        numentries = nr_kernel_pages;
        numentries -= arch_reserved_kernel_pages();
 
        /* It isn't necessary when PAGE_SIZE >= 1MB */
        if (PAGE_SHIFT < 20)
            numentries = round_up(numentries, (1<<20)/PAGE_SIZE);
 
        /* limit to 1 bucket per 2^scale bytes of low memory */
        if (scale > PAGE_SHIFT)
            numentries >>= (scale - PAGE_SHIFT);
        else
            numentries <<= (PAGE_SHIFT - scale);
    }

如果参数中指定了要分配的numentries表项数量,将其roundup到最相近的2的幂运算的结果值。对于以上自计算得到表项数量numentries同样执行以下操作。

    numentries = roundup_pow_of_two(numentries);
在确定了哈希表项数量(numentries)之后,接下来分配所需的内存空间。对于tcp_hashinfo.ehash而言,其bucketsize为sizeof(struct inet_ehash_bucket)的大小。log2qty为表项数量的2的幂值,此值之后将用于计算函数回填的哈希表掩码值。实际分配空间的大小size,为bucketsize右移log2qty位得到,如果分配失败,递减log2qty的值,即将分配空间减少一半,直到分配成功或者size小到不足一个页面的大小为止。

    log2qty = ilog2(numentries);
 
    do {
        size = bucketsize << log2qty;
 
            if (get_order(size) < MAX_ORDER) {
                table = alloc_pages_exact(size, gfp_flags);
                kmemleak_alloc(table, size, 1, gfp_flags);
            }
    } while (!table && size > PAGE_SIZE && --log2qty);
哈希的shift和掩码值的计算就很简单了,以下运算最终得到一个全F的掩码值。

    if (_hash_shift)
        *_hash_shift = log2qty;
    if (_hash_mask)
        *_hash_mask = (1 << log2qty) - 1;
如下函数inet_ehash_bucket所示,hashinfo->ehash_mask(同_hash_mask)加上1实际上表示为哈希表的最大数量。

static inline struct inet_ehash_bucket *inet_ehash_bucket(struct inet_hashinfo *hashinfo, unsigned int hash)
{
    return &hashinfo->ehash[hash & hashinfo->ehash_mask];