[Click] tg3 polling extension

Qinghua(Kevin) Ye qye at trlabs.ca
Tue Jul 5 17:32:48 EDT 2005


Hi, all,

I am thinking about extending the tg3 driver to support Click Polling. Click has its patch on linux kernel, and adds some callback functions and polling variable to the net_device struct(I did this work on 2.6.11 kernel, and on AMD opteron):

netdev->polling = 0;       // Check if click polling is enabled or not

netdev->rx_poll = tg3_rx_poll;   
// Click calls rx_poll whenever PollDevice is scheduled. rx_poll
returns a list of sk_buff's that has been sitting in the NICs receive
buffer. rx_poll should NULL the sk_buff's send to Click.

netdev->rx_refill = tg3_rx_refill;
// If rx_poll returned any sk_buff's PollDevice then calls rx_refill
and passes a new list of sk_buff's to the driver. rx_refill should use
this list to update the drivers receive buffer and receive
descriptors.

netdev->tx_clean = tg3_tx_clean;
// Whenever ToDevice is scheduled it calls tx_clean to clean already
send sk_buffs from the NICs transmit queue. tx_clean should return
this list.

netdev->tx_queue = tg3_tx_pqueue; 
//ToDevice then calls tx_queue and passes an sk_buff to the driver for
transmission.

netdev->tx_eob = tg3_tx_eob;
/tx_eob should set the TX Descriptor Tail

netdev->tx_start = tg3_tx_start;
//tc_start calls tx_eob - Anyone more info on this ?

netdev->poll_off = tg3_poll_off;
//disabling click polling and enabling the NICs irq

netdev->poll_on = tg3_poll_on;
//disabling the NICs irq and enabling click polling


Based on this extension, I wrote these functions as follows. However, it works not well. For uni-process, it does work, but can only send thousands of packet if there is no packet coming in, no matter how many packets supposed to send out. I debugged it, seems that the problem is on the tg3_tx_clean side. It cannot clean the packet buffer on time. So the interface will be timeout, and reseted again and again. However, once there are many packets comming, the tx can send more packets out comparative to the number of packets comming in.

Another problem is: When I start two processes to deal with two interface, the system will crash after process tens of thousands packets. Sometimes I got this message in syslog:


