NUMA的诞生是为了解决SMP架构下不断增多的CPU Core导致的性能问题,NUMA调整了CPU和内存的布局和访问关系。将CPU划分到多个Node节点上,每个Node都有自己独立的内存空间。各个node之间通过高速互联通讯。
CPU访问不同类型节点的内存是不相同的,访问本地节点的速度最快,访问远端节点的速度最慢,即访问速度与节点的距离有关,距离越远访问速度越慢,即非一致。在NUMA系统中,当linux内核收到内存分配请求时,它会优先从发出请求的CPU本地或邻近的内存node中寻找空闲内存,这种方式称为local allocation。
当网卡PCIe EP、内存、TX&RX CPU 不在同一个Numa上,如果存在数据拷贝,那么就会跨node去访问内存,对于CPU的消耗是比在本node上的消耗多的多的。因此,我们可以尽量使CPU,内存,以及TX&RX CPU均处在一个node上。
1. numa查看
查看网卡处在哪个node的方法如下,如果是-1,表示该主机只有一个node。
cat /sys/bus/pci/devices/0000\:01\:00.0/numa_node
查看node上的cpu以及相应的内存
查看伙伴系统内存,以我所处的服务器为例
查看硬中断所绑定的CPU。
2.中断绑定
网卡处理速度很快,如果将中断全部绑定在一个CPU上,就会造成CPU负载压力大,即使全部的时间都在处理中断也可能忙不过来。因此,需要根据网卡中不同的channel(每个channel都有tx和rx中断),而将网卡中断绑定到不同的CPU上。
那么网卡中断可以任意绑定到CPU上吗?答案是当然可以。但是考虑numa node,如果网卡中断绑定到非本numa节点上,那么CPU去处理中断,内存,就会跨node。虽然不影响工作,但性能方面就会受到很大的影响,尤其是当CPU处理速度略差的时候,性能损失的现象会更明显。
因此,我们需要将网卡中断绑定到网卡所在的numa节点上。
将中断绑定在网卡所处的node上的cpu步骤如下:
1.通过dev_to_node函数可以获取网卡所处的numa节点
priv->num_node = dev_to_node(pdev->dev);
2.申请中断后,设置中断处理的cpu。
ret = request_irq(priv->tx_irq[i],
stmmac_msi_intr_tx,
0, int_name, &priv->tx_queue[i]);
.....
cpumask_clear(cpu_mask);
/* assign tx irq to different cpu with rx irq */
cpumask_set_cpu(cpumask_local_spread(i , priv->num_node), cpu_mask);
irq_set_affinity_hint(priv->tx_irq[i], cpu_mask);
通过上述步骤,中断就会由网卡所在的numa节点上的cpu处理。
3.内存绑定
网卡内存申请的目标是最好所有的处理都在网卡所在的numa节点上的内存上,这样,CPU能更快速的访问网卡所在的numa节点的内存。那么如何申请网卡所在的内存呢,其实也比较简单。
使用kzalloc函数申请普通内存的时候需要显示指定node节点。
q_vector = kzalloc(struct_size(q_vector, ring, ring_count),
GFP_KERNEL)
q_vector = kzalloc_node(struct_size(q_vector, ring, ring_count),
GFP_KERNEL, node);
使用alloc_page申请page的时候同样需要显式指定node节点。不要使用dev_alloc_page申请,而使用alloc_page_node申请最好。
struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
unsigned int order)
那么申请流式dma或者一致性dma内存时,需要指定numa节点吗,这个不太需要,因为申请该内存的时候,会传入device参数,通过device参数就可以获取相应的numa节点,会倾向于该numa节点上的dma内存。
static inline void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)
{
return dma_alloc_attrs(dev, size, dma_handle, gfp,
(gfp & __GFP_NOWARN) ? DMA_ATTR_NO_WARN : 0);
}
通过将中断绑定到网卡所处numa节点上的CPU,以及申请内存时申请numa节点上的内存。这样,当网卡插到服务器上不同插槽时,也会避免跨numa节点带来的性能损失。