ネットワークデバイスのステータス管理


前のデータ送受信の過程からも見ましたが、送受信の流れの中で多くの設備の状態を検査する操作があり、このノートは設備の登録に成功した後、いったいどんな状態があるのか、どのように送受信の流れを制御しているのかを完全に見ています.
1. net_デバイスのstateフィールド
dev->stateフィールドには、デバイスとデバイスキューのステータスが記述され、現在定義されている値は次のとおりです.
/* These flag bits are private to the generic network queueing
 * layer, they may not be explicitly referenced by any other
 * code.
 */
enum netdev_state_t
{
	__LINK_STATE_XOFF=0,
	__LINK_STATE_START,
	__LINK_STATE_PRESENT,
	__LINK_STATE_SCHED,
	__LINK_STATE_NOCARRIER,
	__LINK_STATE_LINKWATCH_PENDING,
	__LINK_STATE_DORMANT,
	__LINK_STATE_QDISC_RUNNING,
};

1.1 __LINK_STATE_PRESENT
デバイスが存在するかどうかを示します.登録プロセスを完了したデバイスのこのフラグ位置ビット.また、電源管理中にデバイスが停止すると、フラグビットが消去され、一時的にデバイスが使用できなくなり、リカバリ時に再配置してデバイスがリカバリされます.具体的には、次の「デバイスの停止」と「デバイスのリカバリ」を参照してください.
netif_を使用できますdevice_present()は、フラグビットが設定されているかどうかを検出します.
/**
 *	netif_device_present - is device available or removed
 *	@dev: network device
 *
 * Check if device has not been removed from system.
 */
static inline int netif_device_present(struct net_device *dev)
{
	return test_bit(__LINK_STATE_PRESENT, &dev->state);
}

1.2 __LINK_STATE_START
デバイスが開いているかどうかを示し、その状態にあるデバイスは準備状態であり、データを正常に送受信することができる.dev_Open()のときにこのフラグビットを設定しdev_close()フラグビットをクリアします.
netif_を使用できますrunning()は、フラグビットが設定されているかどうかを検出します.
/**
 *	netif_running - test if up
 *	@dev: network device
 *
 *	Test if the device has been brought up.
 */
static inline int netif_running(const struct net_device *dev)
{
	return test_bit(__LINK_STATE_START, &dev->state);
}

1.3 __LINK_STATE_XOFF
デバイスの送信キューが使用可能かどうかを示します.このフラグビットの設定とクリアは一般的にドライバが担当する.ハードウェアが現在データを送信できるかどうかはドライバのみが知っているため、ドライバはデバイスが使用可能なキャッシュ、メモリ不足などの要因がないため、一時的にこのフラグビットをクリアし、トラフィック制御メカニズムの送信を停止する可能性がある.
次の3つのインタフェースを使用して、フラグビットを操作および確認できます.
/**
 *	netif_start_queue - allow transmit
 *	@dev: network device
 *
 *	Allow upper layers to call the device hard_start_xmit routine.
 */
static inline void netif_start_queue(struct net_device *dev)
{
	clear_bit(__LINK_STATE_XOFF, &dev->state);
}

/**
 *	netif_wake_queue - restart transmit
 *	@dev: network device
 *
 *	Allow upper layers to call the device hard_start_xmit routine.
 *	Used for flow control when transmit resources are available.
 */
static inline void netif_wake_queue(struct net_device *dev)
{
#ifdef CONFIG_NETPOLL_TRAP
	if (netpoll_trap()) {
		clear_bit(__LINK_STATE_XOFF, &dev->state);
		return;
	}
#endif
	//       ,          
	if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state))
		__netif_schedule(dev);
}

/**
 *	netif_stop_queue - stop transmitted packets
 *	@dev: network device
 *
 *	Stop upper layers calling the device hard_start_xmit routine.
 *	Used for flow control when transmit resources are unavailable.
 */
static inline void netif_stop_queue(struct net_device *dev)
{
	set_bit(__LINK_STATE_XOFF, &dev->state);
}

/**
 *	netif_queue_stopped - test if transmit queue is flowblocked
 *	@dev: network device
 *
 *	Test if transmit queue on device is currently unable to send.
 */
static inline int netif_queue_stopped(const struct net_device *dev)
{
	return test_bit(__LINK_STATE_XOFF, &dev->state);
}

1.4 __LINK_STATE_SCHED
デバイスがポーリングキューの送信中であるCPUの送受信キューソフトネット_data.output_queueでは、1つのデバイスが同時に1つのCPUの送信ポーリングキューでのみ、トラフィック制御送信プロセスqdisc_run()中にフラグビットがチェックされ、設定されます.
static inline void netif_schedule(struct net_device *dev)
{
	if (!test_bit(__LINK_STATE_XOFF, &dev->state))
		__netif_schedule(dev);
}

void __netif_schedule(struct net_device *dev)
{
	//       __LINK_STATE_SCHED  
	if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) {
		unsigned long flags;
		struct softnet_data *sd;

		//       CPU        
		local_irq_save(flags);
		sd = &__get_cpu_var(softnet_data);
		dev->next_sched = sd->output_queue;
		sd->output_queue = dev;
		//         
		raise_softirq_irqoff(NET_TX_SOFTIRQ);
		local_irq_restore(flags);
	}
}
EXPORT_SYMBOL(__netif_schedule);

