netdev: dev_watchdog timer(结合stmmac 分析)

分析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);
}

 

你可能感兴趣的:(Network,Linux,Driver)