kernel 網(wǎng)絡驅(qū)動

網(wǎng)絡設備#

網(wǎng)絡驅(qū)動結(jié)構(gòu)##

從上到下劃分4層:

  • 網(wǎng)絡協(xié)議接口層

使上層協(xié)議獨立于具體設備

  • 網(wǎng)絡設備接口層

向協(xié)議接口層提供統(tǒng)一的用于描述具體網(wǎng)絡設別屬性和操作的結(jié)構(gòu)體 net_device

  • 設備驅(qū)動功能層(提供實際功能)

net_device 的具體成員

  • 網(wǎng)絡設備與媒介層

完成數(shù)據(jù)包發(fā)送和接收的物理實體

網(wǎng)絡設備初始化##

  • 硬件準備工作溪窒,檢查設備是否存在和硬件資源
  • net_device 數(shù)據(jù)和函數(shù)指針初始化
  • 獲得設備的私有信息指針并初始化各成員值

網(wǎng)絡設備打開與釋放###

  • open

  • 使能設備硬件資源盗誊,申請IO區(qū)域、中斷和DMA通道

  • 調(diào)用Linux內(nèi)核提供的 netif_start_queue() 函數(shù),激活設備發(fā)送隊列

  • release

  • 調(diào)用內(nèi)核提供的 netif_stop_queue() 函數(shù)

  • 釋放資源

數(shù)據(jù)發(fā)送流程###

  • 獲取sk_buff數(shù)據(jù)包的數(shù)據(jù)和長度
  • 有效長度小于以太網(wǎng)沖突檢測所要求數(shù)據(jù)幀最小長度ETH_ZLEN赵辕,則補0
  • 設置硬件寄存器,驅(qū)動設備進行數(shù)據(jù)發(fā)送

數(shù)據(jù)接收流程###

  • 設備接收數(shù)據(jù)的主要方法是由中斷引發(fā)中斷處理函數(shù)
  • NAPI兼容的驅(qū)動咖熟,需先disable interrupt再調(diào)用NAPI接收pkt

網(wǎng)絡狀態(tài)###

  • 網(wǎng)絡硬件電路可以檢測網(wǎng)絡連接是否正常
  • 驅(qū)動程序可采取一定手段檢測報告鏈路狀態(tài),中斷或定時

網(wǎng)絡設備驅(qū)動流程##


struct net_device_stats(struct net_device *dev)
{
    ...
    return &dev->stats;
}

static int xxx_config(struct net_device *dev, struct ifmap *map)
{
    if (netif_running(dev))
        return -EBUSY;

    /* 假設不允許改變IO地址 */
    if (map->base_addr != dev->base_addr) {
        printk("xxx: Can't change I/O address\n");
        return -EOPNOTSUPP;
    }

    /* 假設允許改變 IRQ */
    if (map->irq != dev->irq)
        dev->irq = map->irq;

    return 0;
}

static int set_mac_address(struct net_device *dev, void *addr)
{
    if (netif_running(dev))
        return -EBUSY;

    xxx_set_mac(dev, addr);

    return 0;
}

static int xxx_open(struct net_device *dev)
{
    struct xxx_priv *priv = netdev_priv(dev);

    ...
    priv->timer.expires = jiffies + 3*Hz;
    priv->timer.data = (unsigned long)dev;
    /* timer handler */
    priv->timer.function = &xxx_timer;
    add_timer(&priv->timer);
    ...
}

static void xxx_timer(unsigned long data)
{
    struct net_device *dev = (struct net_device *)data;
    u16 link;

    ...
    if (!(dev->flags & IFF_UP))
        goto set_timer;

    /* 獲得物理連接狀態(tài) */
    if (link = xxx_chk_link(dev)) {
        if (!(dev->flags & IFF_RUNNING)) {
            /* 通知內(nèi)核 link up */
            netif_carrier_on(dev);
            dev->flags |= IFF_RUNNING;
            printk("%s: link up\n", dev->name);
        }
    } else {
        if (dev->flags & IFF_RUNNING) {
            /* 通知內(nèi)核 link down */
            netif_carrier_off(dev);
            dev->flags &= ~IFF_RUNNING;
            printk("%s: link down\n", dev->name);
        }
    }

set_timer:
    priv->timer.expires = jiffies + 1*Hz;
    priv->timer.data = (unsigned long)dev;
    /* timer handler */
    priv->timer.function = &xxx_timer;
    add_timer(&priv->timer);
}