ここでは、受信プロセスにこのような類似のフラグビットがない理由を説明する必要がある.実際にはありますが、このフラグビットはdev->stateではなくnapi_です.struct.stateでは、具体的には、デバイスインタフェース層のパケット送信を参照することができる.
1.5 __LINK_STATE_QDISC_RUNNING
トラフィック制御プロセスが実行中であるかどうか、すなわち_qdisc_run()関数の実行中.関連コードは次のとおりです.
static inline void qdisc_run(struct net_device *dev)
{
	// 1.          (__LINK_STATE_XOFF       )
	// 2.        CPU    (__LINK_STATE_QDISC_RUNNING       )
	//           ,        ,    __qdisc_run()
	if (!netif_queue_stopped(dev) && !test_and_set_bit(__LINK_STATE_QDISC_RUNNING, &dev->state))
		__qdisc_run(dev);
}

void __qdisc_run(struct net_device *dev)
{
...
	//         __LINK_STATE_QDISC_RUNNING   
	clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state);
}

このフラグが必要なのは、同時にCPUが1つしか呼び出されないことを保証するためです.qdisc_run()関数は、トラフィック制御メカニズムの下で送信プロセスを実行する.
1.6 __LINK_STATE_NOCARRIER
デバイスの搬送波の変化を駆動で感知する場合はnetif_を使用するcarrier_on/off()は、カーネルがこれらの場合にデバイスの送受信能力を合理的に閉じることができるようにカーネルに通知する.具体的には、以下を参照する.
netif_を使用できますcarrier_OK()このフラグビットが設定されているかどうかを確認します.
/**
 *	netif_carrier_ok - test if carrier present
 *	@dev: network device
 *
 * Check if carrier is present on device
 */
static inline int netif_carrier_ok(const struct net_device *dev)
{
	return !test_bit(__LINK_STATE_NOCARRIER, &dev->state);
}