Jun 30 13:00:42 node13 kernel: Oops: 0000 [1] SMP 
Jun 30 13:00:42 node13 kernel: CPU 0 
Jun 30 13:00:42 node13 kernel: Modules linked in: click proclikefs tg3 nfs lockd sunrpc ib_mthca lp button autofs ib_ipoib ib_sa ib_mad ib_core ohci1394 ieee1394 floppy parport_pc parport usbcore ide_disk ide_core
Jun 30 13:00:42 node13 kernel: Pid: 4300, comm: kclick Tainted: GF     2.6.11.6-click
Jun 30 13:00:42 node13 kernel: RIP: 0010:[<ffffffff881dfc14>] <ffffffff881dfc14>{:click:_ZN10PollDevice8run_taskEv+196}
Jun 30 13:00:42 node13 kernel: RSP: 0018:ffff81007ee65e48  EFLAGS: 00010297
Jun 30 13:00:42 node13 kernel: RAX: 000000000000006b RBX: 0000000000000000 RCX: 0000000000000002
Jun 30 13:00:42 node13 kernel: RDX: 0000000000000740 RSI: ffff81007e681340 RDI: 0000000000000000
Jun 30 13:00:42 node13 kernel: RBP: ffff81007e681340 R08: 0000000000000000 R09: 0000000000000000
Jun 30 13:00:42 node13 kernel: R10: 0000000000000690 R11: 0000000000000000 R12: ffff8100e605ce00
Jun 30 13:00:42 node13 kernel: R13: 0000000000000000 R14: 0000000000000001 R15: ffff81007ee65e58
Jun 30 13:00:42 node13 kernel: FS:  00002aaaaae044c0(0000) GS:ffffffff8047a980(0000) knlGS:0000000000000000
Jun 30 13:00:42 node13 kernel: CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
Jun 30 13:00:42 node13 kernel: CR2: 0000000000000000 CR3: 00000000e616c000 CR4: 00000000000006e0
Jun 30 13:00:42 node13 kernel: Process kclick (pid: 4300, threadinfo ffff81007ee64000, task ffff81007fc190d0)
Jun 30 13:00:42 node13 kernel: Stack: 0000000000000000 0000000200000000 0000000042c4415a 0000000000082509 
Jun 30 13:00:42 node13 kernel:        ffff8100e60899c0 ffff8100e605cea0 0000000000000006 0000000000000046 
Jun 30 13:00:42 node13 kernel:        ffff8100e60899c0 0000000000000000 
Jun 30 13:00:42 node13 kernel: Call Trace:<ffffffff881a420d>{:click:_ZN12RouterThread6driverEv+973} 
Jun 30 13:00:42 node13 kernel:        <ffffffff8820324a>{:click:_Z11click_schedPv+170} <ffffffff8010ef9f>{child_rip+8} 
Jun 30 13:00:42 node13 kernel:        <ffffffff882031a0>{:click:_Z11click_schedPv+0} <ffffffff8010ef97>{child_rip+0} 
Jun 30 13:00:42 node13 kernel:        
Jun 30 13:00:42 node13 kernel: 
Jun 30 13:00:42 node13 kernel: Code: 4d 8b 6d 00 48 c7 03 00 00 00 00 4d 85 ed 74 10 41 0f 18 4d 
Jun 30 13:00:42 node13 kernel: RIP <ffffffff881dfc14>{:click:_ZN10PollDevice8run_taskEv+196} RSP <ffff81007ee65e48>
Jun 30 13:00:42 node13 kernel: CR2: 0000000000000000


Any idea will be appreciated! Thanks.

Qinghua


