Re: [PATCH] MIPS checksum fix
From: Atsushi Nemoto <hidden>
Date: 2008-09-29 15:29:01
Also in:
linux-mips
Subsystem:
mips, the rest · Maintainers:
Thomas Bogendoerfer, Linus Torvalds
On Tue, 23 Sep 2008 14:52:24 -0700, Bryan Phillippe [off-list ref] wrote:
I tried this patch (with and without Atsushi's addition, shown below) on a MIPS64 today and the checksums were all bad (i.e. worse than the original problem). Note that I had to manually create the diff, because of "malformed patch" errors at line 21 (second hunk). If anyone would like to send me an updated unified diff for this issue, I can re-test today within the next day.
I suppose your problem is still not fixed, right? If so, could you try this patch? With this patch, checksums is always compared with a result of "reference" implementation. If any mismatch was found, please report the log. arch/mips/lib/Makefile | 1 + arch/mips/lib/csum_partial.S | 4 +- arch/mips/lib/csum_partial_test.c | 109 +++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index 8810dfb..cb56e19 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile@@ -29,3 +29,4 @@ obj-$(CONFIG_CPU_VR41XX) += dump_tlb.o # libgcc-style stuff needed in the kernel obj-y += ashldi3.o ashrdi3.o cmpdi2.o lshrdi3.o ucmpdi2.o +obj-y += csum_partial_test.o
diff --git a/arch/mips/lib/csum_partial.S b/arch/mips/lib/csum_partial.S
index edac989..22e9767 100644
--- a/arch/mips/lib/csum_partial.S
+++ b/arch/mips/lib/csum_partial.S@@ -101,7 +101,7 @@ .text .set noreorder .align 5 -LEAF(csum_partial) +LEAF(asm_csum_partial) move sum, zero move t7, zero
@@ -296,7 +296,7 @@ LEAF(csum_partial) ADDC32(sum, a2) jr ra .set noreorder - END(csum_partial) + END(asm_csum_partial) /*
diff --git a/arch/mips/lib/csum_partial_test.c b/arch/mips/lib/csum_partial_test.c
new file mode 100644
index 0000000..4a4887e
--- /dev/null
+++ b/arch/mips/lib/csum_partial_test.c@@ -0,0 +1,109 @@ +#include <net/checksum.h> + +static inline __sum16 ref_csum_fold(__wsum csum) +{ + u32 sum = (__force u32)csum; + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return (__force __sum16)~sum; +} + +static inline unsigned short from32to16(unsigned int x) +{ + /* add up 16-bit and 16-bit for 16+c bit */ + x = (x & 0xffff) + (x >> 16); + /* add up carry.. */ + x = (x & 0xffff) + (x >> 16); + return x; +} + +static unsigned int do_csum(const unsigned char * buff, int len) +{ + int odd, count; + unsigned int result = 0; + + if (len <= 0) + goto out; + odd = 1 & (unsigned long) buff; + if (odd) { +#ifdef __BIG_ENDIAN + result = *buff; +#else + result = *buff << 8; +#endif + len--; + buff++; + } + count = len >> 1; /* nr of 16-bit words.. */ + if (count) { + if (2 & (unsigned long) buff) { + result += *(unsigned short *) buff; + count--; + len -= 2; + buff += 2; + } + count >>= 1; /* nr of 32-bit words.. */ + if (count) { + unsigned int carry = 0; + do { + unsigned int w = *(unsigned int *) buff; + count--; + buff += 4; + result += carry; + result += w; + carry = (w > result); + } while (count); + result += carry; + result = (result & 0xffff) + (result >> 16); + } + if (len & 2) { + result += *(unsigned short *) buff; + buff += 2; + } + } + if (len & 1) { +#ifdef __BIG_ENDIAN + result += (*buff << 8); +#else + result += *buff; +#endif + } + result = from32to16(result); + if (odd) + result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); +out: + return result; +} + +static __wsum ref_csum_partial(const void *buff, int len, __wsum sum) +{ + unsigned int result = do_csum(buff, len); + + /* add in old sum, and carry.. */ + result += (__force u32)sum; + if ((__force u32)sum > result) + result += 1; + return (__force __wsum)result; +} + +__wsum asm_csum_partial(const void *buff, int len, __wsum sum); + +__wsum csum_partial(const void *buff, int len, __wsum sum) +{ + __wsum ref_wsum = ref_csum_partial(buff, len, sum); + __sum16 ref_sum = ref_csum_fold(ref_wsum); + __wsum cal_wsum = asm_csum_partial(buff, len, sum); + __sum16 cal_sum = csum_fold(cal_wsum); + if (ref_sum != cal_sum) { + int i; + printk("csum_partial error." + " %#04x(%#08x) != %#04x(%#08x)\n", + ref_sum, ref_wsum, cal_sum, cal_wsum); + printk("len %#04x, sum %#08x\n", len, sum); + printk("buf"); + for (i = 0; i < len; i++) + printk(" %#02x", *((const u8 *)buff + i)); + printk("\n"); + } + return ref_wsum; +}