netdev
[Top] [All Lists]

Q: (ab)using zerocopy for drivers with alignment contraints

To: netdev@xxxxxxxxxxx
Subject: Q: (ab)using zerocopy for drivers with alignment contraints
From: Manfred Spraul <manfred@xxxxxxxxxxxxxxxx>
Date: Sun, 10 Jun 2001 16:58:57 +0200
Sender: owner-netdev@xxxxxxxxxxx
Several cheap busmaster nics only accept tx buffers that are 32-bit
aligned.

Currently they memcpy into transfer buffers. What about replacing that
memcpy with csum_copy_partial_nocheck and enabling NETIF_F_{SG,HW_CSUM}? 

I've attached a beta patch against the 8139too driver.

szc_copy_csum() isn't driver specific, perhaps move it to
linux/net/core/skbuff.c?

--
        Manfred
--- 2.4/drivers/net/8139too.c   Sun Jun 10 12:54:37 2001
+++ build-2.4/drivers/net/8139too.c     Sun Jun 10 15:17:13 2001
@@ -656,6 +656,69 @@
 
 #endif /* USE_IO_OPS */
 
+/***************************************************************************/
+/* zerocopy support */
+#include <linux/ipv6.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+
+#ifdef MAX_SKB_FRAGS
+#define ENABLE_SZC
+#endif
+
+#ifdef ENABLE_SZC 
+#define                SZC_FEATURES    (NETIF_F_SG|NETIF_F_HW_CSUM)
+
+static int szc_copy_csum(void *tbuf, struct sk_buff *skb, int mask)
+{
+       int i, t, csum, csstart;
+
+       if (( !(unsigned long)skb->data & mask) &&
+               (skb->ip_summed != CHECKSUM_HW) &&
+               (skb_shinfo(skb)->nr_frags == 0) )
+               return 0;
+
+       t = skb->len - skb->data_len;
+       if (skb->ip_summed == CHECKSUM_HW)
+               csstart = skb->h.raw - skb->data;
+        else
+               csstart = t;
+       if (csstart > t) BUG();
+       memcpy(tbuf, skb->data, csstart);
+       if (t != csstart)
+               csum = csum_partial_copy_nocheck(skb->data+csstart, 
tbuf+csstart, t-csstart, 0);
+       else
+               csum = 0;
+       for (i=0;i<skb_shinfo(skb)->nr_frags;i++) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+               void *ptr = kmap_skb_frag(frag);
+               if (skb->ip_summed == CHECKSUM_HW)
+                       csum = csum_partial_copy_nocheck(ptr+frag->page_offset, 
tbuf+t, frag->size, csum);
+               else
+                       memcpy(tbuf+t, ptr+frag->page_offset, frag->size);
+               kunmap_skb_frag(ptr);
+               t += frag->size;
+       }
+       if(skb->ip_summed == CHECKSUM_HW) {
+               int csstuff = csstart + skb->csum;
+               *((unsigned short*)(tbuf+csstuff)) = csum_fold(csum);
+       }
+       return 1;
+}
+#else
+#define                SZC_FEATURES    (0)
+
+static int szc_copy_csum(void *tbuf, struct sk_buff *skb, int mask)
+{
+       if ( (unsigned long)skb->data & mask)
+               return 1;
+
+       memcpy(tbuf, skb->data, skb->len);
+       return 0;
+}
+#endif
+/* END */
+/***************************************************************************/
 
 static const u16 rtl8139_intr_mask =
        PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
@@ -927,6 +990,7 @@
        dev->do_ioctl = mii_ioctl;
        dev->tx_timeout = rtl8139_tx_timeout;
        dev->watchdog_timeo = TX_TIMEOUT;
+       dev->features |= SZC_FEATURES;
 
        dev->irq = pdev->irq;
 
@@ -1662,8 +1726,6 @@
        netif_wake_queue (dev);
 }
 
-
-
 static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
 {
        struct rtl8139_private *tp = dev->priv;
@@ -1677,9 +1739,9 @@
        assert (tp->tx_info[entry].mapping == 0);
 
        tp->tx_info[entry].skb = skb;
-       if ((long) skb->data & 3) {     /* Must use alignment buffer. */
+       if (szc_copy_csum(tp->tx_buf[entry], skb, 3)) {
+               /* Using alignment buffer. */
                /* tp->tx_info[entry].mapping = 0; */
-               memcpy (tp->tx_buf[entry], skb->data, skb->len);
                RTL_W32 (TxAddr0 + (entry * 4),
                         tp->tx_bufs_dma + (tp->tx_buf[entry] - tp->tx_bufs));
        } else {

<Prev in Thread] Current Thread [Next in Thread>