网络设备驱动最佳实践
1. 始终使用 NAPI
高速网络必须使用 NAPI,避免中断风暴:
c
/* ✅ 正确:NAPI 模式 */
netif_napi_add(ndev, &priv->napi, my_poll);
/* 中断中只调度,不处理数据 */
static irqreturn_t my_isr(int irq, void *dev_id)
{
disable_rx_irq(priv);
napi_schedule(&priv->napi);
return IRQ_HANDLED;
}2. 正确设置 skb 的协议字段
c
/* eth_type_trans 自动解析以太网帧类型并设置 skb->protocol */
skb->protocol = eth_type_trans(skb, ndev);
/* 对于非以太网设备,手动设置 */
skb->protocol = htons(ETH_P_IP);3. 使用 netdev_alloc_skb_ip_align
c
/* ✅ IP 对齐分配,避免非对齐访问性能损失 */
skb = netdev_alloc_skb_ip_align(ndev, pkt_len);
/* ❌ 不要用 dev_alloc_skb(已废弃) */4. 统计计数器使用 per-CPU
c
struct mynet_priv {
struct pcpu_sw_netstats __percpu *stats;
};
/* probe 中分配 */
priv->stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
/* 更新统计(在 softirq 上下文中) */
u64_stats_update_begin(&stats->syncp);
stats->rx_packets++;
stats->rx_bytes += len;
u64_stats_update_end(&stats->syncp);
/* 实现 ndo_get_stats64 */
static void my_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *s)
{
struct mynet_priv *priv = netdev_priv(ndev);
int cpu;
for_each_possible_cpu(cpu) {
const struct pcpu_sw_netstats *stats;
u64 rx_packets, rx_bytes;
unsigned int start;
stats = per_cpu_ptr(priv->stats, cpu);
do {
start = u64_stats_fetch_begin(&stats->syncp);
rx_packets = stats->rx_packets;
rx_bytes = stats->rx_bytes;
} while (u64_stats_fetch_retry(&stats->syncp, start));
s->rx_packets += rx_packets;
s->rx_bytes += rx_bytes;
}
}5. 发送队列管理
c
/* 发送队列满时停止上层 */
if (tx_ring_full(priv)) {
netif_stop_queue(ndev);
/* 设置内存屏障,确保停止队列对其他 CPU 可见 */
smp_mb();
/* 再次检查,避免竞态 */
if (!tx_ring_full(priv))
netif_wake_queue(ndev);
}
/* 发送完成中断中唤醒队列 */
if (netif_queue_stopped(ndev) && !tx_ring_full(priv))
netif_wake_queue(ndev);6. 链路状态上报
c
/* 链路 UP */
netif_carrier_on(ndev);
/* 链路 DOWN */
netif_carrier_off(ndev);
/* PHY 状态变化回调 */
static void my_phy_adjust_link(struct net_device *ndev)
{
struct phy_device *phydev = ndev->phydev;
if (phydev->link) {
netif_carrier_on(ndev);
netdev_info(ndev, "Link up: %d Mbps %s-duplex\n",
phydev->speed,
phydev->duplex == DUPLEX_FULL ? "full" : "half");
} else {
netif_carrier_off(ndev);
}
}7. 零拷贝发送(scatter-gather)
c
/* 声明支持 scatter-gather */
ndev->hw_features |= NETIF_F_SG;
ndev->features |= NETIF_F_SG;
/* 在 ndo_start_xmit 中处理分散的 skb */
static netdev_tx_t my_xmit(struct sk_buff *skb, struct net_device *ndev)
{
int nr_frags = skb_shinfo(skb)->nr_frags;
/* 处理线性部分 */
dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
/* 处理分片 */
for (int i = 0; i < nr_frags; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
dma_map_page(dev, skb_frag_page(frag),
skb_frag_off(frag), skb_frag_size(frag),
DMA_TO_DEVICE);
}
return NETDEV_TX_OK;
}