Extended functions:
static struct sk_buff *
tg3_rx_poll(struct net_device *dev, int *want)
{
        struct tg3 *tp=dev->priv;
        int budget = *want;
        
        struct sk_buff *skb_head = 0, *skb_last = 0;
        int skb_size;
        u32 rx_rcb_ptr;
        u16 hw_idx, sw_idx;
        int received;
        int has_jumbo=0;
        struct tg3_hw_status *sblk = tp->hw_status;
        

         /* process the reset request to avoid reenable ints*/
        if(tp->reset == 1){
                tp->reset = 0;
                tg3_reset_task(tp);
                spin_lock(&tp->lock);
                tg3_disable_ints(tp);
                spin_unlock(&tp->lock);
        }
       
 
        *want = 0;
        rx_rcb_ptr=tp->rx_rcb_ptr;
        hw_idx = tp->hw_status->idx[0].rx_producer;
        /*
         * We need to order the read of hw_idx and the read of
         * the opaque cookie.
         */
        rmb();
        spin_lock(&tp->lock);
        sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE(tp);
        received = 0;
       
        while (sw_idx != hw_idx && budget > 0) {
                struct tg3_rx_buffer_desc *desc = &tp->rx_rcb[sw_idx];
                unsigned int len;
                struct sk_buff *skb;
                dma_addr_t dma_addr;
                u32 opaque_key, desc_idx, *post_ptr;
                
                desc_idx = desc->opaque & RXD_OPAQUE_INDEX_MASK;
                opaque_key = desc->opaque & RXD_OPAQUE_RING_MASK;
                                                                          
                                                                          
                if (opaque_key == RXD_OPAQUE_RING_STD) {
                        
                        if ((desc->err_vlan & RXD_ERR_MASK) != 0 &&
                        (desc->err_vlan != RXD_ERR_ODD_NIBBLE_RCVD_MII)) {                                rx_rcb_ptr++;
                                sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE(tp);
                                continue;
                        }
                                                                          
                        dma_addr = pci_unmap_addr(&tp->rx_std_buffers[desc_idx],                                                  mapping);
                        skb = tp->rx_std_buffers[desc_idx].skb;
                        if(!skb){

                  rx_rcb_ptr++;
                  sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE(tp);
                    continue;                                                    
}
                        skb_size = RX_PKT_BUF_SZ;
                } else if (opaque_key == RXD_OPAQUE_RING_JUMBO) {
                        /* Qinghua: We do not need jumbo packet, just take as error*/
                        
                        post_ptr = &tp->rx_jumbo_ptr;
                        tg3_recycle_rx(tp, opaque_key,
                                       desc_idx, *post_ptr);
                        *post_ptr++;
                        has_jumbo=1;
                        rx_rcb_ptr++;
                        sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE(tp);
                        continue;
                }
                else {
                        /* other case, do nothing */
                        rx_rcb_ptr++;
                        sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE(tp);
                        continue;
                }

                /* get the length of packet */
                len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) -
4; /*omit crc */
                                                                          
                pci_unmap_single(tp->pdev, dma_addr,
                                 skb_size - tp->rx_offset,
                                 PCI_DMA_FROMDEVICE);

                tp->rx_std_buffers[desc_idx].skb=NULL;
                skb_put(skb, len);
                //skb->protocol = eth_type_trans(skb, tp->dev);
                skb_pull(skb,dev->hard_header_len);

  /* process checksum */
               if ((tp->tg3_flags & TG3_FLAG_RX_CHECKSUMS) &&
                    (desc->type_flags & RXD_FLAG_TCPUDP_CSUM) &&
                    (((desc->ip_tcp_csum & RXD_TCPCSUM_MASK)
                      >> RXD_TCPCSUM_SHIFT) == 0xffff))
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                else
                        skb->ip_summed = CHECKSUM_NONE;
                                                                          
                /* let Click process it *
#if TG3_VLAN_TAG_USED
                if (tp->vlgrp != NULL &&
                    desc->type_flags & RXD_FLAG_VLAN) {
                        tg3_vlan_rx(tp, skb,
                                    desc->err_vlan & RXD_VLAN_MASK);
                } else
#endif
                        netif_receive_skb(skb, skb->protocol, 0);
                */
                
                if (received == 0) {
                        skb_head = skb;
                        skb_last = skb;
                        skb_last->next = NULL;
                } else {
                        skb_last->next = skb;
                        skb->next = NULL;
                        skb_last = skb;
                }
                                                                          
                tp->dev->last_rx = jiffies;
                received++;
                budget--;
                                                                          
                rx_rcb_ptr++;
                sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE(tp);
        }
                                                                          
                                                                          
        /* ACK the status ring. */
        if(tp->rx_rcb_ptr!=rx_rcb_ptr){
                tp->rx_rcb_ptr = rx_rcb_ptr;
                tw32_rx_mbox(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW,(rx_rcb_ptr % TG3_RX_RCB_RING_SIZE(tp)));
                mmiowb();
        }
        /* Refill RX ring(s). */
       
        if (has_jumbo) {
                
                sw_idx = tp->rx_jumbo_ptr % TG3_RX_JUMBO_RING_SIZE;
                tw32_rx_mbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW,
                             sw_idx);
                mmiowb();
        }
        
        *want = received;
        spin_unlock(&tp->lock);
        return skb_head;
}