1.7 __LINK_STATE_LINKWATCH_PENDING
デバイスがLINKWATCHイベントを生成し、スケジューリング中である場合、フラグビットを設定します.これは、同じデバイスが同時に複数回スケジューリングされることを防止するためである.
1.8 __LINK_STATE_DORMANT
このフラグビットは、現在の送受信プロセスでどのように使用されるかはまだ見られず、しばらく注目されません.
4.デバイスの保留
電源管理モジュールがデバイスシステムがスリープ状態に入ることを通知すると、ドライバはスリープ準備を実行する必要があります.この過程で、ドライバはnetif_を呼び出す必要があります.device_detach()は、フレームワークデバイスが休眠すると伝えます.
保留中に2つのことをする必要があります.
  • クリア_LINK_STATE_PRESENTフラグビットは、デバイスが一時的に使用できないようにする.
  • デバイスが開いている場合(dev_open()が呼び出され、正常にデータを送受信できるようになった場合)、デバイスの送信キューを閉じる必要があります.
  • /**
     * netif_device_detach - mark device as removed
     * @dev: network device
     *
     * Mark device as removed from system and therefore no longer available.
     */
    void netif_device_detach(struct net_device *dev)
    {
    	//cond1:     PRESET    ,     
    	//cond2:   START    ,       
    	if (test_and_clear_bit(__LINK_STATE_PRESENT, &dev->state) &&
    	    netif_running(dev)) {
    		//         
    		netif_stop_queue(dev);
    	}
    }
    

    5.設備の復旧
    同様に、デバイスがスリープ状態を終了すると、ドライバはリカバリ作業を実行する必要があります.この過程で、ドライバはnetif_を呼び出す必要があります.device_attach()は、フレームワークデバイスがリカバリされることを示します.
    設備を復旧する際、2つのことをします.
  • 再社長は_LINK_STATE_PRESENTフラグビット;
  • デバイスが既にオープンしている場合、送信キューがオンになり、
  • デバイスが送信可能に再スケジュールされる.
    /**
     * netif_device_attach - mark device as attached
     * @dev: network device
     *
     * Mark device as attached from system and restart if needed.
     */
    void netif_device_attach(struct net_device *dev)
    {
    	//cond1:     PRESET    ,    
    	//cond2:   START    ,       
    	if (!test_and_set_bit(__LINK_STATE_PRESENT, &dev->state) &&
    	    netif_running(dev)) {
    		//      ;           ;       
    		netif_wake_queue(dev);
    		//  WatchDog,            tx_timeout()  ,
    		//            
    		__netdev_watchdog_up(dev);
    	}
    }
    

    6.接続状態検知
    ドライバは、ネットワーク・デバイスが使用可能な状態にあるかどうか、例えば、ネットワーク・ケーブルが落ちているかどうかなどのイベントを感知することができ、これらのイベントは、使用可能と使用不可の2つの状態に簡単に分けることができる.この2つのイベントが発生した場合、ドライバはnetif_を介してネットワークデバイスインタフェースレイヤにこの状態を渡すべきである.carrier_on()とnetif_carrier_off()で実現しました.
    /**
     *	netif_carrier_on - set carrier
     *	@dev: network device
     *
     * Device has detected that carrier.
     */
    void netif_carrier_on(struct net_device *dev)
    {
    	//     __LINK_STATE_NOCARRIER  
    	if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
    		//            
    		linkwatch_fire_event(dev);
    		//        ,  WatchDog
    		if (netif_running(dev))
    			__netdev_watchdog_up(dev);
    	}
    }
    
    /**
     *	netif_carrier_off - clear carrier
     *	@dev: network device
     *
     * Device has detected loss of carrier.
     */
    void netif_carrier_off(struct net_device *dev)
    {
    	//  __LINK_STATE_NOCARRIER   
    	if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state))
        	//             
    		linkwatch_fire_event(dev);
    }
    

    6.1リンク変化状態イベント
    6.1.1イベントの発生
    以上のように、リンク状態が変化するイベントは、最初にドライバによって感知され、最後にlinkwatch_が呼び出される.fire_event()はイベントの生成をトリガーします.
    //      
    static int linkwatch_urgent_event(struct net_device *dev)
    {
    	//     &&      off  on &&        
    	return netif_running(dev) && netif_carrier_ok(dev) &&
    	       dev->qdisc != dev->qdisc_sleeping;
    }
    
    void linkwatch_fire_event(struct net_device *dev)
    {
    	//           
    	int urgent = linkwatch_urgent_event(dev);
    
    	//  LINKWATCH_PENDING  ,      LINKWATCH        ,              
    	//  LINKWATCH     ,             ,      
    	if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
    		//           ,        LINKWATCH     ,
    		//                   LINKWATCH     
    		dev_hold(dev);
    		linkwatch_add_event(dev);
    	} else if (!urgent)
    		//     ,      LINKWATCH         ,  
    		return;
    
    	//    、          
    	linkwatch_schedule_work(urgent);
    }
    

    6.1.2イベントリスト
    システムには複数のネットワークデバイスがあり、これらのデバイスにはLINKWATCHイベントが発生する可能性があります.これらの異なるデバイスからのLINKWATCHは組織する必要があります.カーネルは簡単なチェーンテーブルで組織されています.
    static struct net_device *lweventlist;
    static DEFINE_SPINLOCK(lweventlist_lock);
    

    6.1.3イベントのスケジュール
    カーネルのイベントキューが空でない場合は、linkwatch_を介してこれらのイベントをタイムリーに処理できるようにスケジューリングをトリガーする必要があります.schedule_ワーク()が完了しました.ここでは、遅延タスクの実行原理について説明します.私たちが注目しているポイントではありません.イベントを処理する関数の実装を直接見ています.
    static void __linkwatch_run_queue(int urgent_only)
    {
    	struct net_device *next;
    
    	/*
    	 * Limit the number of linkwatch events to one
    	 * per second so that a runaway driver does not
    	 * cause a storm of messages on the netlink
    	 * socket.  This limit does not apply to up events
    	 * while the device qdisc is down.
    	 */
    	//       ,            1s  
    	if (!urgent_only)
    		linkwatch_nextevent = jiffies + HZ;
    	/* Limit wrap-around effect on delay. */
    	else if (time_after(linkwatch_nextevent, jiffies + HZ))
    		linkwatch_nextevent = jiffies;
    
    	//    ,  LW_URGENT  
    	clear_bit(LW_URGENT, &linkwatch_flags);
    
    	//  LINKWATCH    
    	spin_lock_irq(&lweventlist_lock);
    	next = lweventlist;
    	lweventlist = NULL;
    	spin_unlock_irq(&lweventlist_lock);
    
    	while (next) {
    		struct net_device *dev = next;
    
    		next = dev->link_watch_next;
    
    		if (urgent_only && !linkwatch_urgent_event(dev)) {
    			linkwatch_add_event(dev);
    			continue;
    		}
    
    		/*
    		 * Make sure the above read is complete since it can be
    		 * rewritten as soon as we clear the bit below.
    		 */
    		smp_mb__before_clear_bit();
    
    		/* We are about to handle this device,
    		 * so new events can be accepted
    		 */
    		//  LINKWATCH_PENDING  
    		clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
    
    		rfc2863_policy(dev);
    		if (dev->flags & IFF_UP) {
    			//         ,          ok           
    			if (netif_carrier_ok(dev)) {
    				WARN_ON(dev->qdisc_sleeping == &noop_qdisc);
    				dev_activate(dev);
    			} else
    				dev_deactivate(dev);
    			//         ,  RT_NETLINK      
    			netdev_state_change(dev);
    		}
    
    		dev_put(dev);
    	}
    	//  LINKWATCH    ,            
    	if (lweventlist)
    		linkwatch_schedule_work(0);
    }
    

    注意:_linkwatch_run_Queue()には、遅延タスクの実行メカニズムに関するいくつかの内容が含まれています.無視してdev_を重点的に把握することができます.activate()とdev_deactivate()の呼び出し.