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 {
|