分析netdev看门狗定时器
1. dev_watchdog()作为定时器回调函数会被周期执行
在dev_watchdog()中,如果 if (netif_xmit_stopped(txq) && time_after(jiffies, (trans_start + dev->watchdog_timeo))) 成立,执行some_queue_timedout = 1, 然后便调用ndo_tx_timeout。
ndo_tx_timeout函数便是网卡发送异常(数据发不出去等)时的超时处理函数。它会调用 stmmac_tx_timeout重新启动transmission。
if (some_queue_timedout) {
WARN_ONCE(1, KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit queue %u timed out\n",
dev->name, netdev_drivername(dev), i);
dev->netdev_ops->ndo_tx_timeout(dev);
}
2. dev_watchdog()执行ndo_tx_timeout的条件
if (netif_xmit_stopped(txq) && time_after(jiffies, (trans_start + dev->watchdog_timeo)))
2.1 time_after(jiffies, (trans_start + dev->watchdog_timeo))
即queue发送超时
1)dev->watchdog_timeo为定时周期
2)在 __netdev_start_xmit中,如果 ndo_start_xmit返回 NETDEV_TX_OK(这并不代表数据已经通过DMA发送出去),便调用 txq_trans_update更新txq->trans_start。在dev_watchdog中,会将txq->trans_start赋值给trans_start。
2.2 netif_xmit_stopped(txq)
queue已经停止发送
static inline bool netif_xmit_stopped(const struct netdev_queue *dev_queue)
{
return dev_queue->state & QUEUE_STATE_ANY_XOFF;
}
#define QUEUE_STATE_ANY_XOFF (QUEUE_STATE_DRV_XOFF | QUEUE_STATE_STACK_XOFF)
#define QUEUE_STATE_DRV_XOFF (1 << __QUEUE_STATE_DRV_XOFF)
#define QUEUE_STATE_STACK_XOFF (1 << __QUEUE_STATE_STACK_XOFF)
1)__QUEUE_STATE_STACK_XOFF
清除__QUEUE_STATE_STACK_XOFF:
如果DMA发送完成,在中断服务程序 stmmac_dma_interrupt() -> stmmac_poll() -> stmmac_tx_clean() -> netdev_tx_completed_queue() -> test_and_clear_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state) 清除
设置__QUEUE_STATE_STACK_XOFF:
发送时,在stmmac_xmit() -> netdev_tx_sent_queue() -> set_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state) 设置
所以如果dma中断没有被触发,或是触发了但是发送没有完成,则netif_xmit_stopped()返回1
static inline void netdev_tx_sent_queue(struct netdev_queue *dev_queue,
unsigned int bytes)
{
#ifdef CONFIG_BQL
dql_queued(&dev_queue->dql, bytes);
if (likely(dql_avail(&dev_queue->dql) >= 0))
return;
set_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state);
/* check again in case another CPU has just made room avail */
if (unlikely(dql_avail(&dev_queue->dql) >= 0))
clear_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state);
#endif
}
static inline void netdev_tx_reset_queue(struct netdev_queue *q)
{
#ifdef CONFIG_BQL
clear_bit(__QUEUE_STATE_STACK_XOFF, &q->state);
dql_reset(&q->dql);
#endif
}
static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue,
unsigned int pkts, unsigned int bytes)
{
#ifdef CONFIG_BQL
if (unlikely(!bytes))
return;
dql_completed(&dev_queue->dql, bytes);
if (dql_avail(&dev_queue->dql) < 0)
return;
if (test_and_clear_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state))
netif_schedule_queue(dev_queue);
#endif
}
2) __QUEUE_STATE_DRV_XOFF
主要针对网卡queue,当queue满或不足以进行下一个传输时,会调用netif_tx_stop_queue通知上层停止发送。当queue恢复时,调用netif_tx_wake_queue通知上层重新开始传输。
清除__QUEUE_STATE_DRV_XOFF:
stmmac_open -> stmmac_start_all_queues -> netif_tx_start_queue -> clear_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state)
stmmac_dma_interrupt -> stmmac_poll -> stmmac_tx_clean -> netif_tx_wake_queue
设置__QUEUE_STATE_DRV_XOFF:
static __always_inline void netif_tx_start_queue(struct netdev_queue *dev_queue)
{
clear_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state);
}
void netif_tx_wake_queue(struct netdev_queue *dev_queue)
{
if (test_and_clear_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state)) {
struct Qdisc *q;
rcu_read_lock();
q = rcu_dereference(dev_queue->qdisc);
__netif_schedule(q);
rcu_read_unlock();
}
}
static __always_inline void netif_tx_stop_queue(struct netdev_queue *dev_queue)
{
set_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state);
}