Index: linux-2.4.35.4/include/linux/skbuff.h =================================================================== --- linux-2.4.35.4.orig/include/linux/skbuff.h 2007-12-15 05:19:38.654623841 +0100 +++ linux-2.4.35.4/include/linux/skbuff.h 2007-12-15 05:20:05.060128604 +0100 @@ -912,6 +912,49 @@ skb->tail+=len; } +/* + * CPUs often take a performance hit when accessing unaligned memory + * locations. The actual performance hit varies, it can be small if the + * hardware handles it or large if we have to take an exception and fix it + * in software. + * + * Since an ethernet header is 14 bytes network drivers often end up with + * the IP header at an unaligned offset. The IP header can be aligned by + * shifting the start of the packet by 2 bytes. Drivers should do this + * with: + * + * skb_reserve(NET_IP_ALIGN); + * + * The downside to this alignment of the IP header is that the DMA is now + * unaligned. On some architectures the cost of an unaligned DMA is high + * and this cost outweighs the gains made by aligning the IP header. + * + * Since this trade off varies between architectures, we allow NET_IP_ALIGN + * to be overridden. + */ +#ifndef NET_IP_ALIGN +#define NET_IP_ALIGN 2 +#endif + +/* + * The networking layer reserves some headroom in skb data (via + * dev_alloc_skb). This is used to avoid having to reallocate skb data when + * the header has to grow. In the default case, if the header has to grow + * 16 bytes or less we avoid the reallocation. + * + * Unfortunately this headroom changes the DMA alignment of the resulting + * network packet. As for NET_IP_ALIGN, this unaligned DMA is expensive + * on some architectures. An architecture can override this value, + * perhaps setting it to a cacheline in size (since that will maintain + * cacheline alignment of the DMA). It must be a power of 2. + * + * Various parts of the networking layer expect at least 16 bytes of + * headroom, you should not reduce this. + */ +#ifndef NET_SKB_PAD +#define NET_SKB_PAD 16 +#endif + extern int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc); static inline void __skb_trim(struct sk_buff *skb, unsigned int len) Index: linux-2.4.35.4/drivers/net/tun.c =================================================================== --- linux-2.4.35.4.orig/drivers/net/tun.c 2007-12-15 05:19:38.642623156 +0100 +++ linux-2.4.35.4/drivers/net/tun.c 2007-12-15 05:20:05.056128377 +0100 @@ -185,22 +185,31 @@ { struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) }; struct sk_buff *skb; - size_t len = count; + size_t len = count, align = 0; if (!(tun->flags & TUN_NO_PI)) { if ((len -= sizeof(pi)) > count) return -EINVAL; - memcpy_fromiovec((void *)&pi, iv, sizeof(pi)); + if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi))) + return -EFAULT; } - - if (!(skb = alloc_skb(len + 2, GFP_KERNEL))) { + + if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) + align = NET_IP_ALIGN; + + if (!(skb = alloc_skb(len + align, GFP_KERNEL))) { tun->stats.rx_dropped++; return -ENOMEM; } - skb_reserve(skb, 2); - memcpy_fromiovec(skb_put(skb, len), iv, len); + if (align) + skb_reserve(skb, align); + if (memcpy_fromiovec(skb_put(skb, len), iv, len)) { + tun->stats.rx_dropped++; + kfree_skb(skb); + return -EFAULT; + } skb->dev = &tun->dev; switch (tun->flags & TUN_TYPE_MASK) { @@ -271,7 +280,8 @@ pi.flags |= TUN_PKT_STRIP; } - memcpy_toiovec(iv, (void *) &pi, sizeof(pi)); + if(memcpy_toiovec(iv, (void *) &pi, sizeof(pi))) + return -EFAULT; total += sizeof(pi); }