Skip to content

Commit 51b8f81

Browse files
edumazetdavem330
authored andcommitted
ipv6: exthdrs: get rid of indirect calls in ip6_parse_tlv()
As presented last month in our "BIG TCP" talk at netdev 0x15, we plan using IPv6 jumbograms. One of the minor problem we talked about is the fact that ip6_parse_tlv() is currently using tables to list known tlvs, thus using potentially expensive indirect calls. While we could mitigate this cost using macros from indirect_call_wrapper.h, we also can get rid of the tables and let the compiler emit optimized code. Signed-off-by: Eric Dumazet <edumazet@google.com> Cc: Justin Iurman <justin.iurman@uliege.be> Cc: Coco Li <lixiaoyan@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent d851798 commit 51b8f81

File tree

1 file changed

+46
-59
lines changed

1 file changed

+46
-59
lines changed

net/ipv6/exthdrs.c

Lines changed: 46 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,6 @@
5555

5656
#include <linux/uaccess.h>
5757

58-
/*
59-
* Parsing tlv encoded headers.
60-
*
61-
* Parsing function "func" returns true, if parsing succeed
62-
* and false, if it failed.
63-
* It MUST NOT touch skb->h.
64-
*/
65-
66-
struct tlvtype_proc {
67-
int type;
68-
bool (*func)(struct sk_buff *skb, int offset);
69-
};
70-
7158
/*********************
7259
Generic functions
7360
*********************/
@@ -112,16 +99,23 @@ static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff,
11299
return false;
113100
}
114101

102+
static bool ipv6_hop_ra(struct sk_buff *skb, int optoff);
103+
static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff);
104+
static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff);
105+
static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff);
106+
#if IS_ENABLED(CONFIG_IPV6_MIP6)
107+
static bool ipv6_dest_hao(struct sk_buff *skb, int optoff);
108+
#endif
109+
115110
/* Parse tlv encoded option header (hop-by-hop or destination) */
116111

117-
static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
112+
static bool ip6_parse_tlv(bool hopbyhop,
118113
struct sk_buff *skb,
119114
int max_count)
120115
{
121116
int len = (skb_transport_header(skb)[1] + 1) << 3;
122117
const unsigned char *nh = skb_network_header(skb);
123118
int off = skb_network_header_len(skb);
124-
const struct tlvtype_proc *curr;
125119
bool disallow_unknowns = false;
126120
int tlv_count = 0;
127121
int padlen = 0;
@@ -176,20 +170,45 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
176170
if (tlv_count > max_count)
177171
goto bad;
178172

179-
for (curr = procs; curr->type >= 0; curr++) {
180-
if (curr->type == nh[off]) {
181-
/* type specific length/alignment
182-
checks will be performed in the
183-
func(). */
184-
if (curr->func(skb, off) == false)
173+
if (hopbyhop) {
174+
switch (nh[off]) {
175+
case IPV6_TLV_ROUTERALERT:
176+
if (!ipv6_hop_ra(skb, off))
177+
return false;
178+
break;
179+
case IPV6_TLV_IOAM:
180+
if (!ipv6_hop_ioam(skb, off))
181+
return false;
182+
break;
183+
case IPV6_TLV_JUMBO:
184+
if (!ipv6_hop_jumbo(skb, off))
185+
return false;
186+
break;
187+
case IPV6_TLV_CALIPSO:
188+
if (!ipv6_hop_calipso(skb, off))
189+
return false;
190+
break;
191+
default:
192+
if (!ip6_tlvopt_unknown(skb, off,
193+
disallow_unknowns))
194+
return false;
195+
break;
196+
}
197+
} else {
198+
switch (nh[off]) {
199+
#if IS_ENABLED(CONFIG_IPV6_MIP6)
200+
case IPV6_TLV_HAO:
201+
if (!ipv6_dest_hao(skb, off))
202+
return false;
203+
break;
204+
#endif
205+
default:
206+
if (!ip6_tlvopt_unknown(skb, off,
207+
disallow_unknowns))
185208
return false;
186209
break;
187210
}
188211
}
189-
if (curr->type < 0 &&
190-
!ip6_tlvopt_unknown(skb, off, disallow_unknowns))
191-
return false;
192-
193212
padlen = 0;
194213
}
195214
off += optlen;
@@ -267,16 +286,6 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
267286
}
268287
#endif
269288

270-
static const struct tlvtype_proc tlvprocdestopt_lst[] = {
271-
#if IS_ENABLED(CONFIG_IPV6_MIP6)
272-
{
273-
.type = IPV6_TLV_HAO,
274-
.func = ipv6_dest_hao,
275-
},
276-
#endif
277-
{-1, NULL}
278-
};
279-
280289
static int ipv6_destopt_rcv(struct sk_buff *skb)
281290
{
282291
struct inet6_dev *idev = __in6_dev_get(skb->dev);
@@ -307,8 +316,7 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)
307316
dstbuf = opt->dst1;
308317
#endif
309318

310-
if (ip6_parse_tlv(tlvprocdestopt_lst, skb,
311-
net->ipv6.sysctl.max_dst_opts_cnt)) {
319+
if (ip6_parse_tlv(false, skb, net->ipv6.sysctl.max_dst_opts_cnt)) {
312320
skb->transport_header += extlen;
313321
opt = IP6CB(skb);
314322
#if IS_ENABLED(CONFIG_IPV6_MIP6)
@@ -1051,26 +1059,6 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
10511059
return false;
10521060
}
10531061

1054-
static const struct tlvtype_proc tlvprochopopt_lst[] = {
1055-
{
1056-
.type = IPV6_TLV_ROUTERALERT,
1057-
.func = ipv6_hop_ra,
1058-
},
1059-
{
1060-
.type = IPV6_TLV_IOAM,
1061-
.func = ipv6_hop_ioam,
1062-
},
1063-
{
1064-
.type = IPV6_TLV_JUMBO,
1065-
.func = ipv6_hop_jumbo,
1066-
},
1067-
{
1068-
.type = IPV6_TLV_CALIPSO,
1069-
.func = ipv6_hop_calipso,
1070-
},
1071-
{ -1, }
1072-
};
1073-
10741062
int ipv6_parse_hopopts(struct sk_buff *skb)
10751063
{
10761064
struct inet6_skb_parm *opt = IP6CB(skb);
@@ -1096,8 +1084,7 @@ int ipv6_parse_hopopts(struct sk_buff *skb)
10961084
goto fail_and_free;
10971085

10981086
opt->flags |= IP6SKB_HOPBYHOP;
1099-
if (ip6_parse_tlv(tlvprochopopt_lst, skb,
1100-
net->ipv6.sysctl.max_hbh_opts_cnt)) {
1087+
if (ip6_parse_tlv(true, skb, net->ipv6.sysctl.max_hbh_opts_cnt)) {
11011088
skb->transport_header += extlen;
11021089
opt = IP6CB(skb);
11031090
opt->nhoff = sizeof(struct ipv6hdr);

0 commit comments

Comments
 (0)