前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >网卡收到一个数据包的时候,是如何传给应用层的(上)

网卡收到一个数据包的时候,是如何传给应用层的(上)

作者头像
theanarkh
发布2019-03-06 10:04:09
1.1K0
发布2019-03-06 10:04:09
举报
文章被收录于专栏:原创分享

这里以3c501网卡为例,每个设备对应一个device的结构体,下面代码即对3c501网卡的数据结构进行初始化,包括发送函数,注册中断回调,mac头长度等。

代码语言:javascript
复制
/* The actual probe. */ 
static int
el1_probe1(struct device *dev, int ioaddr)
{
    #ifndef MODULE

    char *mname;        /* Vendor name */
    unsigned char station_addr[6];
    int autoirq = 0;
    int i;

    /* Read the station address PROM data from the special port.  */
    for (i = 0; i < 6; i++) {
    outw(i, ioaddr + EL1_DATAPTR);
    station_addr[i] = inb(ioaddr + EL1_SAPROM);
    }
    /* Check the first three octets of the S.A. for 3Com's prefix, or
       for the Sager NP943 prefix. */ 
    if (station_addr[0] == 0x02  &&  station_addr[1] == 0x60
    && station_addr[2] == 0x8c) {
    mname = "3c501";
    } else if (station_addr[0] == 0x00  &&  station_addr[1] == 0x80
    && station_addr[2] == 0xC8) {
    mname = "NP943";
    } else
    return ENODEV;

    /* Grab the region so we can find the another board if autoIRQ fails. */
    request_region(ioaddr, EL1_IO_EXTENT,"3c501");

    /* We auto-IRQ by shutting off the interrupt line and letting it float
       high. */
    if (dev->irq < 2) {

    autoirq_setup(2);

    inb(RX_STATUS);     /* Clear pending interrupts. */
    inb(TX_STATUS);
    outb(AX_LOOP + 1, AX_CMD);

    outb(0x00, AX_CMD);

    autoirq = autoirq_report(1);

    if (autoirq == 0) {
        printk("%s probe at %#x failed to detect IRQ line.\n",
           mname, ioaddr);
        return EAGAIN;
    }
    }

    outb(AX_RESET+AX_LOOP, AX_CMD);            /* Loopback mode. */

    dev->base_addr = ioaddr;
    memcpy(dev->dev_addr, station_addr, ETH_ALEN);
    if (dev->mem_start & 0xf)
    el_debug = dev->mem_start & 0x7;
    if (autoirq)
    dev->irq = autoirq;

    printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n",
       dev->name, mname, dev->base_addr,
       autoirq ? "auto":"assigned ", dev->irq);

#ifdef CONFIG_IP_MULTICAST
    printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
#endif    

    if (el_debug)
    printk("%s", version);

    /* Initialize the device structure. */
    if (dev->priv == NULL)
    dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
    memset(dev->priv, 0, sizeof(struct net_local));

    /* The EL1-specific entries in the device structure. */
    dev->open = &el_open;
    // 发送函数
    dev->hard_start_xmit = &el_start_xmit;
    dev->stop = &el1_close;
    dev->get_stats = &el1_get_stats;
    dev->set_multicast_list = &set_multicast_list;
    /* Setup the generic properties */
    ether_setup(dev);

#endif /* !MODULE */
    return 0;
}

void ether_setup(struct device *dev)
{
    int i;
    /* Fill in the fields of the device structure with ethernet-generic values.
       This should be in a common file instead of per-driver.  */
    for (i = 0; i < DEV_NUMBUFFS; i++)
        skb_queue_head_init(&dev->buffs[i]);

    /* register boot-defined "eth" devices */
    if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) {
        i = simple_strtoul(dev->name + 3, NULL, 0);
        if (ethdev_index[i] == NULL) {
            ethdev_index[i] = dev;
        }
        else if (dev != ethdev_index[i]) {
            /* Really shouldn't happen! */
            printk("ether_setup: Ouch! Someone else took %s\n",
                dev->name);
        }
    }

    dev->hard_header    = eth_header;
    dev->rebuild_header = eth_rebuild_header;
    dev->type_trans = eth_type_trans;

    dev->type       = ARPHRD_ETHER;
    dev->hard_header_len = ETH_HLEN;
    dev->mtu        = 1500; /* eth_mtu */
    dev->addr_len   = ETH_ALEN;
    for (i = 0; i < ETH_ALEN; i++) {
        dev->broadcast[i]=0xff;
    }

    /* New-style flags. */
    dev->flags      = IFF_BROADCAST|IFF_MULTICAST;
    dev->family     = AF_INET;
    dev->pa_addr    = 0;
    dev->pa_brdaddr = 0;
    dev->pa_mask    = 0;
    dev->pa_alen    = sizeof(unsigned long);
}

