Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netinet/in_rss.c
39475 views
1
/*-
2
* Copyright (c) 2010-2011 Juniper Networks, Inc.
3
* All rights reserved.
4
*
5
* This software was developed by Robert N. M. Watson under contract
6
* to Juniper Networks, Inc.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
31
#include "opt_inet6.h"
32
33
#include <sys/param.h>
34
#include <sys/mbuf.h>
35
#include <sys/socket.h>
36
#include <sys/priv.h>
37
#include <sys/kernel.h>
38
#include <sys/smp.h>
39
#include <sys/sysctl.h>
40
#include <sys/sbuf.h>
41
42
#include <net/if.h>
43
#include <net/if_var.h>
44
#include <net/netisr.h>
45
#include <net/rss_config.h>
46
47
#include <netinet/in.h>
48
#include <netinet/in_pcb.h>
49
#include <netinet/in_rss.h>
50
#include <netinet/in_var.h>
51
52
/* for software rss hash support */
53
#include <netinet/ip.h>
54
#include <netinet/tcp.h>
55
#include <netinet/udp.h>
56
57
/*
58
* Hash an IPv4 2-tuple.
59
*/
60
uint32_t
61
rss_hash_ip4_2tuple(struct in_addr src, struct in_addr dst)
62
{
63
uint8_t data[sizeof(src) + sizeof(dst)];
64
u_int datalen;
65
66
datalen = 0;
67
bcopy(&src, &data[datalen], sizeof(src));
68
datalen += sizeof(src);
69
bcopy(&dst, &data[datalen], sizeof(dst));
70
datalen += sizeof(dst);
71
return (rss_hash(datalen, data));
72
}
73
74
/*
75
* Hash an IPv4 4-tuple.
76
*/
77
uint32_t
78
rss_hash_ip4_4tuple(struct in_addr src, u_short srcport, struct in_addr dst,
79
u_short dstport)
80
{
81
uint8_t data[sizeof(src) + sizeof(dst) + sizeof(srcport) +
82
sizeof(dstport)];
83
u_int datalen;
84
85
datalen = 0;
86
bcopy(&src, &data[datalen], sizeof(src));
87
datalen += sizeof(src);
88
bcopy(&dst, &data[datalen], sizeof(dst));
89
datalen += sizeof(dst);
90
bcopy(&srcport, &data[datalen], sizeof(srcport));
91
datalen += sizeof(srcport);
92
bcopy(&dstport, &data[datalen], sizeof(dstport));
93
datalen += sizeof(dstport);
94
return (rss_hash(datalen, data));
95
}
96
97
/*
98
* Calculate an appropriate ipv4 2-tuple or 4-tuple given the given
99
* IPv4 source/destination address, UDP or TCP source/destination ports
100
* and the protocol type.
101
*
102
* The protocol code may wish to do a software hash of the given
103
* tuple. This depends upon the currently configured RSS hash types.
104
*
105
* This assumes that the packet in question isn't a fragment.
106
*
107
* It also assumes the packet source/destination address
108
* are in "incoming" packet order (ie, source is "far" address.)
109
*/
110
int
111
rss_proto_software_hash_v4(struct in_addr s, struct in_addr d,
112
u_short sp, u_short dp, int proto,
113
uint32_t *hashval, uint32_t *hashtype)
114
{
115
uint32_t hash;
116
117
/*
118
* Next, choose the hash type depending upon the protocol
119
* identifier.
120
*/
121
if ((proto == IPPROTO_TCP) &&
122
(rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4)) {
123
hash = rss_hash_ip4_4tuple(s, sp, d, dp);
124
*hashval = hash;
125
*hashtype = M_HASHTYPE_RSS_TCP_IPV4;
126
return (0);
127
} else if ((proto == IPPROTO_UDP) &&
128
(rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4)) {
129
hash = rss_hash_ip4_4tuple(s, sp, d, dp);
130
*hashval = hash;
131
*hashtype = M_HASHTYPE_RSS_UDP_IPV4;
132
return (0);
133
} else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) {
134
/* RSS doesn't hash on other protocols like SCTP; so 2-tuple */
135
hash = rss_hash_ip4_2tuple(s, d);
136
*hashval = hash;
137
*hashtype = M_HASHTYPE_RSS_IPV4;
138
return (0);
139
}
140
141
/* No configured available hashtypes! */
142
RSS_DEBUG("no available hashtypes!\n");
143
return (-1);
144
}
145
146
/*
147
* Calculate an appropriate ipv4 2-tuple or 4-tuple given the given
148
* IPv4 source/destination address, UDP or TCP source/destination ports
149
* and the protocol type.
150
*
151
* The protocol code may wish to do a software hash of the given
152
* tuple. This depends upon the currently configured RSS hash types.
153
*
154
* It assumes the packet source/destination address
155
* are in "outgoing" packet order (ie, destination is "far" address.)
156
*/
157
uint32_t
158
xps_proto_software_hash_v4(struct in_addr s, struct in_addr d,
159
u_short sp, u_short dp, int proto, uint32_t *hashtype)
160
{
161
uint32_t hash;
162
163
/*
164
* Next, choose the hash type depending upon the protocol
165
* identifier.
166
*/
167
if ((proto == IPPROTO_TCP) &&
168
(rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4)) {
169
hash = rss_hash_ip4_4tuple(d, dp, s, sp);
170
*hashtype = M_HASHTYPE_RSS_TCP_IPV4;
171
return (hash);
172
} else if ((proto == IPPROTO_UDP) &&
173
(rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4)) {
174
hash = rss_hash_ip4_4tuple(d, dp, s, sp);
175
*hashtype = M_HASHTYPE_RSS_UDP_IPV4;
176
return (hash);
177
} else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) {
178
/* RSS doesn't hash on other protocols like SCTP; so 2-tuple */
179
hash = rss_hash_ip4_2tuple(d, s);
180
*hashtype = M_HASHTYPE_RSS_IPV4;
181
return (hash);
182
}
183
184
*hashtype = M_HASHTYPE_NONE;
185
return (0);
186
}
187
188
/*
189
* Do a software calculation of the RSS for the given mbuf.
190
*
191
* This is typically used by the input path to recalculate the RSS after
192
* some form of packet processing (eg de-capsulation, IP fragment reassembly.)
193
*
194
* dir is the packet direction - RSS_HASH_PKT_INGRESS for incoming and
195
* RSS_HASH_PKT_EGRESS for outgoing.
196
*
197
* Returns 0 if a hash was done, -1 if no hash was done, +1 if
198
* the mbuf already had a valid RSS flowid.
199
*
200
* This function doesn't modify the mbuf. It's up to the caller to
201
* assign flowid/flowtype as appropriate.
202
*/
203
int
204
rss_mbuf_software_hash_v4(const struct mbuf *m, int dir, uint32_t *hashval,
205
uint32_t *hashtype)
206
{
207
const struct ip *ip;
208
const struct tcphdr *th;
209
const struct udphdr *uh;
210
uint32_t flowtype;
211
uint8_t proto;
212
int iphlen;
213
int is_frag = 0;
214
215
/*
216
* XXX For now this only handles hashing on incoming mbufs.
217
*/
218
if (dir != RSS_HASH_PKT_INGRESS) {
219
RSS_DEBUG("called on EGRESS packet!\n");
220
return (-1);
221
}
222
223
/*
224
* First, validate that the mbuf we have is long enough
225
* to have an IPv4 header in it.
226
*/
227
if (m->m_pkthdr.len < (sizeof(struct ip))) {
228
RSS_DEBUG("short mbuf pkthdr\n");
229
return (-1);
230
}
231
if (m->m_len < (sizeof(struct ip))) {
232
RSS_DEBUG("short mbuf len\n");
233
return (-1);
234
}
235
236
/* Ok, let's dereference that */
237
ip = mtod(m, struct ip *);
238
proto = ip->ip_p;
239
iphlen = ip->ip_hl << 2;
240
241
/*
242
* If this is a fragment then it shouldn't be four-tuple
243
* hashed just yet. Once it's reassembled into a full
244
* frame it should be re-hashed.
245
*/
246
if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
247
is_frag = 1;
248
249
/*
250
* If the mbuf flowid/flowtype matches the packet type,
251
* and we don't support the 4-tuple version of the given protocol,
252
* then signal to the owner that it can trust the flowid/flowtype
253
* details.
254
*
255
* This is a little picky - eg, if TCPv4 / UDPv4 hashing
256
* is supported but we got a TCP/UDP frame only 2-tuple hashed,
257
* then we shouldn't just "trust" the 2-tuple hash. We need
258
* a 4-tuple hash.
259
*/
260
flowtype = M_HASHTYPE_GET(m);
261
262
if (flowtype != M_HASHTYPE_NONE) {
263
switch (proto) {
264
case IPPROTO_UDP:
265
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4) &&
266
(flowtype == M_HASHTYPE_RSS_UDP_IPV4) &&
267
(is_frag == 0)) {
268
return (1);
269
}
270
/*
271
* Only allow 2-tuple for UDP frames if we don't also
272
* support 4-tuple for UDP.
273
*/
274
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) &&
275
((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4) == 0) &&
276
flowtype == M_HASHTYPE_RSS_IPV4) {
277
return (1);
278
}
279
break;
280
case IPPROTO_TCP:
281
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4) &&
282
(flowtype == M_HASHTYPE_RSS_TCP_IPV4) &&
283
(is_frag == 0)) {
284
return (1);
285
}
286
/*
287
* Only allow 2-tuple for TCP frames if we don't also
288
* support 4-tuple for TCP.
289
*/
290
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) &&
291
((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4) == 0) &&
292
flowtype == M_HASHTYPE_RSS_IPV4) {
293
return (1);
294
}
295
break;
296
default:
297
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) &&
298
flowtype == M_HASHTYPE_RSS_IPV4) {
299
return (1);
300
}
301
break;
302
}
303
}
304
305
/*
306
* Decode enough information to make a hash decision.
307
*
308
* XXX TODO: does the hardware hash on 4-tuple if IP
309
* options are present?
310
*/
311
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4) &&
312
(proto == IPPROTO_TCP) &&
313
(is_frag == 0)) {
314
if (m->m_len < iphlen + sizeof(struct tcphdr)) {
315
RSS_DEBUG("short TCP frame?\n");
316
return (-1);
317
}
318
th = (const struct tcphdr *)((c_caddr_t)ip + iphlen);
319
return rss_proto_software_hash_v4(ip->ip_src, ip->ip_dst,
320
th->th_sport,
321
th->th_dport,
322
proto,
323
hashval,
324
hashtype);
325
} else if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4) &&
326
(proto == IPPROTO_UDP) &&
327
(is_frag == 0)) {
328
uh = (const struct udphdr *)((c_caddr_t)ip + iphlen);
329
if (m->m_len < iphlen + sizeof(struct udphdr)) {
330
RSS_DEBUG("short UDP frame?\n");
331
return (-1);
332
}
333
return rss_proto_software_hash_v4(ip->ip_src, ip->ip_dst,
334
uh->uh_sport,
335
uh->uh_dport,
336
proto,
337
hashval,
338
hashtype);
339
} else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) {
340
/* Default to 2-tuple hash */
341
return rss_proto_software_hash_v4(ip->ip_src, ip->ip_dst,
342
0, /* source port */
343
0, /* destination port */
344
0, /* IPPROTO_IP */
345
hashval,
346
hashtype);
347
} else {
348
RSS_DEBUG("no available hashtypes!\n");
349
return (-1);
350
}
351
}
352
353
/*
354
* Similar to rss_m2cpuid, but designed to be used by the IP NETISR
355
* on incoming frames.
356
*
357
* If an existing RSS hash exists and it matches what the configured
358
* hashing is, then use it.
359
*
360
* If there's an existing RSS hash but the desired hash is different,
361
* or if there's no useful RSS hash, then calculate it via
362
* the software path.
363
*
364
* XXX TODO: definitely want statistics here!
365
*/
366
struct mbuf *
367
rss_soft_m2cpuid_v4(struct mbuf *m, uintptr_t source, u_int *cpuid)
368
{
369
uint32_t hash_val, hash_type;
370
int ret;
371
372
M_ASSERTPKTHDR(m);
373
374
ret = rss_mbuf_software_hash_v4(m, RSS_HASH_PKT_INGRESS,
375
&hash_val, &hash_type);
376
if (ret > 0) {
377
/* mbuf has a valid hash already; don't need to modify it */
378
*cpuid = rss_hash2cpuid(m->m_pkthdr.flowid, M_HASHTYPE_GET(m));
379
} else if (ret == 0) {
380
/* hash was done; update */
381
m->m_pkthdr.flowid = hash_val;
382
M_HASHTYPE_SET(m, hash_type);
383
*cpuid = rss_hash2cpuid(m->m_pkthdr.flowid, M_HASHTYPE_GET(m));
384
} else { /* ret < 0 */
385
/* no hash was done */
386
*cpuid = NETISR_CPUID_NONE;
387
}
388
return (m);
389
}
390
391