Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb286bb2 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller
Browse files

[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: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1064e944
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -927,6 +927,13 @@ extern int netdev_max_backlog;
extern int		weight_p;
extern int		netdev_set_master(struct net_device *dev, struct net_device *master);
extern int skb_checksum_help(struct sk_buff *skb, int inward);
#ifdef CONFIG_BUG
extern void netdev_rx_csum_fault(struct net_device *dev);
#else
static inline void netdev_rx_csum_fault(struct net_device *dev)
{
}
#endif
/* rx skb timestamps */
extern void		net_enable_timestamp(void);
extern void		net_disable_timestamp(void);
+25 −2
Original line number Diff line number Diff line
@@ -1236,8 +1236,7 @@ extern unsigned int datagram_poll(struct file *file, struct socket *sock,
extern int	       skb_copy_datagram_iovec(const struct sk_buff *from,
					       int offset, struct iovec *to,
					       int size);
extern int	       skb_copy_and_csum_datagram_iovec(const
							struct sk_buff *skb,
extern int	       skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
							int hlen,
							struct iovec *iov);
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 *

extern void __net_timestamp(struct sk_buff *skb);

extern unsigned int __skb_checksum_complete(struct sk_buff *skb);

/**
 *	skb_checksum_complete - Calculate checksum of an entire packet
 *	@skb: packet to process
 *
 *	This function calculates the checksum over the entire packet plus
 *	the value of skb->csum.  The latter can be used to supply the
 *	checksum of a pseudo header as used by TCP/UDP.  It returns the
 *	checksum.
 *
 *	For protocols that contain complete checksums such as ICMP/TCP/UDP,
 *	this function can be used to verify that checksum on received
 *	packets.  In that case the function should return zero if the
 *	checksum is correct.  In particular, this function will return zero
 *	if skb->ip_summed is CHECKSUM_UNNECESSARY which indicates that the
 *	hardware has already verified the correctness of the checksum.
 */
static inline unsigned int skb_checksum_complete(struct sk_buff *skb)
{
	return skb->ip_summed != CHECKSUM_UNNECESSARY &&
		__skb_checksum_complete(skb);
}

#ifdef CONFIG_NETFILTER
static inline void nf_conntrack_put(struct nf_conntrack *nfct)
{
+2 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/cache.h>
#include <linux/percpu.h>
#include <linux/skbuff.h>

#include <net/inet_connection_sock.h>
#include <net/inet_timewait_sock.h>
@@ -852,7 +853,7 @@ static __inline__ u16 tcp_v4_check(struct tcphdr *th, int len,

static __inline__ int __tcp_checksum_complete(struct sk_buff *skb)
{
	return (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
	return __skb_checksum_complete(skb);
}

static __inline__ int tcp_checksum_complete(struct sk_buff *skb)
+18 −3
Original line number Diff line number Diff line
@@ -350,6 +350,20 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
	return -EFAULT;
}

unsigned int __skb_checksum_complete(struct sk_buff *skb)
{
	unsigned int sum;

	sum = (u16)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
	if (likely(!sum)) {
		if (unlikely(skb->ip_summed == CHECKSUM_HW))
			netdev_rx_csum_fault(skb->dev);
		skb->ip_summed = CHECKSUM_UNNECESSARY;
	}
	return sum;
}
EXPORT_SYMBOL(__skb_checksum_complete);

/**
 *	skb_copy_and_csum_datagram_iovec - Copy and checkum skb to user iovec.
 *	@skb: skbuff
@@ -363,7 +377,7 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
 *		 -EFAULT - fault during copy. Beware, in this case iovec
 *			   can be modified!
 */
int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb,
int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
				     int hlen, struct iovec *iov)
{
	unsigned int csum;
@@ -376,8 +390,7 @@ int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb,
		iov++;

	if (iov->iov_len < chunk) {
		if ((unsigned short)csum_fold(skb_checksum(skb, 0, chunk + hlen,
							   skb->csum)))
		if (__skb_checksum_complete(skb))
			goto csum_error;
		if (skb_copy_datagram_iovec(skb, hlen, iov, chunk))
			goto fault;
@@ -388,6 +401,8 @@ int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb,
			goto fault;
		if ((unsigned short)csum_fold(csum))
			goto csum_error;
		if (unlikely(skb->ip_summed == CHECKSUM_HW))
			netdev_rx_csum_fault(skb->dev);
		iov->iov_len -= chunk;
		iov->iov_base += chunk;
	}
+12 −0
Original line number Diff line number Diff line
@@ -1108,6 +1108,18 @@ int skb_checksum_help(struct sk_buff *skb, int inward)
	return ret;
}

/* Take action when hardware reception checksum errors are detected. */
#ifdef CONFIG_BUG
void netdev_rx_csum_fault(struct net_device *dev)
{
	if (net_ratelimit()) {
		printk(KERN_ERR "%s: hw csum failure.\n", dev->name);
		dump_stack();
	}
}
EXPORT_SYMBOL(netdev_rx_csum_fault);
#endif

#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.
Loading