亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
123下一頁(yè)
最近訪問(wèn)板塊 發(fā)新帖
查看: 53433 | 回復(fù): 27
打印 上一主題 下一主題

網(wǎng)絡(luò)子系統(tǒng)在鏈路層的收發(fā)過(guò)程剖析 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2007-01-30 17:43 |只看該作者 |倒序?yàn)g覽
R.wen (rwen2012@126.com)


由于太長(zhǎng), 這只是一部分內(nèi)容,完整的文檔在附件中。
有興趣的請(qǐng)看看并幫忙指正,謝謝。


1),Skb_buff


/* To allow 64K frame to be packed as single skb without frag_list */
#define MAX_SKB_FRAGS (65536/PAGE_SIZE + 2)

typedef struct skb_frag_struct skb_frag_t;

struct skb_frag_struct {
        struct page *page;
        __u16 page_offset;
        __u16 size;
};

/* This data is invariant across clones and lives at
* the end of the header data, ie. at skb->end.
*/
struct skb_shared_info {
        atomic_t        dataref;
        unsigned short        nr_frags;
        unsigned short        gso_size;
        /* Warning: this field is not always filled in (UFO)! */
        unsigned short        gso_segs;
        unsigned short  gso_type;
        unsigned int    ip6_frag_id;
        struct sk_buff        *frag_list;
        skb_frag_t        frags[MAX_SKB_FRAGS];
};


Skb比較復(fù)雜的部分在于skb_shared_info部分,alloc_skb()在為數(shù)據(jù)分配空間的時(shí)候,會(huì)在這個(gè)數(shù)據(jù)的末尾加上一個(gè)skb_shared_info結(jié)構(gòu),這個(gè)結(jié)構(gòu)就是用于scatter/gather IO的實(shí)現(xiàn)的。它主要用于提高性能,避免數(shù)據(jù)的多次拷貝。例如,當(dāng)用戶用sendmsg分送一個(gè)數(shù)組結(jié)構(gòu)的數(shù)據(jù)時(shí),這些數(shù)據(jù)在物理可能是不連續(xù)的(大多數(shù)情況),在不支持scatter/gather IO的網(wǎng)卡上,它只能通過(guò)重新拷貝,將它重裝成連續(xù)的skb(skb_linearize),才可以進(jìn)行DMA操作。而在支持S/G IO上,它就省去了這次拷貝。




4),數(shù)據(jù)包的接收

* Incoming packets are placed on per-cpu queues so that
* no locking is needed.
*/
struct softnet_data
{
        struct net_device        *output_queue;
        struct sk_buff_head        input_pkt_queue;
        struct list_head        poll_list;
        struct sk_buff                *completion_queue;

        struct net_device        backlog_dev;        /* Sorry. */
#ifdef CONFIG_NET_DMA
        struct dma_chan                *net_dma;
#endif
};

這個(gè)數(shù)據(jù)結(jié)構(gòu)同時(shí)用于接收與發(fā)送數(shù)據(jù)包,它為per_CPU結(jié)構(gòu),這樣每個(gè)CPU有自己獨(dú)立的信息,這樣在SMP之間就避免了加鎖操作,從而大大提高了信息處理的并行性。

struct net_device        *output_queue;        
struct sk_buff                *completion_queue;
這兩個(gè)域用于發(fā)送數(shù)據(jù),將在下一節(jié)中描述。

struct sk_buff_head         input_pkt_queue;
struct list_head        poll_list;
struct net_device        backlog_dev;
這三個(gè)域用于接收數(shù)據(jù),其中input_pkt_queue與backlog_dev僅用于non-NAPI的NIC,input_pkt_queue是接收到的數(shù)據(jù)隊(duì)列頭,它用于netif_rx()中,并最終由虛擬的poll函數(shù)process_backlog()處理這個(gè)SKB隊(duì)列。
poll_list則是有數(shù)據(jù)包等待處理的NIC設(shè)備隊(duì)列。對(duì)于non-NAPI驅(qū)動(dòng)來(lái)說(shuō),它始終是backlog_dev。

接收過(guò)程:
   當(dāng)一個(gè)數(shù)據(jù)包到來(lái)時(shí),NIC會(huì)產(chǎn)生一個(gè)中斷,這時(shí),它會(huì)執(zhí)行中斷處理全程。