#ifdef NAPI

static int xxx_poll(struct napi_struct *napi, int budget)
{
    int npackets = 0;
    struct sk_buff *skb;
    struct xxx_priv *priv = container_of(napi, struct xxx_priv, napi);
    struct xxx_packet *pkt;

    while ((npackets < budget) && priv->rx_queue) {
        /* 從隊列中取出數(shù)據(jù)包 */
        pkt = xxx_dequeue_buf(dev);

        /* 接下來的處理和中斷觸發(fā)接收一致 */
        skb = dev_alloc_skb(length + 2);
        ...
        skb_reserve(skb, 2);
        memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
        skb->dev = dev;
        skb->protocol = eth_type_trans(skb, dev);

        /* 調(diào)用 netif_receive_skb, 而不是net_rx, 將數(shù)據(jù)包交給上層協(xié)議 */
        netif_receive_skb(skb);

        /* 更改統(tǒng)計數(shù)據(jù) */
        priv->stats.rx_packets++;
        priv->stats.rx_bytes += pkt->datalen;
        xxx_release_buffer(pkt);
        npackets++;
    }
    if (npackets < budget) {
        napi_complete(napi);
        /* 再次啟動網(wǎng)絡設備的接收中斷 */
        xxx_enable_rx_int(...);
    }
    
    return npackets;
}

static void xxx_interrupt(int irq, void *dev_id)
{
    struct net_device *dev = dev_id;

    ...
    switch (status & ISQ_EVENT_MASK) {
    case ISQ_RECEIVER_EVENT:
        /* 禁止接收中斷 */
        xxx_disable_rx_int(...);

        /* 獲取數(shù)據(jù)包 */
        napi_schedule(&priv->napi);
        break;

    case ISQ_TRANSMITTER_EVENT:
        dev->stats.tx_packets++;
        netif_wake_queue(dev);

        /* 檢查硬件錯誤標志 */
        if ((status & (TX_OK | TX_LOST_CRS | TX_SQE_ERROR | TX_LATE_COL | TX_16_COL)) != TX_OK) {

            /* 錯誤統(tǒng)計 */
            if ((status & TX_OK) == 0)
                dev->stats.tx_errors++;

            if (status & TX_LOST_CRS)
                dev->stats.tx_carrier_errors++;
            
            ...
        }
        break;

    case ISQ_RX_MISS_EVENT:
        ...
        break;

    ...
    }
}

#else

static void xxx_rx(struct xxx_device *dev)
{
    ...
    length = get_rev_len(...);
    skb = dev_alloc_skb(length + 2);

    /* 對齊 */
    skb_reserve(skb, 2);
    skb->dev = dev;

    /* 讀取硬件上接收到的數(shù)據(jù) */
    insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length),  length >> 1);
    if (length & 1)
        skb->data[length - 1] = inw(ioaddr + RX_FRAME_PORT);
    
    /* 獲取上層協(xié)議類型 */
    skb->protocol = eth_type_trans(skb, dev);

    /* 把數(shù)據(jù)包交給上層 */
    netif_rx(skb);

    /* 記錄接收時間戳 */
    dev->last_rx = jiffies;
    ...
}

static void xxx_interrupt(int irq, void *dev_id)
{
    ...
    switch (status & ISQ_EVENT_MASK) {
    case ISQ_RECEIVER_EVENT:
        /* 獲取數(shù)據(jù)包 */
        xxx_rx(dev);
        break;
    ...
    }
}

#endif

/* 發(fā)送超時處理函數(shù) */
void xxx_tx_timeout(struct net_device *dev)
{
    ...
    netif_wake_queue(dev);
    ...
    dev->stats.tx_errors++;
}

int xxx_tx(struct sk_buff *skb, struct net_device *dev)
{
    int len;
    char *data, shortpkt[ETH_ZLEN];

    /* 發(fā)送隊列未滿胃惜,可以發(fā)送 */
    if (xxx_send_available(...)) {

        /* 有效數(shù)據(jù)指針和長度 */
        data = skb->data;
        len = skb->len;

        if (len < ETH_ZLEN) {
            /* 如果幀長小于以太網(wǎng)幀最小長度,補0 */
            memset(shortpkt, 0, ETH_ZLEN);
            memcpy(shortpkt, skb->data, skb->len);
            len = ETH_ZLEN;
            data = shortpkt;
        }

        /* 記錄發(fā)送時間戳 */
        dev->trans_start = jiffies;

        if (avail) {
            xxx_hw_tx(data, len, dev);
        } else {
            /* 
                阻止上層繼續(xù)向網(wǎng)絡設備傳遞數(shù)據(jù)包哪雕;當忙于發(fā)送的數(shù)據(jù)包被發(fā)送完成后船殉,在以TX結(jié)束的中斷處理中,應該調(diào)用 netif_wake_queue() 喚醒被阻塞的上層斯嚎,以啟動它繼續(xù)向網(wǎng)絡設備傳送數(shù)據(jù)包
            */
            netif_stop_queue(dev);
            ...
        }
    }
}

