D&C GLug - Home Page

[ Date Index ] [ Thread Index ] [ <= Previous by date / thread ] [ Next by date / thread => ]

Re: [LUG] Tasklets

 


Yes its perfectly feasable, its all gpl and on sourceforge in a cvs tree. 
So I 
can either post sections of offending code or you can have a browse of the 
tree directly. What is simplest?

Offending code is probably easiest. I didn't get to bed last night and 
I'm not firing on all cylinders...


Ok here goes its quite a bit of code but it is the important bits.
rt2x00_interrupt_rxdone is called from a master interupt handler that reads a 
bit mask on the device to determine what interrupt routine to fire, that 
(master handler) is protected with a spin_lock_irq_save() and 
spin_lock_irq_restore(). 


void rt2x00_interrupt_rxdone(struct _rt2x00_adapter *adapter)
{
       struct _rxd             rxd;
       u8                      *dma = NULL;
       struct ieee802_11_hdr   *header802_11 = NULL;
       struct sk_buff          *skb = NULL;
       struct _skb_cb          *skb_cb = NULL;

       spin_lock(&adapter->rx_lock);

       while(adapter->dma->rx_ring_done_index != adapter->dma->rx_ring_index){
               dma = (u8*) 
adapter->dma->rx_ring[adapter->dma->rx_ring_index].data_addr;
               device_to_cpu(&rxd, 
adapter->dma->rx_ring[adapter->dma->rx_ring_index].desc_addr, sizeof(struct 
_rxd));
               header802_11 = (struct ieee802_11_hdr*)dma; 

               if(rxd.owner == DESC_OWNED_BY_NIC)
                       break;

               /* If it is the first frame of multiple fragments allocate the 
maximum 
frame_size. */
               if(header802_11->frame_ctl & IEEE802_11_FCTL_MOREFRAGS &&
                 !(header802_11->seq_ctl & IEEE802_11_SCTL_FRAG))
                       skb = dev_alloc_skb(MAX_PACKET_SIZE);
               else
                       skb = dev_alloc_skb(rxd.data_byte_count + 2);
               if(!skb){
                       adapter->stats.rx_dropped++;
                       /* Hand dma back to the adapter. */
                       rxd.owner = DESC_OWNED_BY_NIC;
                       
cpu_to_device(adapter->dma->rx_ring[adapter->dma->rx_ring_index].desc_addr, 
&rxd, sizeof(struct _rxd));
                       break;
               }

               skb_reserve(skb, 2);    /* 16 byte align the IP header */

//              code below seems to be illegal before skb_put
//              skb_cb = (struct _skb_cb*)skb->cb;
//              memset(skb_cb, 0x00, sizeof(struct _skb_cb));

//              memcpy(&skb_cb->rxd, &rxd, sizeof(struct _rxd));
//              skb_cb->header_length = IEEE802_11_HLEN;

               /* Copy entire frame to buffer. */
               device_to_cpu(skb_put(skb, rxd.data_byte_count), dma, 
rxd.data_byte_count);

               /* Enqueue packet for processing. */
               skb_queue_tail(&adapter->rx_packet_queue, skb);

               /* Hand dma back to the adapter. */
               rxd.owner = DESC_OWNED_BY_NIC;
               
cpu_to_device(adapter->dma->rx_ring[adapter->dma->rx_ring_index].desc_addr, 
&rxd, sizeof(struct _rxd));

               /* Move to next packet in dma-> */
               INCREASE_RING(adapter->dma->rx_ring_index, RX_RING_SIZE);
       }

       /* 
        * Update the done index. 
        * Make sure the rx done index is 1 lower then main index.
        * Also make sure to check for end of buffer.
        */
       if(adapter->dma->rx_ring_index == 0)
               adapter->dma->rx_ring_done_index = RX_RING_SIZE;
       else
               adapter->dma->rx_ring_done_index = adapter->dma->rx_ring_index - 1;

       /* Schedule rx packet processing. */
       if(!skb_queue_empty(&adapter->rx_packet_queue))
               tasklet_schedule(&adapter->rx_tasklet);

       spin_unlock(&adapter->rx_lock);
}


That is the interupt handler, it *basicly* works although i believe the stuff 
with skn->cb to be illegal before a skb_put, so i am not running that at the 
moment (commented out as above).

device_to_cpu and cpu_to_device are basicly a big/little end convertor for 
multiple ARCHS, for the moment it translates directly (in my cvs tree anyway) 
to a memcpy();

If i replace the tasklet_schedule with a *direct* call to the tasklet handler 
then all that code works. But left as is here the tasklets don't fire and a 
big crash is encountered at some point later.

during the device init there is the following

               tasklet_init(&adapter->rx_tasklet, rt2x00_delayed_handle_rx, 
(unsigned                          
                       long)adapter);


and during net device up there is a

       tasklet_enable(&adapter->rx_tasklet);

duing net device down/stop there is a

       tasklet_kill(&adapter->rx_tasklet);

       tasklet_disable(&adapter->rx_tasklet);

The tasklet handler is 

void rt2x00_delayed_handle_rx(unsigned long data)
{
       struct _rt2x00_adapter          *adapter = (struct _rt2x00_adapter*) data;
       struct sk_buff                  *skb = NULL;

       spin_lock_bh(&adapter->rx_lock);

       while(!skb_queue_empty(&adapter->rx_packet_queue)){

               if(!TEST_FLAG(adapter, ADAPTER_ENABLED))
                       break; /* We are not ready, abort sending packets. */

               skb = skb_dequeue(&adapter->rx_packet_queue);

               skb->dev = adapter->net_dev;                            /* Packet 
comes from our device. */
               skb->protocol = eth_type_trans(skb, adapter->net_dev);  /* Update 
protocol. 
*/

               adapter->stats.rx_packets++;
               adapter->stats.rx_bytes += skb->len;
               if(((struct _skb_cb*)skb)->multicast)
                       adapter->stats.multicast++;

               if(rt2x00_receive_packet(adapter, skb)){                /* Packet 
received succesfull. */
                       netif_rx(skb);                                  /* Send 
packet to upper layer. */
                       adapter->net_dev->last_rx = jiffies;            /* Update RX 
time. */
               } else {                                                /* Packet 
failed. */
                       adapter->stats.rx_errors++;                     /* Update 
error count. */
                       dev_kfree_skb_irq(skb);                         /* Release 
skb packet. */
               }
       }

       spin_unlock_bh(&adapter->rx_lock);

       if(LINK_TYPE(adapter, IW_MODE_REPEAT) 
&& !skb_queue_empty(&adapter->tx_packet_queue))
               tasklet_schedule(&adapter->tx_tasklet);
}


-- 
Robin Cornelius
---------------------------------------------------
robin@xxxxxxxxxxxxxxxxxxxxx
http://www.cornelius.demon.co.uk
http://sourceforge.net/projects/rt2400
GPG Key ID: 0x729A79A23B7EE764
http://www.biglumber.com/x/web?qs=0x729A79A23B7EE764

Attachment: pgp00015.pgp
Description: PGP signature