/* Open/initialize the board. */
static int el_open(struct device *dev)
{
    int ioaddr = dev->base_addr;

    if (el_debug > 2)
    printk("%s: Doing el_open()...", dev->name);
    // 设置中断的回调是el_interrupt函数,网络收到数据包后会触发系统中断,系统会执行该函数
    if (request_irq(dev->irq, &el_interrupt, 0, "3c501")) {
    return -EAGAIN;
    }
    irq2dev_map[dev->irq] = dev;

    el_reset(dev);

    dev->start = 1;

    outb(AX_RX, AX_CMD);    /* Aux control, irq and receive enabled */
    MOD_INC_USE_COUNT;
    return 0;
}

设置完网卡对应的数据结构后,如果有数据包到达,由驱动程序中的这两个函数处理。

代码语言:javascript
复制
/* The typical workload of the driver:
   Handle the ether interface interrupts. */
static void
el_interrupt(int irq, struct pt_regs *regs)
{
    struct device *dev = (struct device *)(irq2dev_map[irq]);
    struct net_local *lp;
    int ioaddr;
    int axsr;            /* Aux. status reg. */

    if (dev == NULL  ||  dev->irq != irq) {
    printk ("3c501 driver: irq %d for unknown device.\n", irq);
    return;
    }

    ioaddr = dev->base_addr;
    lp = (struct net_local *)dev->priv;
    axsr = inb(AX_STATUS);

    if (el_debug > 3)
      printk("%s: el_interrupt() aux=%#02x", dev->name, axsr);
    if (dev->interrupt)
    printk("%s: Reentering the interrupt driver!\n", dev->name);
    dev->interrupt = 1;

    if (dev->tbusy) {

        /*
         *  Board in transmit mode.
         */

    int txsr = inb(TX_STATUS);

    if (el_debug > 6)
        printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),
           inw(RX_LOW));

    if ((axsr & 0x80) && (txsr & TX_READY) == 0) {
    /*
     *  FIXME: is there a logic to whether to keep on trying or
     *  reset immediately ?
     */
        printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
           " gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
           inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
        dev->tbusy = 0;
        mark_bh(NET_BH);
    } else if (txsr & TX_16COLLISIONS) {
    /*
     *  Timed out
     */
        if (el_debug)
        printk("%s: Transmit failed 16 times, ethernet jammed?\n",
               dev->name);
        outb(AX_SYS, AX_CMD);
        lp->stats.tx_aborted_errors++;
    } else if (txsr & TX_COLLISION) {   /* Retrigger xmit. */
        if (el_debug > 6)
        printk(" retransmitting after a collision.\n");
    /*
     *  Poor little chip can't reset its own start pointer
     */
        outb(AX_SYS, AX_CMD);
        outw(lp->tx_pkt_start, GP_LOW);
        outb(AX_XMIT, AX_CMD);
        lp->stats.collisions++;
        dev->interrupt = 0;
        return;
    } else {
    /*
     *  It worked.. we will now fall through and receive
     */
        lp->stats.tx_packets++;
        if (el_debug > 6)
        printk(" Tx succeeded %s\n",
               (txsr & TX_RDY) ? "." : "but tx is busy!");
    /*
     *  This is safe the interrupt is atomic WRT itself.
     */
        dev->tbusy = 0;
        mark_bh(NET_BH);    /* In case more to transmit */
    }
    } else {

        /*
         *  In receive mode.
         */

    int rxsr = inb(RX_STATUS);
    if (el_debug > 5)
        printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),
           inw(RX_LOW));

    /*
     *  Just reading rx_status fixes most errors. 
     */
    if (rxsr & RX_MISSED)
        lp->stats.rx_missed_errors++;
    if (rxsr & RX_RUNT) {   /* Handled to avoid board lock-up. */
        lp->stats.rx_length_errors++;
        if (el_debug > 5) printk(" runt.\n");
    } else if (rxsr & RX_GOOD) {
    /*
     *  Receive worked.
     */
        // 成功收到数据包后执行到这
        el_receive(dev);
    } else {            /* Nothing?  Something is broken! */
        if (el_debug > 2)
        printk("%s: No packet seen, rxsr=%02x **resetting 3c501***\n",
               dev->name, rxsr);
        el_reset(dev);
    }
    if (el_debug > 3)
        printk(".\n");
    }

    /*
     *    Move into receive mode 
     */
    outb(AX_RX, AX_CMD);
    outw(0x00, RX_BUF_CLR);
    inb(RX_STATUS);        /* Be certain that interrupts are cleared. */
    inb(TX_STATUS);
    dev->interrupt = 0;
    return;
}


