diff options
Diffstat (limited to 'arch/alpha/lib/checksum.c')
-rw-r--r-- | arch/alpha/lib/checksum.c | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/arch/alpha/lib/checksum.c b/arch/alpha/lib/checksum.c new file mode 100644 index 000000000000..89044e6385fe --- /dev/null +++ b/arch/alpha/lib/checksum.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/checksum.c | ||
3 | * | ||
4 | * This file contains network checksum routines that are better done | ||
5 | * in an architecture-specific manner due to speed.. | ||
6 | * Comments in other versions indicate that the algorithms are from RFC1071 | ||
7 | * | ||
8 | * accellerated versions (and 21264 assembly versions ) contributed by | ||
9 | * Rick Gorton <rick.gorton@alpha-processor.com> | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/string.h> | ||
14 | |||
15 | #include <asm/byteorder.h> | ||
16 | |||
17 | static inline unsigned short from64to16(unsigned long x) | ||
18 | { | ||
19 | /* Using extract instructions is a bit more efficient | ||
20 | than the original shift/bitmask version. */ | ||
21 | |||
22 | union { | ||
23 | unsigned long ul; | ||
24 | unsigned int ui[2]; | ||
25 | unsigned short us[4]; | ||
26 | } in_v, tmp_v, out_v; | ||
27 | |||
28 | in_v.ul = x; | ||
29 | tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1]; | ||
30 | |||
31 | /* Since the bits of tmp_v.sh[3] are going to always be zero, | ||
32 | we don't have to bother to add that in. */ | ||
33 | out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1] | ||
34 | + (unsigned long) tmp_v.us[2]; | ||
35 | |||
36 | /* Similarly, out_v.us[2] is always zero for the final add. */ | ||
37 | return out_v.us[0] + out_v.us[1]; | ||
38 | } | ||
39 | |||
40 | /* | ||
41 | * computes the checksum of the TCP/UDP pseudo-header | ||
42 | * returns a 16-bit checksum, already complemented. | ||
43 | */ | ||
44 | unsigned short int csum_tcpudp_magic(unsigned long saddr, | ||
45 | unsigned long daddr, | ||
46 | unsigned short len, | ||
47 | unsigned short proto, | ||
48 | unsigned int sum) | ||
49 | { | ||
50 | return ~from64to16(saddr + daddr + sum + | ||
51 | ((unsigned long) ntohs(len) << 16) + | ||
52 | ((unsigned long) proto << 8)); | ||
53 | } | ||
54 | |||
55 | unsigned int csum_tcpudp_nofold(unsigned long saddr, | ||
56 | unsigned long daddr, | ||
57 | unsigned short len, | ||
58 | unsigned short proto, | ||
59 | unsigned int sum) | ||
60 | { | ||
61 | unsigned long result; | ||
62 | |||
63 | result = (saddr + daddr + sum + | ||
64 | ((unsigned long) ntohs(len) << 16) + | ||
65 | ((unsigned long) proto << 8)); | ||
66 | |||
67 | /* Fold down to 32-bits so we don't lose in the typedef-less | ||
68 | network stack. */ | ||
69 | /* 64 to 33 */ | ||
70 | result = (result & 0xffffffff) + (result >> 32); | ||
71 | /* 33 to 32 */ | ||
72 | result = (result & 0xffffffff) + (result >> 32); | ||
73 | return result; | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * Do a 64-bit checksum on an arbitrary memory area.. | ||
78 | * | ||
79 | * This isn't a great routine, but it's not _horrible_ either. The | ||
80 | * inner loop could be unrolled a bit further, and there are better | ||
81 | * ways to do the carry, but this is reasonable. | ||
82 | */ | ||
83 | static inline unsigned long do_csum(const unsigned char * buff, int len) | ||
84 | { | ||
85 | int odd, count; | ||
86 | unsigned long result = 0; | ||
87 | |||
88 | if (len <= 0) | ||
89 | goto out; | ||
90 | odd = 1 & (unsigned long) buff; | ||
91 | if (odd) { | ||
92 | result = *buff << 8; | ||
93 | len--; | ||
94 | buff++; | ||
95 | } | ||
96 | count = len >> 1; /* nr of 16-bit words.. */ | ||
97 | if (count) { | ||
98 | if (2 & (unsigned long) buff) { | ||
99 | result += *(unsigned short *) buff; | ||
100 | count--; | ||
101 | len -= 2; | ||
102 | buff += 2; | ||
103 | } | ||
104 | count >>= 1; /* nr of 32-bit words.. */ | ||
105 | if (count) { | ||
106 | if (4 & (unsigned long) buff) { | ||
107 | result += *(unsigned int *) buff; | ||
108 | count--; | ||
109 | len -= 4; | ||
110 | buff += 4; | ||
111 | } | ||
112 | count >>= 1; /* nr of 64-bit words.. */ | ||
113 | if (count) { | ||
114 | unsigned long carry = 0; | ||
115 | do { | ||
116 | unsigned long w = *(unsigned long *) buff; | ||
117 | count--; | ||
118 | buff += 8; | ||
119 | result += carry; | ||
120 | result += w; | ||
121 | carry = (w > result); | ||
122 | } while (count); | ||
123 | result += carry; | ||
124 | result = (result & 0xffffffff) + (result >> 32); | ||
125 | } | ||
126 | if (len & 4) { | ||
127 | result += *(unsigned int *) buff; | ||
128 | buff += 4; | ||
129 | } | ||
130 | } | ||
131 | if (len & 2) { | ||
132 | result += *(unsigned short *) buff; | ||
133 | buff += 2; | ||
134 | } | ||
135 | } | ||
136 | if (len & 1) | ||
137 | result += *buff; | ||
138 | result = from64to16(result); | ||
139 | if (odd) | ||
140 | result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); | ||
141 | out: | ||
142 | return result; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * This is a version of ip_compute_csum() optimized for IP headers, | ||
147 | * which always checksum on 4 octet boundaries. | ||
148 | */ | ||
149 | unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) | ||
150 | { | ||
151 | return ~do_csum(iph,ihl*4); | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * computes the checksum of a memory block at buff, length len, | ||
156 | * and adds in "sum" (32-bit) | ||
157 | * | ||
158 | * returns a 32-bit number suitable for feeding into itself | ||
159 | * or csum_tcpudp_magic | ||
160 | * | ||
161 | * this function must be called with even lengths, except | ||
162 | * for the last fragment, which may be odd | ||
163 | * | ||
164 | * it's best to have buff aligned on a 32-bit boundary | ||
165 | */ | ||
166 | unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) | ||
167 | { | ||
168 | unsigned long result = do_csum(buff, len); | ||
169 | |||
170 | /* add in old sum, and carry.. */ | ||
171 | result += sum; | ||
172 | /* 32+c bits -> 32 bits */ | ||
173 | result = (result & 0xffffffff) + (result >> 32); | ||
174 | return result; | ||
175 | } | ||
176 | |||
177 | EXPORT_SYMBOL(csum_partial); | ||
178 | |||
179 | /* | ||
180 | * this routine is used for miscellaneous IP-like checksums, mainly | ||
181 | * in icmp.c | ||
182 | */ | ||
183 | unsigned short ip_compute_csum(unsigned char * buff, int len) | ||
184 | { | ||
185 | return ~from64to16(do_csum(buff,len)); | ||
186 | } | ||