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関数は,NICが異常を送信した場合(データが送信できない場合など)のタイムアウト処理関数である.stmmac_が呼び出されますtx_timeoutはtransmissionを再起動します.
if (some_queue_timedout) {
    WARN_ONCE(1, KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit queue %u timed out
", 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
主にNIC 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);
}