]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/i386/in_cksum_machdep.c
Merge bmake-20230909
[FreeBSD/FreeBSD.git] / sys / i386 / i386 / in_cksum_machdep.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1990 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  *      from tahoe:     in_cksum.c      1.2     86/01/05
32  *      from:           @(#)in_cksum.c  1.3 (Berkeley) 1/19/91
33  */
34
35 #include <sys/cdefs.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/mbuf.h>
39
40 #include <netinet/in.h>
41 #include <netinet/in_systm.h>
42 #include <netinet/ip.h>
43
44 #include <machine/in_cksum.h>
45
46 /*
47  * Checksum routine for Internet Protocol family headers.
48  *
49  * This routine is very heavily used in the network
50  * code and should be modified for each CPU to be as fast as possible.
51  *
52  * This implementation is 386 version.
53  */
54
55 #undef  ADDCARRY
56 #define ADDCARRY(x)     if ((x) > 0xffff) (x) -= 0xffff
57 #define REDUCE          {sum = (sum & 0xffff) + (sum >> 16); ADDCARRY(sum);}
58
59 /*
60  * These asm statements require __volatile because they pass information
61  * via the condition codes.  GCC does not currently provide a way to specify
62  * the condition codes as an input or output operand.
63  *
64  * The LOAD macro below is effectively a prefetch into cache.  GCC will
65  * load the value into a register but will not use it.  Since modern CPUs
66  * reorder operations, this will generally take place in parallel with
67  * other calculations.
68  */
69 u_short
70 in_cksum_skip(struct mbuf *m, int len, int skip)
71 {
72         u_short *w;
73         unsigned sum = 0;
74         int mlen = 0;
75         int byte_swapped = 0;
76         union { char    c[2]; u_short   s; } su;
77
78         len -= skip;
79         for (; skip && m; m = m->m_next) {
80                 if (m->m_len > skip) {
81                         mlen = m->m_len - skip;
82                         w = (u_short *)(mtod(m, u_char *) + skip);
83                         goto skip_start;
84                 } else {
85                         skip -= m->m_len;
86                 }
87         }
88
89         for (;m && len; m = m->m_next) {
90                 if (m->m_len == 0)
91                         continue;
92                 w = mtod(m, u_short *);
93                 if (mlen == -1) {
94                         /*
95                          * The first byte of this mbuf is the continuation
96                          * of a word spanning between this mbuf and the
97                          * last mbuf.
98                          */
99
100                         /* su.c[0] is already saved when scanning previous
101                          * mbuf.  sum was REDUCEd when we found mlen == -1
102                          */
103                         su.c[1] = *(u_char *)w;
104                         sum += su.s;
105                         w = (u_short *)((char *)w + 1);
106                         mlen = m->m_len - 1;
107                         len--;
108                 } else
109                         mlen = m->m_len;
110 skip_start:
111                 if (len < mlen)
112                         mlen = len;
113                 len -= mlen;
114                 /*
115                  * Force to long boundary so we do longword aligned
116                  * memory operations
117                  */
118                 if (3 & (int) w) {
119                         REDUCE;
120                         if ((1 & (int) w) && (mlen > 0)) {
121                                 sum <<= 8;
122                                 su.c[0] = *(char *)w;
123                                 w = (u_short *)((char *)w + 1);
124                                 mlen--;
125                                 byte_swapped = 1;
126                         }
127                         if ((2 & (int) w) && (mlen >= 2)) {
128                                 sum += *w++;
129                                 mlen -= 2;
130                         }
131                 }
132                 /*
133                  * Advance to a 486 cache line boundary.
134                  */
135                 if (4 & (int) w && mlen >= 4) {
136                         __asm __volatile (
137                                 "addl %1, %0\n"
138                                 "adcl $0, %0"
139                                 : "+r" (sum)
140                                 : "g" (((const u_int32_t *)w)[0])
141                         );
142                         w += 2;
143                         mlen -= 4;
144                 }
145                 if (8 & (int) w && mlen >= 8) {
146                         __asm __volatile (
147                                 "addl %1, %0\n"
148                                 "adcl %2, %0\n"
149                                 "adcl $0, %0"
150                                 : "+r" (sum)
151                                 : "g" (((const u_int32_t *)w)[0]),
152                                   "g" (((const u_int32_t *)w)[1])
153                         );
154                         w += 4;
155                         mlen -= 8;
156                 }
157                 /*
158                  * Do as much of the checksum as possible 32 bits at at time.
159                  * In fact, this loop is unrolled to make overhead from
160                  * branches &c small.
161                  */
162                 mlen -= 1;
163                 while ((mlen -= 32) >= 0) {
164                         /*
165                          * Add with carry 16 words and fold in the last
166                          * carry by adding a 0 with carry.
167                          *
168                          * The early ADD(16) and the LOAD(32) are to load
169                          * the next 2 cache lines in advance on 486's.  The
170                          * 486 has a penalty of 2 clock cycles for loading
171                          * a cache line, plus whatever time the external
172                          * memory takes to load the first word(s) addressed.
173                          * These penalties are unavoidable.  Subsequent
174                          * accesses to a cache line being loaded (and to
175                          * other external memory?) are delayed until the
176                          * whole load finishes.  These penalties are mostly
177                          * avoided by not accessing external memory for
178                          * 8 cycles after the ADD(16) and 12 cycles after
179                          * the LOAD(32).  The loop terminates when mlen
180                          * is initially 33 (not 32) to guaranteed that
181                          * the LOAD(32) is within bounds.
182                          */
183                         __asm __volatile (
184                                 "addl %1, %0\n"
185                                 "adcl %2, %0\n"
186                                 "adcl %3, %0\n"
187                                 "adcl %4, %0\n"
188                                 "adcl %5, %0\n"
189                                 "mov  %6, %%eax\n"
190                                 "adcl %7, %0\n"
191                                 "adcl %8, %0\n"
192                                 "adcl %9, %0\n"
193                                 "adcl $0, %0"
194                                 : "+r" (sum)
195                                 : "g" (((const u_int32_t *)w)[4]),
196                                   "g" (((const u_int32_t *)w)[0]),
197                                   "g" (((const u_int32_t *)w)[1]),
198                                   "g" (((const u_int32_t *)w)[2]),
199                                   "g" (((const u_int32_t *)w)[3]),
200                                   "g" (((const u_int32_t *)w)[8]),
201                                   "g" (((const u_int32_t *)w)[5]),
202                                   "g" (((const u_int32_t *)w)[6]),
203                                   "g" (((const u_int32_t *)w)[7])
204                                 : "eax"
205                         );
206                         w += 16;
207                 }
208                 mlen += 32 + 1;
209                 if (mlen >= 32) {
210                         __asm __volatile (
211                                 "addl %1, %0\n"
212                                 "adcl %2, %0\n"
213                                 "adcl %3, %0\n"
214                                 "adcl %4, %0\n"
215                                 "adcl %5, %0\n"
216                                 "adcl %6, %0\n"
217                                 "adcl %7, %0\n"
218                                 "adcl %8, %0\n"
219                                 "adcl $0, %0"
220                                 : "+r" (sum)
221                                 : "g" (((const u_int32_t *)w)[4]),
222                                   "g" (((const u_int32_t *)w)[0]),
223                                   "g" (((const u_int32_t *)w)[1]),
224                                   "g" (((const u_int32_t *)w)[2]),
225                                   "g" (((const u_int32_t *)w)[3]),
226                                   "g" (((const u_int32_t *)w)[5]),
227                                   "g" (((const u_int32_t *)w)[6]),
228                                   "g" (((const u_int32_t *)w)[7])
229                         );
230                         w += 16;
231                         mlen -= 32;
232                 }
233                 if (mlen >= 16) {
234                         __asm __volatile (
235                                 "addl %1, %0\n"
236                                 "adcl %2, %0\n"
237                                 "adcl %3, %0\n"
238                                 "adcl %4, %0\n"
239                                 "adcl $0, %0"
240                                 : "+r" (sum)
241                                 : "g" (((const u_int32_t *)w)[0]),
242                                   "g" (((const u_int32_t *)w)[1]),
243                                   "g" (((const u_int32_t *)w)[2]),
244                                   "g" (((const u_int32_t *)w)[3])
245                         );
246                         w += 8;
247                         mlen -= 16;
248                 }
249                 if (mlen >= 8) {
250                         __asm __volatile (
251                                 "addl %1, %0\n"
252                                 "adcl %2, %0\n"
253                                 "adcl $0, %0"
254                                 : "+r" (sum)
255                                 : "g" (((const u_int32_t *)w)[0]),
256                                   "g" (((const u_int32_t *)w)[1])
257                         );
258                         w += 4;
259                         mlen -= 8;
260                 }
261                 if (mlen == 0 && byte_swapped == 0)
262                         continue;       /* worth 1% maybe ?? */
263                 REDUCE;
264                 while ((mlen -= 2) >= 0) {
265                         sum += *w++;
266                 }
267                 if (byte_swapped) {
268                         sum <<= 8;
269                         byte_swapped = 0;
270                         if (mlen == -1) {
271                                 su.c[1] = *(char *)w;
272                                 sum += su.s;
273                                 mlen = 0;
274                         } else
275                                 mlen = -1;
276                 } else if (mlen == -1)
277                         /*
278                          * This mbuf has odd number of bytes.
279                          * There could be a word split between
280                          * this mbuf and the next mbuf.
281                          * Save the last byte (to prepend to next mbuf).
282                          */
283                         su.c[0] = *(char *)w;
284         }
285
286         if (len)
287                 printf("%s: out of data by %d\n", __func__, len);
288         if (mlen == -1) {
289                 /* The last mbuf has odd # of bytes. Follow the
290                    standard (the odd byte is shifted left by 8 bits) */
291                 su.c[1] = 0;
292                 sum += su.s;
293         }
294         REDUCE;
295         return (~sum & 0xffff);
296 }