int
tg3_rx_refill(struct net_device *dev, struct sk_buff **skbs)//, int rcb_idx)
{
        struct tg3 *tp=dev->priv;
        struct tg3_rx_buffer_desc *desc;
        struct ring_info *map;
        dma_addr_t mapping;
        u16 sw_idx;
        int nfilled = 0, last_filled = -1;
        struct sk_buff *skb_list;
        u32 rcb_idx,post_idx;
        u32 rx_rcb_ptr=tp->rx_rcb_ptr-1;
                                                                          
        rmb();
        if(skbs == 0){
                
                return (rcb_idx-post_idx)%TG3_RX_RING_SIZE;
        }
        sw_idx=rx_rcb_ptr%TG3_RX_RCB_RING_SIZE(tp);
        desc=&tp->rx_rcb[sw_idx];
        post_idx=tp->rx_std_ptr;
                                                                          
        rcb_idx=desc->opaque&RXD_OPAQUE_INDEX_MASK;

 /* if this is not a std ring index, or the ring is full, return */
        if(((desc->opaque&RXD_OPAQUE_RING_MASK)!=RXD_OPAQUE_RING_STD)||((rcb_idx-post_idx)%TG3_RX_RING_SIZE==0)){
                return 0;
        }
 
        spin_lock(&tp->lock);
        
                                                                          
        map = &tp->rx_std_buffers[post_idx];
        skb_list = *skbs;
                                                                          
                                                                          
        while(((rcb_idx - post_idx)%TG3_RX_RING_SIZE!=0)&& skb_list){
                struct sk_buff *skb = skb_list;
                if(map->skb!=NULL) {
                        
                        post_idx = (post_idx + 1) % TG3_RX_RING_SIZE;
                        map = &tp->rx_std_buffers[post_idx];
                        continue;
                }
                skb_list = skb_list->next;
                desc = &tp->rx_std[post_idx];
                                                                          
                                                                          
                skb->dev = dev;
                mapping = pci_map_single(tp->pdev, skb->data,
                                 RX_PKT_BUF_SZ - tp->rx_offset,
                                 PCI_DMA_FROMDEVICE);
        
                map->skb = skb;
                pci_unmap_addr_set(map, mapping, mapping);
                                                                          
                                                                          
                desc->addr_hi = ((u64)mapping >> 32);
                desc->addr_lo = ((u64)mapping & 0xffffffff);
                last_filled = post_idx;
                post_idx = (post_idx + 1) % TG3_RX_RING_SIZE;
                                                                          
                nfilled++;
                map = &tp->rx_std_buffers[post_idx];
        }
                                                                          
                                                                          
        /* Return the unsed list of skb */
        *skbs = skb_list;
                                                                          
        /* Refill RX ring(s). */
        //dest_idx = &tp->rx_std_ptr % TG3_RX_RING_SIZE;
        if(post_idx!=tp->rx_std_ptr){
                
                tp->rx_std_ptr = post_idx ;
                tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW,post_idx);
                mmiowb();
        }
                                                                          
 spin_unlock(&tp->lock);
      

 return (rcb_idx-post_idx)%TG3_RX_RING_SIZE;
}
                                                                          
static int
tg3_poll_on(struct net_device *dev)
{
        struct tg3 *tp = dev->priv;
        unsigned long flags;
                                                                          
                                                                          
        if (!dev->polling) {
                spin_lock_irqsave(&tp->lock,flags);
                dev->polling = 2;
                tp->reset = 0;
                tg3_disable_ints(tp);
                spin_unlock_irqrestore(&tp->lock,flags);
        }
        //printk("tg3_poll_on\n");
        return 0;
}

static int
tg3_poll_off(struct net_device *dev)
{
        struct tg3 *tp = dev->priv;
        unsigned long flags;
                                                                          
                                                                          
        printk("tg3_poll_off:begin\n");
        if (dev->polling > 0) {
                dev->polling = 0;
                spin_lock(&tp->lock);
                tg3_restart_ints(tp);
                spin_unlock(&tp->lock);
                //printk("tg3_poll_off\n");
        }
        printk("tg3_poll_off:end\n");
                                                                          
                                                                          
        return 0;
}

