在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];
}