/* We have a good packet. Well, not really "good", just mostly not broken.
   We must check everything to see if it is good. */
static void
el_receive(struct device *dev)
{
    struct net_local *lp = (struct net_local *)dev->priv;
    int ioaddr = dev->base_addr;
    int pkt_len;
    struct sk_buff *skb;
    // 包长度
    pkt_len = inw(RX_LOW);

    if (el_debug > 4)
      printk(" el_receive %d.\n", pkt_len);
    // 包太大或太小
    if ((pkt_len < 60)  ||  (pkt_len > 1536)) {
    if (el_debug)
      printk("%s: bogus packet, length=%d\n", dev->name, pkt_len);
    lp->stats.rx_over_errors++;
    return;
    }

    /*
     *    Command mode so we can empty the buffer
     */

    outb(AX_SYS, AX_CMD);
    // 分配一个承载数据的skb
    skb = alloc_skb(pkt_len, GFP_ATOMIC);
    /*
     *    Start of frame
     */
    outw(0x00, GP_LOW);
    if (skb == NULL) {
    printk("%s: Memory squeeze, dropping packet.\n", dev->name);
    lp->stats.rx_dropped++;
    return;
    } else {
    // 记录数据包长度和收到该包的设备
    skb->len = pkt_len;
    skb->dev = dev;

    /*
     *  The read increments through the bytes. The interrupt
     *  handler will fix the pointer when it returns to 
     *  receive mode.
     */
    // 读取数据到skb中 
    insb(DATAPORT, skb->data, pkt_len);
    // 传给mac层
    netif_rx(skb);
    lp->stats.rx_packets++;
    }
    return;
}

驱动层处理生成一个skb结构体,然后通过netif_rx函数传给链路层。netif_rx直接把skb挂载到backlog队列中,然后结束中断处理,等下半部分再进行数据包的具体处理。由sock_init函数的代码我们知道,下半部分的处理函数是net_bh。

代码语言:javascript
复制
/*
 *    Receive a packet from a device driver and queue it for the upper
 *    (protocol) levels.  It always succeeds. This is the recommended 
 *    interface to use.
 */

void netif_rx(struct sk_buff *skb)
{
    static int dropping = 0;

    /*
     *  Any received buffers are un-owned and should be discarded
     *  when freed. These will be updated later as the frames get
     *  owners.
     */
    skb->sk = NULL;
    skb->free = 1;
    if(skb->stamp.tv_sec==0)
        skb->stamp = xtime;

    /*
     *  Check that we aren't overdoing things.
     */
    // 是否过载
    if (!backlog_size)
          dropping = 0;
    else if (backlog_size > 300)
        dropping = 1;
    // 过载则丢弃
    if (dropping) 
    {
        kfree_skb(skb, FREE_READ);
        return;
    }

    /*
     *  Add it to the "backlog" queue. 
     */
#ifdef CONFIG_SKB_CHECK
    IS_SKB(skb);
#endif    
    // 加到backlog队列
    skb_queue_tail(&backlog,skb);
    backlog_size++;

    /*
     *  If any packet arrived, mark it for processing after the
     *  hardware interrupt returns.
     */
    // 激活下半部分,处理数据包
    mark_bh(NET_BH);
    return;
}
代码语言:javascript
复制
/*
 *    When we are called the queue is ready to grab, the interrupts are
 *    on and hardware can interrupt and queue to the receive queue a we
 *    run with no problems.
 *    This is run as a bottom half after an interrupt handler that does
 *    mark_bh(NET_BH);
 */