static int
tg3_tx_eob(struct net_device *dev)
{
        struct tg3 *tp = dev->priv;
 struct tg3_hw_status *sblk = tp->hw_status;
        
 
 // handle link change and other phy events 
 if (sblk->status & SD_STATUS_UPDATED) {
  spin_lock(&tp->lock);
  tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
        0x00000001);
  tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
  sblk->status &= ~SD_STATUS_UPDATED;
  spin_unlock(&tp->lock);
 }
        if (!(tp->tg3_flags &
              (TG3_FLAG_USE_LINKCHG_REG |
               TG3_FLAG_POLL_SERDES))) {
                if (sblk->status & SD_STATUS_LINK_CHG) {
   spin_lock(&tp->lock);
                        sblk->status = SD_STATUS_UPDATED |
                                (sblk->status & ~SD_STATUS_LINK_CHG);
                        tg3_setup_phy(tp, 0);
   spin_unlock(&tp->lock);
                }
        }
 
 spin_lock(&tp->tx_lock);
 
 /* Packets are ready, update Tx producer idx local and on card. */
 tw32_tx_mbox((MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW), tp->tx_prod);
     mmiowb();
 spin_unlock(&tp->tx_lock);
        return 0;
}
                                                                          
                                                                          
static int
tg3_tx_start(struct net_device *dev)
{
                                                                          
                                                                          
        //printk("tg3_tx_start\n");
        tg3_tx_eob(dev);
        return 0;
}

static int
tg3_tx_pqueue(struct net_device *netdev, struct sk_buff *skb)
{
        //return tg3_start_xmit(skb,netdev);
 struct tg3 *tp = netdev_priv(netdev);
 dma_addr_t mapping;
 unsigned int i;
 u32 len, entry, base_flags, mss;
 int would_hit_hwbug;
 unsigned long flags;

 len = skb_headlen(skb);

 /* No BH disabling for tx_lock here.  We are running in BH disabled
  * context and TX reclaim runs via tp->poll inside of a software
  * interrupt.  Rejoice!
  *
  * Actually, things are not so simple.  If we are to take a hw
  * IRQ here, we can deadlock, consider:
  *
  *       CPU1  CPU2
  *   tg3_start_xmit
  *   take tp->tx_lock
  *   tg3_timer
  *   take tp->lock
  *   tg3_interrupt
  *   spin on tp->lock
  *   spin on tp->tx_lock
  *
  * So we really do need to disable interrupts when taking
  * tx_lock here.
  */
 if (!spin_trylock(&tp->tx_lock)) { 
  return NETDEV_TX_LOCKED; 
 } 

 /* This is a hard error, log it. */
 if (unlikely(TX_BUFFS_AVAIL(tp) <= (skb_shinfo(skb)->nr_frags + 1))) {
  netif_stop_queue(netdev);
  spin_unlock(&tp->tx_lock);
  printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n",
         netdev->name);
  return NETDEV_TX_BUSY;
 }

 entry = tp->tx_prod;
 base_flags = 0;
 if (skb->ip_summed == CHECKSUM_HW)
  base_flags |= TXD_FLAG_TCPUDP_CSUM;
#if TG3_TSO_SUPPORT != 0
 mss = 0;
 if (skb->len > (tp->dev->mtu + ETH_HLEN) &&
     (mss = skb_shinfo(skb)->tso_size) != 0) {
  int tcp_opt_len, ip_tcp_len;

  tcp_opt_len = ((skb->h.th->doff - 5) * 4);
  ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr);

  base_flags |= (TXD_FLAG_CPU_PRE_DMA |
          TXD_FLAG_CPU_POST_DMA);

  skb->nh.iph->check = 0;
  skb->nh.iph->tot_len = ntohs(mss + ip_tcp_len + tcp_opt_len);
  if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
   skb->h.th->check = 0;
   base_flags &= ~TXD_FLAG_TCPUDP_CSUM;
  }
  else {
   skb->h.th->check =
    ~csum_tcpudp_magic(skb->nh.iph->saddr,
         skb->nh.iph->daddr,
         0, IPPROTO_TCP, 0);
  }

  if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) ||
      (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) {
   if (tcp_opt_len || skb->nh.iph->ihl > 5) {
    int tsflags;

    tsflags = ((skb->nh.iph->ihl - 5) +
        (tcp_opt_len >> 2));
    mss |= (tsflags << 11);
   }
  } else {
   if (tcp_opt_len || skb->nh.iph->ihl > 5) {
    int tsflags;

    tsflags = ((skb->nh.iph->ihl - 5) +
        (tcp_opt_len >> 2));
    base_flags |= tsflags << 12;
   }
  }
 }
#else
 mss = 0;
#endif
#if TG3_VLAN_TAG_USED
 if (tp->vlgrp != NULL && vlan_tx_tag_present(skb))
  base_flags |= (TXD_FLAG_VLAN |
          (vlan_tx_tag_get(skb) << 16));
#endif

 /* Queue skb data, a.k.a. the main skb fragment. */
 mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE);

 tp->tx_buffers[entry].skb = skb;
 pci_unmap_addr_set(&tp->tx_buffers[entry], mapping, mapping);

 would_hit_hwbug = 0;

 if (tg3_4g_overflow_test(mapping, len))
  would_hit_hwbug = entry + 1;

 tg3_set_txd(tp, entry, mapping, len, base_flags,
      (skb_shinfo(skb)->nr_frags == 0) | (mss << 1));

 entry = NEXT_TX(entry);

 /* Now loop through additional data fragments, and queue them. */
 if (skb_shinfo(skb)->nr_frags > 0) {
  unsigned int i, last;

  last = skb_shinfo(skb)->nr_frags - 1;
  for (i = 0; i <= last; i++) {
   skb_frag_t *frag = &skb_shinfo(skb)->frags[i];

   len = frag->size;
   mapping = pci_map_page(tp->pdev,
            frag->page,
            frag->page_offset,
            len, PCI_DMA_TODEVICE);

   tp->tx_buffers[entry].skb = NULL;
   pci_unmap_addr_set(&tp->tx_buffers[entry], mapping, mapping);

   if (tg3_4g_overflow_test(mapping, len)) {
    /* Only one should match. */
    if (would_hit_hwbug)
     BUG();
    would_hit_hwbug = entry + 1;
   }

   if (tp->tg3_flags2 & TG3_FLG2_HW_TSO)
    tg3_set_txd(tp, entry, mapping, len,
         base_flags, (i == last)|(mss << 1));
   else
    tg3_set_txd(tp, entry, mapping, len,
         base_flags, (i == last));

   entry = NEXT_TX(entry);
  }
 }

 if (would_hit_hwbug) {
  u32 last_plus_one = entry;
  u32 start;
  unsigned int len = 0;

  would_hit_hwbug -= 1;
  entry = entry - 1 - skb_shinfo(skb)->nr_frags;
  entry &= (TG3_TX_RING_SIZE - 1);
  start = entry;
  i = 0;
  while (entry != last_plus_one) {
   if (i == 0)
    len = skb_headlen(skb);
   else
    len = skb_shinfo(skb)->frags[i-1].size;

   if (entry == would_hit_hwbug)
    break;

   i++;
   entry = NEXT_TX(entry);

  }

  /* If the workaround fails due to memory/mapping
   * failure, silently drop this packet.
   */
  if (tigon3_4gb_hwbug_workaround(tp, skb,
      entry, len,
      last_plus_one,
      &start, mss))
   goto queue_out;

  entry = start;
 }


 tp->tx_prod = entry;
 if (TX_BUFFS_AVAIL(tp) <= (MAX_SKB_FRAGS + 1))
  netif_stop_queue(netdev);

queue_out:
 spin_unlock(&tp->tx_lock);

 netdev->trans_start = jiffies;

 return NETDEV_TX_OK;
}