static int xxx_open(struct net_device *dev)
{
    /* 申請端口利虫、IRQ等,類似于fops->open */
    ret = request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev);
    ...
    netif_start_queue(dev);
    ...
}

static int xxx_release(struct net_device *dev)
{
    netif_stop_queue(dev);
    ...
    free_irq(dev->irq, dev);
    ...
}

void xxx_init(struct net_device *dev)
{
    struct xxx_priv *priv;

    /* 檢查設備是否存在和設備所使用的硬件資源 */
    xxx_hw_init();

    /* 初始化 net_device 公有成員 */
    ether_setup(dev);

    /* 設置設備的成員函數(shù)指針 */
    dev->netdev_ops = &xxx_netdev_ops;
    dev->ethtool_ops = &xxx_ethtool_ops;
    dev->watchdog_timeo = timeout;

    /* 取得私有指針并初始化 */
    priv = netdev_priv(dev);
    ...
}

static int xxx_register(void)
{
    ...
    /* 分配 net_device 結(jié)構(gòu)體并賦值 */
    xxx_dev = alloc_netdev(sizeof(struct xxx_priv), "sn%d", xxx_init);
    if (!xxx_dev)
        ...

    /* 注冊 net_device 結(jié)構(gòu)體 */
    if ((result = register_netdev(xxx_dev)))
        ...
}

static void xxx_unregister(void)
{
    ...
    /* 注銷 net_device 結(jié)構(gòu)體 */
    unregister_netdev(xxx_dev);
    /* 釋放 net_device 結(jié)構(gòu)體 */
    free_netdev(xxx_dev);
}

總結(jié)##

  • net_device 結(jié)構(gòu)體的存在將網(wǎng)絡設備進行抽象堡僻,使得設備功能層中除數(shù)據(jù)包接收以外的主體工作都由填充 net_device 的屬性和函數(shù)指針完成
  • 套接字緩沖區(qū) sk_buff 是所有數(shù)據(jù)流動的載體糠惫,網(wǎng)絡設備驅(qū)動和上層協(xié)議之間也基于此結(jié)構(gòu)進行數(shù)據(jù)包交互
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市钉疫,隨后出現(xiàn)的幾起案子硼讽,更是在濱河造成了極大的恐慌,老刑警劉巖牲阁,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件固阁,死亡現(xiàn)場離奇詭異,居然都是意外死亡城菊,警方通過查閱死者的電腦和手機备燃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來役电,“玉大人赚爵,你說我怎么就攤上這事》ㄉ” “怎么了冀膝?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長霎挟。 經(jīng)常有香客問我窝剖,道長,這世上最難降的妖魔是什么酥夭? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任赐纱,我火速辦了婚禮,結(jié)果婚禮上熬北,老公的妹妹穿的比我還像新娘疙描。我一直安慰自己,他們只是感情好讶隐,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布起胰。 她就那樣靜靜地躺著,像睡著了一般巫延。 火紅的嫁衣襯著肌膚如雪效五。 梳的紋絲不亂的頭發(fā)上地消,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音畏妖,去河邊找鬼脉执。 笑死,一個胖子當著我的面吹牛戒劫,可吹牛的內(nèi)容都是我干的半夷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼谱仪,長吁一口氣:“原來是場噩夢啊……” “哼玻熙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起疯攒,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤嗦随,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后敬尺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枚尼,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年砂吞,在試婚紗的時候發(fā)現(xiàn)自己被綠了署恍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜻直,死狀恐怖盯质,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情概而,我是刑警寧澤呼巷,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站赎瑰,受9級特大地震影響王悍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜餐曼,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一压储、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧源譬,春花似錦集惋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春为朋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背厚脉。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工习寸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人傻工。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓霞溪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親中捆。 傳聞我的和親對象是個殘疾皇子鸯匹,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容