diff options
| author | Herbert Xu <herbert@gondor.apana.org.au> | 2005-11-10 16:01:24 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2005-11-10 16:01:24 -0500 |
| commit | fb286bb2990a107009dbf25f6ffebeb7df77f9be (patch) | |
| tree | 0eede2c37f1b3831e59601933eebf6b82be75ffc | |
| parent | 1064e944d03eb7a72c0fa11236d5e69cfd877a71 (diff) | |
[NET]: Detect hardware rx checksum faults correctly
Here is the patch that introduces the generic skb_checksum_complete
which also checks for hardware RX checksum faults. If that happens,
it'll call netdev_rx_csum_fault which currently prints out a stack
trace with the device name. In future it can turn off RX checksum.
I've converted every spot under net/ that does RX checksum checks to
use skb_checksum_complete or __skb_checksum_complete with the
exceptions of:
* Those places where checksums are done bit by bit. These will call
netdev_rx_csum_fault directly.
* The following have not been completely checked/converted:
ipmr
ip_vs
netfilter
dccp
This patch is based on patches and suggestions from Stephen Hemminger
and David S. Miller.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/linux/netdevice.h | 7 | ||||
| -rw-r--r-- | include/linux/skbuff.h | 27 | ||||
| -rw-r--r-- | include/net/tcp.h | 3 | ||||
| -rw-r--r-- | net/core/datagram.c | 21 | ||||
| -rw-r--r-- | net/core/dev.c | 12 | ||||
| -rw-r--r-- | net/core/netpoll.c | 18 | ||||
| -rw-r--r-- | net/ipv4/icmp.c | 6 | ||||
| -rw-r--r-- | net/ipv4/igmp.c | 19 | ||||
| -rw-r--r-- | net/ipv4/ip_gre.c | 15 | ||||
| -rw-r--r-- | net/ipv4/netfilter/ip_conntrack_proto_icmp.c | 11 | ||||
| -rw-r--r-- | net/ipv4/tcp_ipv4.c | 24 | ||||
| -rw-r--r-- | net/ipv4/udp.c | 7 | ||||
| -rw-r--r-- | net/ipv6/icmp.c | 21 | ||||
| -rw-r--r-- | net/ipv6/raw.c | 42 | ||||
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 20 | ||||
| -rw-r--r-- | net/ipv6/udp.c | 25 | ||||
| -rw-r--r-- | net/rxrpc/transport.c | 15 | ||||
| -rw-r--r-- | net/sunrpc/socklib.c | 5 | ||||
| -rw-r--r-- | net/sunrpc/svcsock.c | 9 |
19 files changed, 173 insertions, 134 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c6efce4a04a4..936f8b76114e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
| @@ -927,6 +927,13 @@ extern int netdev_max_backlog; | |||
| 927 | extern int weight_p; | 927 | extern int weight_p; |
| 928 | extern int netdev_set_master(struct net_device *dev, struct net_device *master); | 928 | extern int netdev_set_master(struct net_device *dev, struct net_device *master); |
| 929 | extern int skb_checksum_help(struct sk_buff *skb, int inward); | 929 | extern int skb_checksum_help(struct sk_buff *skb, int inward); |
| 930 | #ifdef CONFIG_BUG | ||
| 931 | extern void netdev_rx_csum_fault(struct net_device *dev); | ||
| 932 | #else | ||
| 933 | static inline void netdev_rx_csum_fault(struct net_device *dev) | ||
| 934 | { | ||
| 935 | } | ||
| 936 | #endif | ||
| 930 | /* rx skb timestamps */ | 937 | /* rx skb timestamps */ |
| 931 | extern void net_enable_timestamp(void); | 938 | extern void net_enable_timestamp(void); |
| 932 | extern void net_disable_timestamp(void); | 939 | extern void net_disable_timestamp(void); |
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 83010231db99..0a8ea8b35816 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
| @@ -1236,8 +1236,7 @@ extern unsigned int datagram_poll(struct file *file, struct socket *sock, | |||
| 1236 | extern int skb_copy_datagram_iovec(const struct sk_buff *from, | 1236 | extern int skb_copy_datagram_iovec(const struct sk_buff *from, |
| 1237 | int offset, struct iovec *to, | 1237 | int offset, struct iovec *to, |
| 1238 | int size); | 1238 | int size); |
| 1239 | extern int skb_copy_and_csum_datagram_iovec(const | 1239 | extern int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, |
| 1240 | struct sk_buff *skb, | ||
| 1241 | int hlen, | 1240 | int hlen, |
| 1242 | struct iovec *iov); | 1241 | struct iovec *iov); |
| 1243 | extern void skb_free_datagram(struct sock *sk, struct sk_buff *skb); | 1242 | extern void skb_free_datagram(struct sock *sk, struct sk_buff *skb); |
| @@ -1305,6 +1304,30 @@ static inline void skb_set_timestamp(struct sk_buff *skb, const struct timeval * | |||
| 1305 | 1304 | ||
| 1306 | extern void __net_timestamp(struct sk_buff *skb); | 1305 | extern void __net_timestamp(struct sk_buff *skb); |
| 1307 | 1306 | ||
| 1307 | extern unsigned int __skb_checksum_complete(struct sk_buff *skb); | ||
| 1308 | |||
| 1309 | /** | ||
| 1310 | * skb_checksum_complete - Calculate checksum of an entire packet | ||
| 1311 | * @skb: packet to process | ||
| 1312 | * | ||
| 1313 | * This function calculates the checksum over the entire packet plus | ||
| 1314 | * the value of skb->csum. The latter can be used to supply the | ||
| 1315 | * checksum of a pseudo header as used by TCP/UDP. It returns the | ||
| 1316 | * checksum. | ||
| 1317 | * | ||
| 1318 | * For protocols that contain complete checksums such as ICMP/TCP/UDP, | ||
| 1319 | * this function can be used to verify that checksum on received | ||
| 1320 | * packets. In that case the function should return zero if the | ||
| 1321 | * checksum is correct. In particular, this function will return zero | ||
| 1322 | * if skb->ip_summed is CHECKSUM_UNNECESSARY which indicates that the | ||
| 1323 | * hardware has already verified the correctness of the checksum. | ||
| 1324 | */ | ||
| 1325 | static inline unsigned int skb_checksum_complete(struct sk_buff *skb) | ||
| 1326 | { | ||
| 1327 | return skb->ip_summed != CHECKSUM_UNNECESSARY && | ||
| 1328 | __skb_checksum_complete(skb); | ||
| 1329 | } | ||
| 1330 | |||
| 1308 | #ifdef CONFIG_NETFILTER | 1331 | #ifdef CONFIG_NETFILTER |
| 1309 | static inline void nf_conntrack_put(struct nf_conntrack *nfct) | 1332 | static inline void nf_conntrack_put(struct nf_conntrack *nfct) |
| 1310 | { | 1333 | { |
diff --git a/include/net/tcp.h b/include/net/tcp.h index c24339c4e310..96cc3b434e40 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
| 28 | #include <linux/cache.h> | 28 | #include <linux/cache.h> |
| 29 | #include <linux/percpu.h> | 29 | #include <linux/percpu.h> |
| 30 | #include <linux/skbuff.h> | ||
| 30 | 31 | ||
| 31 | #include <net/inet_connection_sock.h> | 32 | #include <net/inet_connection_sock.h> |
| 32 | #include <net/inet_timewait_sock.h> | 33 | #include <net/inet_timewait_sock.h> |
| @@ -852,7 +853,7 @@ static __inline__ u16 tcp_v4_check(struct tcphdr *th, int len, | |||
| 852 | 853 | ||
| 853 | static __inline__ int __tcp_checksum_complete(struct sk_buff *skb) | 854 | static __inline__ int __tcp_checksum_complete(struct sk_buff *skb) |
| 854 | { | 855 | { |
| 855 | return (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)); | 856 | return __skb_checksum_complete(skb); |
| 856 | } | 857 | } |
| 857 | 858 | ||
| 858 | static __inline__ int tcp_checksum_complete(struct sk_buff *skb) | 859 | static __inline__ int tcp_checksum_complete(struct sk_buff *skb) |
diff --git a/net/core/datagram.c b/net/core/datagram.c index d219435d086c..1bcfef51ac58 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c | |||
| @@ -350,6 +350,20 @@ fault: | |||
| 350 | return -EFAULT; | 350 | return -EFAULT; |
| 351 | } | 351 | } |
| 352 | 352 | ||
| 353 | unsigned int __skb_checksum_complete(struct sk_buff *skb) | ||
| 354 | { | ||
| 355 | unsigned int sum; | ||
| 356 | |||
| 357 | sum = (u16)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)); | ||
| 358 | if (likely(!sum)) { | ||
| 359 | if (unlikely(skb->ip_summed == CHECKSUM_HW)) | ||
| 360 | netdev_rx_csum_fault(skb->dev); | ||
| 361 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
| 362 | } | ||
| 363 | return sum; | ||
| 364 | } | ||
| 365 | EXPORT_SYMBOL(__skb_checksum_complete); | ||
| 366 | |||
| 353 | /** | 367 | /** |
| 354 | * skb_copy_and_csum_datagram_iovec - Copy and checkum skb to user iovec. | 368 | * skb_copy_and_csum_datagram_iovec - Copy and checkum skb to user iovec. |
| 355 | * @skb: skbuff | 369 | * @skb: skbuff |
| @@ -363,7 +377,7 @@ fault: | |||
| 363 | * -EFAULT - fault during copy. Beware, in this case iovec | 377 | * -EFAULT - fault during copy. Beware, in this case iovec |
| 364 | * can be modified! | 378 | * can be modified! |
| 365 | */ | 379 | */ |
| 366 | int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, | 380 | int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, |
| 367 | int hlen, struct iovec *iov) | 381 | int hlen, struct iovec *iov) |
| 368 | { | 382 | { |
| 369 | unsigned int csum; | 383 | unsigned int csum; |
| @@ -376,8 +390,7 @@ int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, | |||
| 376 | iov++; | 390 | iov++; |
| 377 | 391 | ||
| 378 | if (iov->iov_len < chunk) { | 392 | if (iov->iov_len < chunk) { |
| 379 | if ((unsigned short)csum_fold(skb_checksum(skb, 0, chunk + hlen, | 393 | if (__skb_checksum_complete(skb)) |
| 380 | skb->csum))) | ||
| 381 | goto csum_error; | 394 | goto csum_error; |
| 382 | if (skb_copy_datagram_iovec(skb, hlen, iov, chunk)) | 395 | if (skb_copy_datagram_iovec(skb, hlen, iov, chunk)) |
| 383 | goto fault; | 396 | goto fault; |
| @@ -388,6 +401,8 @@ int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, | |||
| 388 | goto fault; | 401 | goto fault; |
| 389 | if ((unsigned short)csum_fold(csum)) | 402 | if ((unsigned short)csum_fold(csum)) |
| 390 | goto csum_error; | 403 | goto csum_error; |
| 404 | if (unlikely(skb->ip_summed == CHECKSUM_HW)) | ||
| 405 | netdev_rx_csum_fault(skb->dev); | ||
| 391 | iov->iov_len -= chunk; | 406 | iov->iov_len -= chunk; |
| 392 | iov->iov_base += chunk; | 407 | iov->iov_base += chunk; |
| 393 | } | 408 | } |
diff --git a/net/core/dev.c b/net/core/dev.c index 8d1541595277..0b48e294aafe 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
| @@ -1108,6 +1108,18 @@ out: | |||
| 1108 | return ret; | 1108 | return ret; |
| 1109 | } | 1109 | } |
| 1110 | 1110 | ||
| 1111 | /* Take action when hardware reception checksum errors are detected. */ | ||
| 1112 | #ifdef CONFIG_BUG | ||
| 1113 | void netdev_rx_csum_fault(struct net_device *dev) | ||
| 1114 | { | ||
| 1115 | if (net_ratelimit()) { | ||
| 1116 | printk(KERN_ERR "%s: hw csum failure.\n", dev->name); | ||
| 1117 | dump_stack(); | ||
| 1118 | } | ||