static struct sk_buff *
tg3_tx_poll(struct tg3 *tp)
{
 u32 hw_idx = tp->hw_status->idx[0].tx_consumer;
 u32 sw_idx = tp->tx_cons;
        struct sk_buff *skb_head,*skb_last;

 skb_head=skb_last=0;

 while (sw_idx != hw_idx) {
  struct tx_ring_info *ri = &tp->tx_buffers[sw_idx];
  struct sk_buff *skb = ri->skb;
  int i;

  if (unlikely(skb == NULL))
   BUG();

  pci_unmap_single(tp->pdev,
     pci_unmap_addr(ri, mapping),
     skb_headlen(skb),
     PCI_DMA_TODEVICE);

  ri->skb = NULL;

  sw_idx = NEXT_TX(sw_idx);

  for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
   if (unlikely(sw_idx == hw_idx))
    BUG();

   ri = &tp->tx_buffers[sw_idx];
   if (unlikely(ri->skb != NULL))
    BUG();

   pci_unmap_page(tp->pdev,
           pci_unmap_addr(ri, mapping),
           skb_shinfo(skb)->frags[i].size,
           PCI_DMA_TODEVICE);

   sw_idx = NEXT_TX(sw_idx);
  }
     
                if (skb_head == 0) {
                        skb_head = skb;
                        skb_last = skb;
                        skb_last->next = NULL;
                } else {
                        skb_last->next = skb;
                        skb->next = NULL;
                        skb_last = skb;
                }
        
//  dev_kfree_skb_irq(skb);
 }

 tp->tx_cons = sw_idx;

 if (netif_queue_stopped(tp->dev) &&
     (TX_BUFFS_AVAIL(tp) > TG3_TX_WAKEUP_THRESH))
  netif_wake_queue(tp->dev);
 return skb_head;
}


static struct sk_buff *
tg3_tx_clean(struct net_device *netdev)
{
        struct tg3 *tp = netdev_priv(netdev);
        struct sk_buff *skb_head,*skb_last;
 u32 hw_idx = tp->hw_status->idx[0].tx_consumer;
 u32 sw_idx = tp->tx_cons;

 skb_head=skb_last=0;
 rmb();                                                    
                spin_lock(&tp->lock);

       if(tp->hw_status->idx[0].tx_consumer == tp->tx_cons){

  spin_unlock(&tp->lock);
  return NULL;
        }

 spin_lock(&tp->tx_lock);
        
//        skb_head = tg3_tx_poll(tp);


 while (sw_idx != hw_idx) {
  struct tx_ring_info *ri = &tp->tx_buffers[sw_idx];
  struct sk_buff *skb = ri->skb;
  int i;

  if (unlikely(skb == NULL))
   BUG();

  pci_unmap_single(tp->pdev,
     pci_unmap_addr(ri, mapping),
     skb_headlen(skb),
     PCI_DMA_TODEVICE);

  ri->skb = NULL;

  sw_idx = NEXT_TX(sw_idx);

  for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
   if (unlikely(sw_idx == hw_idx))
    BUG();

   ri = &tp->tx_buffers[sw_idx];
   if (unlikely(ri->skb != NULL))
    BUG();

   pci_unmap_page(tp->pdev,
           pci_unmap_addr(ri, mapping),
           skb_shinfo(skb)->frags[i].size,
           PCI_DMA_TODEVICE);

   sw_idx = NEXT_TX(sw_idx);
  }
     
                if (skb_head == 0) {
                        skb_head = skb;
                        skb_last = skb;
                        skb_last->next = NULL;
                } else {
                        skb_last->next = skb;
                        skb->next = NULL;
                        skb_last = skb;
                }
        
//  dev_kfree_skb_irq(skb);
 }

 tp->tx_cons = sw_idx;

 if (netif_queue_stopped(tp->dev) &&
     (TX_BUFFS_AVAIL(tp) > TG3_TX_WAKEUP_THRESH))
  netif_wake_queue(tp->dev);
 //return skb_head;
/* the end of tx_poll function */
        spin_unlock(&tp->tx_lock);
        spin_unlock(&tp->lock);
        return skb_head;
}

                                


More information about the click mailing list