(1), NON-NAPI方式:
如3c59x中的vortex_interrupt(),它會(huì)判斷寄存器的值作出相應(yīng)的動(dòng)作:

                if (status & RxComplete)
                        vortex_rx(dev);
如上,當(dāng)中斷指示,有數(shù)據(jù)包在等待接收,這時(shí),中斷例程會(huì)調(diào)用接收函數(shù)vortex_rx(dev)接收新到來(lái)的包(如下,只保留核心部分):

int pkt_len = rx_status & 0x1fff;
                        struct sk_buff *skb;

                        skb = dev_alloc_skb(pkt_len + 5);
               
                        if (skb != NULL) {
                                skb->dev = dev;
                                skb_reserve(skb, 2);        /* Align IP on 16 byte boundaries */
                                /* 'skb_put()' points to the start of sk_buff data area. */
                                if (vp->bus_master &&
                                        ! (ioread16(ioaddr + Wn7_MasterStatus) & 0x8000)) {
                                        dma_addr_t dma = pci_map_single(VORTEX_PCI(vp), skb_put(skb, pkt_len),
                                                                           pkt_len, PCI_DMA_FROMDEVICE);
                                        iowrite32(dma, ioaddr + Wn7_MasterAddr);
                                        iowrite16((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
                                        iowrite16(StartDMAUp, ioaddr + EL3_CMD);
                                        while (ioread16(ioaddr + Wn7_MasterStatus) & 0x8000)
                                                ;
                                        pci_unmap_single(VORTEX_PCI(vp), dma, pkt_len, PCI_DMA_FROMDEVICE);
                       
                                }
                                iowrite16(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
                                skb->protocol = eth_type_trans(skb, dev);
                                netif_rx(skb);
                       
它首先為新到來(lái)的數(shù)據(jù)包分配一個(gè)skb結(jié)構(gòu)及pkt_len+5大小的數(shù)據(jù)長(zhǎng)度,然后便將接收到的數(shù)據(jù)從網(wǎng)卡復(fù)制到(DMA)這個(gè)SKB的數(shù)據(jù)部分中。最后,調(diào)用netif_rx(skb)進(jìn)一步處理數(shù)據(jù):

int netif_rx(struct sk_buff *skb)
{
        struct softnet_data *queue;
        unsigned long flags;

        /*
         * The code is rearranged so that the path is the most
         * short when CPU is congested, but is still operating.
         */
        local_irq_save(flags);
        queue = &__get_cpu_var(softnet_data);

        if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
                if (queue->input_pkt_queue.qlen) {
enqueue:
                        dev_hold(skb->dev);
                        __skb_queue_tail(&queue->input_pkt_queue, skb);
                        local_irq_restore(flags);
                        return NET_RX_SUCCESS;
                }

                netif_rx_schedule(&queue->backlog_dev);
                goto enqueue;
        }
}

這段代碼關(guān)鍵是,將這個(gè)SKB加入到相應(yīng)的input_pkt_queue隊(duì)列中,并調(diào)用netif_rx_schedule(),而對(duì)于NAPI方式,它沒(méi)有使用input_pkt_queue隊(duì)列,而是使用私有的隊(duì)列,所以它沒(méi)有這一個(gè)步驟。至此,中斷的上半部已經(jīng)完成,以下的工作則交由中斷的下半部來(lái)實(shí)現(xiàn)。

void __netif_rx_schedule(struct net_device *dev)
{
        unsigned long flags;

        local_irq_save(flags);
        dev_hold(dev);
        list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);
        if (dev->quota < 0)
                dev->quota += dev->weight;
        else
                dev->quota = dev->weight;
        __raise_softirq_irqoff(NET_RX_SOFTIRQ);
        local_irq_restore(flags);
}

netif_rx_schedule()就是將有等待接收數(shù)據(jù)包的NIC鏈入softnet_data的poll_list隊(duì)列,然后觸發(fā)軟中斷,讓后半部去完成數(shù)據(jù)的處理工作。

注意:這里是否調(diào)用netif_rx_schedule()是有條件的,即當(dāng)queue->input_pkt_queue.qlen==0時(shí)才會(huì)調(diào)用,否則由于這個(gè)隊(duì)列的長(zhǎng)度不為0,這個(gè)中斷下半部的執(zhí)行已由先前的中斷觸發(fā),它會(huì)斷續(xù)處理余下來(lái)的數(shù)據(jù)包的接收,所以,這里就不必要再次觸發(fā)它的執(zhí)行了。

總之,NON-NAPI的中斷上半部接收過(guò)程可以簡(jiǎn)單的描述為,它首先為新到來(lái)的數(shù)據(jù)幀分配合適長(zhǎng)度的SKB,再將接收到的數(shù)據(jù)從NIC中拷貝過(guò)來(lái),然后將這個(gè)SKB鏈入當(dāng)前CPU的softnet_data中的鏈表中,最后進(jìn)一步觸發(fā)中斷下半部發(fā)繼續(xù)處理。


(2), NAPI方式:

static irqreturn_t e100_intr(int irq, void *dev_id)
{

        if(likely(netif_rx_schedule_prep(netdev))) {
                e100_disable_irq(nic);
                __netif_rx_schedule(netdev);
        }

        return IRQ_HANDLED;
}
       
可以看到,兩種方式的不同之處在于,NAPI方式直接調(diào)用__netif_rx_schedule(),而非NAPI方式則要通過(guò)輔助函數(shù)netif_rx()設(shè)置好接收隊(duì)列再調(diào)用netif_rx_schedule(),再者,在非NAPI方式中,提交的是netif_rx_schedule(&queue->backlog_dev),而NAPI中,提交的是__netif_rx_schedule(netdev),即是設(shè)備驅(qū)動(dòng)的net_device結(jié)構(gòu),而不是queue中的backlog_dev。


(3),net_rx_action()
        netif_rx_schedule()觸發(fā)中斷下半部的執(zhí)行,這個(gè)下半部將執(zhí)行net_rx_action():

static void net_rx_action(struct softirq_action *h)
{
        struct softnet_data *queue = &__get_cpu_var(softnet_data);
        unsigned long start_time = jiffies;

        local_irq_disable();

        while (!list_empty(&queue->poll_list)) {
                struct net_device *dev;

                local_irq_enable();

                dev = list_entry(queue->poll_list.next,
                                 struct net_device, poll_list);

                if (dev->quota <= 0 || dev->poll(dev, &budget)) {
                        … //出錯(cuò)處理
                } else {
                        netpoll_poll_unlock(have);
                        dev_put(dev);
                        local_irq_disable();
                }
}

由上可以看到,下半部的主要工作是遍歷有數(shù)據(jù)幀等待接收的設(shè)備鏈表,對(duì)于每個(gè)設(shè)備,執(zhí)行它相應(yīng)的poll函數(shù)。

(4),poll函數(shù)
NON—NAPI方式:
        這種方式對(duì)應(yīng)該的poll函數(shù)為process_backlog:

        struct softnet_data *queue = &__get_cpu_var(softnet_data);
        for (; {

                local_irq_disable();
                skb = __skb_dequeue(&queue->input_pkt_queue);
                local_irq_enable();

                netif_receive_skb(skb);
}

它首先找到當(dāng)前CPU的softnet_data結(jié)構(gòu),然后遍歷其數(shù)據(jù)隊(duì)SKB,并將數(shù)據(jù)上交netif_receive_skb(skb)處理。

NAPI方式:
        這種方式下,NIC驅(qū)動(dòng)程序會(huì)提供自己的poll函數(shù)和私有接收隊(duì)列。
如intel 8255x系列網(wǎng)卡程序e100,它有在初始化的時(shí)候首先分配一個(gè)接收隊(duì)列,而不像以上那種方式在接收到數(shù)據(jù)幀的時(shí)候再為其分配數(shù)據(jù)空間。這樣,NAPI的poll函數(shù)在處理接收的時(shí)候,它遍歷的是自己的私有隊(duì)列:

        static int e100_poll(struct net_device *netdev, int *budget)
{
        e100_rx_clean(nic, &work_done, work_to_do);
        ……
}

static void e100_rx_clean(struct nic *nic, unsigned int *work_done,
        unsigned int work_to_do)
{
        …….
        /* Indicate newly arrived packets */
        for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) {
                int err = e100_rx_indicate(nic, rx, work_done, work_to_do);
                if(-EAGAIN == err) {
                ……
        }
                ……
}

static int e100_rx_indicate(struct nic *nic, struct rx *rx,
        unsigned int *work_done, unsigned int work_to_do)
{
        struct sk_buff *skb = rx->skb;
        struct rfd *rfd = (struct rfd *)skb->data;
        rfd_status = le16_to_cpu(rfd->status);

        /* Get actual data size */
        actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF;

        /* Pull off the RFD and put the actual data (minus eth hdr) */
        skb_reserve(skb, sizeof(struct rfd));
        skb_put(skb, actual_size);
        skb->protocol = eth_type_trans(skb, nic->netdev);

        netif_receive_skb(skb);

        return 0;
}

主要工作在e100_rx_indicate()中完成,這主要重設(shè)SKB的一些參數(shù),然后跟process_backlog(),一樣,最終調(diào)用netif_receive_skb(skb)。




                                                                                                                                        07.01.30

[ 本帖最后由 rwen2012 于 2007-1-30 19:37 編輯 ]

網(wǎng)絡(luò)子系統(tǒng)在鏈路層的收發(fā)過(guò)程剖析.pdf

192.76 KB, 下載次數(shù): 7001

論壇徽章:
0
2 [報(bào)告]
發(fā)表于 2007-01-31 09:10 |只看該作者
相關(guān)問(wèn)題:
dev_alloc_skb()--用于在內(nèi)核中分配空間。通常,硬件有兩種處理方式, 一為如果NIC數(shù)據(jù)幀直接通過(guò)系統(tǒng)內(nèi)存接收,則skb_buff就得先行分配;其二,如果數(shù)據(jù)幀是先被接收到NIC的緩沖中,然后通過(guò)DMA送到系統(tǒng)內(nèi)存中,則這個(gè)在系統(tǒng)內(nèi)存的skb_buff就會(huì)在接收到這個(gè)幀后,根據(jù)它的大小再發(fā)配合適的skb,然后再進(jìn)一步初始化DMA操作。

論壇徽章:
0
3 [報(bào)告]
發(fā)表于 2007-01-31 10:59 |只看該作者
不錯(cuò)!

論壇徽章:
0
4 [報(bào)告]
發(fā)表于 2007-01-31 17:05 |只看該作者
very good,3ks!

up

論壇徽章:
0
5 [報(bào)告]
發(fā)表于 2007-01-31 17:52 |只看該作者
有哪位大哥對(duì)linux的鏈路檢測(cè)有研究過(guò)的指點(diǎn)一下,不是很透,謝謝。

http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=638153&page=0&view=collapsed&sb=5&o=7&fpart=

[ 本帖最后由 rwen2012 于 2007-1-31 18:10 編輯 ]

論壇徽章:
0
6 [報(bào)告]
發(fā)表于 2007-04-05 16:24 |只看該作者
不錯(cuò),不錯(cuò)。

論壇徽章:
0
7 [報(bào)告]
發(fā)表于 2007-04-07 23:30 |只看該作者
原帖由 logonly 于 2007-4-5 16:24 發(fā)表于 6樓  
不錯(cuò),不錯(cuò)。


樓上的為什么要用我的頭像

論壇徽章:
0
8 [報(bào)告]
發(fā)表于 2007-04-29 02:01 |只看該作者
太好了,最近正在看網(wǎng)絡(luò)這部分代碼,學(xué)習(xí)中
謝謝

論壇徽章:
0
9 [報(bào)告]
發(fā)表于 2007-04-29 09:13 |只看該作者
帖子的標(biāo)題不太合適吧!

論壇徽章:
0
10 [報(bào)告]
發(fā)表于 2007-04-29 22:33 |只看該作者
原帖由 AIXHP 于 2007-4-29 09:13 發(fā)表于 9樓  
帖子的標(biāo)題不太合適吧!


謝謝。

因?yàn)橹饕獌?nèi)容是是包括網(wǎng)卡驅(qū)動(dòng)和在其之上但又在IP層之下的部分內(nèi)容,不屬于驅(qū)動(dòng)范圍,
所以姑且叫了這個(gè)題目,呵呵。
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過(guò)ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP