ixgbe_up_complete() -> ixgbe_configure_msix() -> ixgbe_set_ivar() ixgbe_napi_enable_all() -> napi_enable() ixgbe_irq_enable() -> ixgbe_irq_enable_queues() netif_tx_start_all_queues() -> netif_tx_start_queue()
+struct ixgbe_ring { + struct ixgbe_adapter *adapter; /* backlink */ + void *desc; /* descriptor ring memory */ + dma_addr_t dma; /* phys. address of descriptor ring */ + unsigned int size; /* length in bytes */ + unsigned int count; /* amount of descriptors */ + unsigned int next_to_use; + unsigned int next_to_clean; + + union { + struct ixgbe_tx_buffer *tx_buffer_info; + struct ixgbe_rx_buffer *rx_buffer_info; + }; + + u16 head; + u16 tail; + + /* To protect race between sender and clean_tx_irq */ + spinlock_t tx_lock; + + struct ixgbe_queue_stats stats; + + u32 eims_value; + u32 itr_val; + u16 itr_range; + u16 itr_register; + + char name[IFNAMSIZ + 5]; +};
+static void ixgbe_configure_msix(struct ixgbe_adapter *adapter) +{ + int i, vector = 0; + + for (i = 0; i < adapter->num_tx_queues; i++) { + ixgbe_set_ivar(adapter, IXGBE_IVAR_TX_QUEUE(i), + IXGBE_MSIX_VECTOR(vector)); + if (adapter->tx_eitr != 0) + writel(1000000000 / (adapter->tx_eitr * 256), + adapter->hw.hw_addr + + adapter->tx_ring[i].itr_register); + else + writel(0, adapter->hw.hw_addr + + adapter->tx_ring[i].itr_register); + vector++; + } + + for (i = 0; i < adapter->num_rx_queues; i++) { + ixgbe_set_ivar(adapter, IXGBE_IVAR_RX_QUEUE(i), + IXGBE_MSIX_VECTOR(vector)); + if (adapter->rx_eitr != 0) + writel(1000000000 / (adapter->rx_eitr * 256), + adapter->hw.hw_addr + + adapter->rx_ring[i].itr_register); + else + writel(0, adapter->hw.hw_addr + + adapter->rx_ring[i].itr_register); + vector++; + } + + vector = adapter->num_tx_queues + adapter->num_rx_queues; + ixgbe_set_ivar(adapter, IXGBE_IVAR_OTHER_CAUSES_INDEX, + IXGBE_MSIX_VECTOR(vector)); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_EITR(vector), 1950); +}
/* * ixgbe_set_ivar - set the IVAR registers, mapping interrupt causes to vectors * @adapter: pointer to adapter struct * @direction: 0 for Rx, 1 for Tx, -1 for other causes * @queue: queue to map the corresponding interrupt to * @msix_vector: the vector to map to the corresponding queue * */ static void ixgbe_set_ivar(struct ixgbe_adapter *adapter, s8 direction, u8 queue, u8 msix_vector) { u32 ivar, index; struct ixgbe_hw *hw = &adapter->hw; switch (hw->mac.type) { case ixgbe_mac_82598EB: msix_vector |= IXGBE_IVAR_ALLOC_VAL; if (direction == -1) direction = 0; index = (((direction * 64) + queue) >> 2) & 0x1F; ivar = IXGBE_READ_REG(hw, IXGBE_IVAR(index)); ivar &= ~(0xFF << (8 * (queue & 0x3))); ivar |= (msix_vector << (8 * (queue & 0x3))); IXGBE_WRITE_REG(hw, IXGBE_IVAR(index), ivar); break; case ixgbe_mac_82599EB: if (direction == -1) { /* other causes */ msix_vector |= IXGBE_IVAR_ALLOC_VAL; index = ((queue & 1) * 8); ivar = IXGBE_READ_REG(&adapter->hw, IXGBE_IVAR_MISC); ivar &= ~(0xFF << index); ivar |= (msix_vector << index); IXGBE_WRITE_REG(&adapter->hw, IXGBE_IVAR_MISC, ivar); break; } else { /* tx or rx causes */ msix_vector |= IXGBE_IVAR_ALLOC_VAL; index = ((16 * (queue & 1)) + (8 * direction)); ivar = IXGBE_READ_REG(hw, IXGBE_IVAR(queue >> 1)); ivar &= ~(0xFF << index); ivar |= (msix_vector << index); IXGBE_WRITE_REG(hw, IXGBE_IVAR(queue >> 1), ivar); break; } default: break; } }
https://tech.meituan.com/2018/03/16/redis-high-concurrency-optimization.html
(1)注册中断号及中断处理程序,根据网卡是否支持MSI/MSIX
,结果为:MSIX
→ ixgbe_msix_clean_rings
,MSI
→ ixgbe_intr
,都不支持 → ixgbe_intr
。
/**
* 文件:ixgbe_main.c
* ixgbe_request_irq - initialize interrupts
* @adapter: board private structure
*
* Attempts to configure interrupts using the best available
* capabilities of the hardware and kernel.
**/
static int ixgbe_request_irq(struct ixgbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
int err;
/* 支持MSIX,调用 ixgbe_request_msix_irqs 设置中断处理程序*/
if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED)
err = ixgbe_request_msix_irqs(adapter);
/* 支持MSI,直接设置 ixgbe_intr 为中断处理程序 */
else if (adapter->flags & IXGBE_FLAG_MSI_ENABLED)
err = request_irq(adapter->pdev->irq, &ixgbe_intr, 0,
netdev->name, adapter);
/* 都不支持的情况,直接设置 ixgbe_intr 为中断处理程序 */
else
err = request_irq(adapter->pdev->irq, &ixgbe_intr, IRQF_SHARED,
netdev->name, adapter);
if (err)
e_err(probe, "request_irq failed, Error %d
", err);
return err;
}
/**
* 文件:ixgbe_main.c
* ixgbe_request_msix_irqs - Initialize MSI-X interrupts
* @adapter: board private structure
*
* ixgbe_request_msix_irqs allocates MSI-X vectors and requests
* interrupts from the kernel.
**/
static int (struct ixgbe_adapter *adapter)
{
…
for (vector = 0; vector < adapter->num_q_vectors; vector++) {
struct ixgbe_q_vector *q_vector = adapter->q_vector[vector];
struct msix_entry *entry = &adapter->msix_entries[vector];
/* 设置中断处理入口函数为 ixgbe_msix_clean_rings */
err = request_irq(entry->vector, &ixgbe_msix_clean_rings, 0,
q_vector->name, q_vector);
if (err) {
e_err(probe, "request_irq failed for MSIX interrupt '%s' "
"Error: %d
", q_vector->name, err);
goto free_queue_irqs;
}
…
}
}
(2)线上的多队列网卡均支持MSIX,中断处理程序入口为ixgbe_msix_clean_rings
,里面调用了函数napi_schedule(&q_vector->napi)
。
/**
* 文件:ixgbe_main.c
**/
static irqreturn_t ixgbe_msix_clean_rings(int irq, void *data)
{
struct ixgbe_q_vector *q_vector = data;
/* EIAM disabled interrupts (on this vector) for us */
if (q_vector->rx.ring || q_vector->tx.ring)
napi_schedule(&q_vector->napi);
return IRQ_HANDLED;
}