summaryrefslogtreecommitdiff
path: root/net/core/dev.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-04 18:11:38 -0800
committerLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-04 18:11:38 -0800
commit1a0153507ffae9cf3350e76c12d441788c0191e1 (patch)
treed05a502b4fc05202c84c1667019460c08ea088cd /net/core/dev.c
parentb0683ac8928c4cf40646a6ce3eb6ffe94605acfa (diff)
v2.4.3.2 -> v2.4.3.3
- Hui-Fen Hsu: sis900 driver update - NIIBE Yutaka: Super-H update - Alan Cox: more resyncs (ARM down, but more to go) - David Miller: network zerocopy, Sparc sync, qlogic,FC fix, etc. - David Miller/me: get rid of various drivers hacks to do mmap alignment behind the back of the VM layer. Create a real protocol for it.
Diffstat (limited to 'net/core/dev.c')
-rw-r--r--net/core/dev.c92
1 files changed, 87 insertions, 5 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 3276ee10efa6..779e565c0da8 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -91,6 +91,8 @@
#include <net/dst.h>
#include <net/pkt_sched.h>
#include <net/profile.h>
+#include <net/checksum.h>
+#include <linux/highmem.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/module.h>
@@ -503,7 +505,7 @@ struct net_device * dev_get_by_index(int ifindex)
}
/**
- * dev_getbyhwaddr - find a device by its hardware addres
+ * dev_getbyhwaddr - find a device by its hardware address
* @type: media type of device
* @ha: hardware address
*
@@ -871,12 +873,10 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
*/
skb2->mac.raw = skb2->data;
- if (skb2->nh.raw < skb2->data || skb2->nh.raw >= skb2->tail) {
+ if (skb2->nh.raw < skb2->data || skb2->nh.raw > skb2->tail) {
if (net_ratelimit())
printk(KERN_DEBUG "protocol %04x is buggy, dev %s\n", skb2->protocol, dev->name);
skb2->nh.raw = skb2->data;
- if (dev->hard_header)
- skb2->nh.raw += dev->hard_header_len;
}
skb2->h.raw = skb2->nh.raw;
@@ -887,6 +887,55 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
br_read_unlock(BR_NETPROTO_LOCK);
}
+/* Calculate csum in the case, when packet is misrouted.
+ * If it failed by some reason, ignore and send skb with wrong
+ * checksum.
+ */
+struct sk_buff * skb_checksum_help(struct sk_buff *skb)
+{
+ int offset;
+ unsigned int csum;
+
+ offset = skb->h.raw - skb->data;
+ if (offset > (int)skb->len)
+ BUG();
+ csum = skb_checksum(skb, offset, skb->len-offset, 0);
+
+ offset = skb->tail - skb->h.raw;
+ if (offset <= 0)
+ BUG();
+ if (skb->csum+2 > offset)
+ BUG();
+
+ *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
+ skb->ip_summed = CHECKSUM_NONE;
+ return skb;
+}
+
+#ifdef CONFIG_HIGHMEM
+/* Actually, we should eliminate this check as soon as we know, that:
+ * 1. IOMMU is present and allows to map all the memory.
+ * 2. No high memory really exists on this machine.
+ */
+
+static inline int
+illegal_highdma(struct net_device *dev, struct sk_buff *skb)
+{
+ int i;
+
+ if (dev->features&NETIF_F_HIGHDMA)
+ return 0;
+
+ for (i=0; i<skb_shinfo(skb)->nr_frags; i++)
+ if (skb_shinfo(skb)->frags[i].page >= highmem_start_page)
+ return 1;
+
+ return 0;
+}
+#else
+#define illegal_highdma(dev, skb) (0)
+#endif
+
/**
* dev_queue_xmit - transmit a buffer
* @skb: buffer to transmit
@@ -899,12 +948,41 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
* guarantee the frame will be transmitted as it may be dropped due
* to congestion or traffic shaping.
*/
-
+
int dev_queue_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct Qdisc *q;
+ if (skb_shinfo(skb)->frag_list &&
+ !(dev->features&NETIF_F_FRAGLIST) &&
+ skb_linearize(skb, GFP_ATOMIC) != 0) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ /* Fragmented skb is linearized if device does not support SG,
+ * or if at least one of fragments is in highmem and device
+ * does not support DMA from it.
+ */
+ if (skb_shinfo(skb)->nr_frags &&
+ (!(dev->features&NETIF_F_SG) || illegal_highdma(dev, skb)) &&
+ skb_linearize(skb, GFP_ATOMIC) != 0) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ /* If packet is not checksummed and device does not support
+ * checksumming for this protocol, complete checksumming here.
+ */
+ if (skb->ip_summed == CHECKSUM_HW &&
+ (!(dev->features&(NETIF_F_HW_CSUM|NETIF_F_NO_CSUM)) &&
+ (!(dev->features&NETIF_F_IP_CSUM) ||
+ skb->protocol != htons(ETH_P_IP)))) {
+ if ((skb = skb_checksum_help(skb)) == NULL)
+ return -ENOMEM;
+ }
+
/* Grab device queue */
spin_lock_bh(&dev->queue_lock);
q = dev->qdisc;
@@ -1183,6 +1261,10 @@ static int deliver_to_old_ones(struct packet_type *pt, struct sk_buff *skb, int
if (skb == NULL)
return ret;
}
+ if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) {
+ kfree_skb(skb);
+ return ret;
+ }
/* The assumption (correct one) is that old protocols
did not depened on BHs different of NET_BH and TIMER_BH.