void net_bh(void *tmp)
{
    struct sk_buff *skb;
    struct packet_type *ptype;
    struct packet_type *pt_prev;
    unsigned short type;

    /*
     *  Atomically check and mark our BUSY state. 
     */
    // 防止重入
    if (set_bit(1, (void*)&in_bh))
        return;

    /*
     *  Can we send anything now? We want to clear the
     *  decks for any more sends that get done as we
     *  process the input.
     */
    // 发送缓存的数据包
    dev_transmit();

    /*
     *  Any data left to process. This may occur because a
     *  mark_bh() is done after we empty the queue including
     *  that from the device which does a mark_bh() just after
     */

    cli();

    /*
     *  While the queue is not empty
     */
    // backlog队列的数据包来源于网卡收到的数据包  
    while((skb=skb_dequeue(&backlog))!=NULL)
    {
        /*
         *  We have a packet. Therefore the queue has shrunk
         */
          backlog_size--;

        sti();

           /*
        *   Bump the pointer to the next structure.
        *   This assumes that the basic 'skb' pointer points to
        *   the MAC header, if any (as indicated by its "length"
        *   field).  Take care now!
        */
        // 指向ip头
        skb->h.raw = skb->data + skb->dev->hard_header_len;
        // 减去mac头长度
        skb->len -= skb->dev->hard_header_len;

           /*
        *   Fetch the packet protocol ID.  This is also quite ugly, as
        *   it depends on the protocol driver (the interface itself) to
        *   know what the type is, or where to get it from.  The Ethernet
        *   interfaces fetch the ID from the two bytes in the Ethernet MAC
        *   header (the h_proto field in struct ethhdr), but other drivers
        *   may either use the ethernet ID's or extra ones that do not
        *   clash (eg ETH_P_AX25). We could set this before we queue the
        *   frame. In fact I may change this when I have time.
        */
        // 判断上层协议
        type = skb->dev->type_trans(skb, skb->dev);

        /*
         *  We got a packet ID.  Now loop over the "known protocols"
         *  table (which is actually a linked list, but this will
         *  change soon if I get my way- FvK), and forward the packet
         *  to anyone who wants it.
         *
         *  [FvK didn't get his way but he is right this ought to be
         *  hashed so we typically get a single hit. The speed cost
         *  here is minimal but no doubt adds up at the 4,000+ pkts/second
         *  rate we can hit flat out]
         */
        pt_prev = NULL;
        for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) 
        {
            if ((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev))
            {
                /*
                 *  We already have a match queued. Deliver
                 *  to it and then remember the new match
                 */
                // 如果有匹配的项则要单独复制一份skb
                if(pt_prev)
                {
                    struct sk_buff *skb2;

                    skb2=skb_clone(skb, GFP_ATOMIC);

                    /*
                     *  Kick the protocol handler. This should be fast
                     *  and efficient code.
                     */

                    if(skb2)
                        pt_prev->func(skb2, skb->dev, pt_prev);
                }
                /* Remember the current last to do */
                // 记录最近匹配的项
                pt_prev=ptype;
            }
        } /* End of protocol list loop */

        /*
         *  Is there a last item to send to ?
         */
        // 把数据包交给上层协议处理,大于一个匹配项,则把skb复制给最后一项,否则销毁skb
        if(pt_prev)
            pt_prev->func(skb, skb->dev, pt_prev);
        /*
         *  Has an unknown packet has been received ?
         */

        else
            kfree_skb(skb, FREE_WRITE);

        /*
         *  Again, see if we can transmit anything now. 
         *  [Ought to take this out judging by tests it slows
         *   us down not speeds us up]
         */

        dev_transmit();
        cli();
      }   /* End of queue loop */

      /*
       *  We have emptied the queue
       */
      // 处理完毕 
      in_bh = 0;
    sti();

    /*
     *  One last output flush.
     */

    dev_transmit();
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档