Skip to content

Commit eca4205

Browse files
ummakynesdavem330
authored andcommitted
ethtool: add ethtool_rx_flow_spec to flow_rule structure translator
This patch adds a function to translate the ethtool_rx_flow_spec structure to the flow_rule representation. This allows us to reuse code from the driver side given that both flower and ethtool_rx_flow interfaces use the same representation. This patch also includes support for the flow type flags FLOW_EXT, FLOW_MAC_EXT and FLOW_RSS. The ethtool_rx_flow_spec_input wrapper structure is used to convey the rss_context field, that is away from the ethtool_rx_flow_spec structure, and the ethtool_rx_flow_spec structure. Signed-off-by: Pablo Neira Ayuso <[email protected]> Acked-by: Jiri Pirko <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8bec283 commit eca4205

File tree

2 files changed

+256
-0
lines changed

2 files changed

+256
-0
lines changed

include/linux/ethtool.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,4 +400,19 @@ struct ethtool_ops {
400400
void (*get_ethtool_phy_stats)(struct net_device *,
401401
struct ethtool_stats *, u64 *);
402402
};
403+
404+
struct ethtool_rx_flow_rule {
405+
struct flow_rule *rule;
406+
unsigned long priv[0];
407+
};
408+
409+
struct ethtool_rx_flow_spec_input {
410+
const struct ethtool_rx_flow_spec *fs;
411+
u32 rss_ctx;
412+
};
413+
414+
struct ethtool_rx_flow_rule *
415+
ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input);
416+
void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *rule);
417+
403418
#endif /* _LINUX_ETHTOOL_H */

net/core/ethtool.c

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/net.h>
3030
#include <net/devlink.h>
3131
#include <net/xdp_sock.h>
32+
#include <net/flow_offload.h>
3233

3334
/*
3435
* Some useful ethtool_ops methods that're device independent.
@@ -2820,3 +2821,243 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
28202821

28212822
return rc;
28222823
}
2824+
2825+
struct ethtool_rx_flow_key {
2826+
struct flow_dissector_key_basic basic;
2827+
union {
2828+
struct flow_dissector_key_ipv4_addrs ipv4;
2829+
struct flow_dissector_key_ipv6_addrs ipv6;
2830+
};
2831+
struct flow_dissector_key_ports tp;
2832+
struct flow_dissector_key_ip ip;
2833+
struct flow_dissector_key_vlan vlan;
2834+
struct flow_dissector_key_eth_addrs eth_addrs;
2835+
} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
2836+
2837+
struct ethtool_rx_flow_match {
2838+
struct flow_dissector dissector;
2839+
struct ethtool_rx_flow_key key;
2840+
struct ethtool_rx_flow_key mask;
2841+
};
2842+
2843+
struct ethtool_rx_flow_rule *
2844+
ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input)
2845+
{
2846+
const struct ethtool_rx_flow_spec *fs = input->fs;
2847+
static struct in6_addr zero_addr = {};
2848+
struct ethtool_rx_flow_match *match;
2849+
struct ethtool_rx_flow_rule *flow;
2850+
struct flow_action_entry *act;
2851+
2852+
flow = kzalloc(sizeof(struct ethtool_rx_flow_rule) +
2853+
sizeof(struct ethtool_rx_flow_match), GFP_KERNEL);
2854+
if (!flow)
2855+
return ERR_PTR(-ENOMEM);
2856+
2857+
/* ethtool_rx supports only one single action per rule. */
2858+
flow->rule = flow_rule_alloc(1);
2859+
if (!flow->rule) {
2860+
kfree(flow);
2861+
return ERR_PTR(-ENOMEM);
2862+
}
2863+
2864+
match = (struct ethtool_rx_flow_match *)flow->priv;
2865+
flow->rule->match.dissector = &match->dissector;
2866+
flow->rule->match.mask = &match->mask;
2867+
flow->rule->match.key = &match->key;
2868+
2869+
match->mask.basic.n_proto = htons(0xffff);
2870+
2871+
switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
2872+
case TCP_V4_FLOW:
2873+
case UDP_V4_FLOW: {
2874+
const struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec;
2875+
2876+
match->key.basic.n_proto = htons(ETH_P_IP);
2877+
2878+
v4_spec = &fs->h_u.tcp_ip4_spec;
2879+
v4_m_spec = &fs->m_u.tcp_ip4_spec;
2880+
2881+
if (v4_m_spec->ip4src) {
2882+
match->key.ipv4.src = v4_spec->ip4src;
2883+
match->mask.ipv4.src = v4_m_spec->ip4src;
2884+
}
2885+
if (v4_m_spec->ip4dst) {
2886+
match->key.ipv4.dst = v4_spec->ip4dst;
2887+
match->mask.ipv4.dst = v4_m_spec->ip4dst;
2888+
}
2889+
if (v4_m_spec->ip4src ||
2890+
v4_m_spec->ip4dst) {
2891+
match->dissector.used_keys |=
2892+
BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
2893+
match->dissector.offset[FLOW_DISSECTOR_KEY_IPV4_ADDRS] =
2894+
offsetof(struct ethtool_rx_flow_key, ipv4);
2895+
}
2896+
if (v4_m_spec->psrc) {
2897+
match->key.tp.src = v4_spec->psrc;
2898+
match->mask.tp.src = v4_m_spec->psrc;
2899+
}
2900+
if (v4_m_spec->pdst) {
2901+
match->key.tp.dst = v4_spec->pdst;
2902+
match->mask.tp.dst = v4_m_spec->pdst;
2903+
}
2904+
if (v4_m_spec->psrc ||
2905+
v4_m_spec->pdst) {
2906+
match->dissector.used_keys |=
2907+
BIT(FLOW_DISSECTOR_KEY_PORTS);
2908+
match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
2909+
offsetof(struct ethtool_rx_flow_key, tp);
2910+
}
2911+
if (v4_m_spec->tos) {
2912+
match->key.ip.tos = v4_spec->tos;
2913+
match->mask.ip.tos = v4_m_spec->tos;
2914+
match->dissector.used_keys |=
2915+
BIT(FLOW_DISSECTOR_KEY_IP);
2916+
match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
2917+
offsetof(struct ethtool_rx_flow_key, ip);
2918+
}
2919+
}
2920+
break;
2921+
case TCP_V6_FLOW:
2922+
case UDP_V6_FLOW: {
2923+
const struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec;
2924+
2925+
match->key.basic.n_proto = htons(ETH_P_IPV6);
2926+
2927+
v6_spec = &fs->h_u.tcp_ip6_spec;
2928+
v6_m_spec = &fs->m_u.tcp_ip6_spec;
2929+
if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
2930+
memcpy(&match->key.ipv6.src, v6_spec->ip6src,
2931+
sizeof(match->key.ipv6.src));
2932+
memcpy(&match->mask.ipv6.src, v6_m_spec->ip6src,
2933+
sizeof(match->mask.ipv6.src));
2934+
}
2935+
if (memcmp(v6_m_spec->ip6dst, &zero_addr, sizeof(zero_addr))) {
2936+
memcpy(&match->key.ipv6.dst, v6_spec->ip6dst,
2937+
sizeof(match->key.ipv6.dst));
2938+
memcpy(&match->mask.ipv6.dst, v6_m_spec->ip6dst,
2939+
sizeof(match->mask.ipv6.dst));
2940+
}
2941+
if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr)) ||
2942+
memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
2943+
match->dissector.used_keys |=
2944+
BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
2945+
match->dissector.offset[FLOW_DISSECTOR_KEY_IPV6_ADDRS] =
2946+
offsetof(struct ethtool_rx_flow_key, ipv6);
2947+
}
2948+
if (v6_m_spec->psrc) {
2949+
match->key.tp.src = v6_spec->psrc;
2950+
match->mask.tp.src = v6_m_spec->psrc;
2951+
}
2952+
if (v6_m_spec->pdst) {
2953+
match->key.tp.dst = v6_spec->pdst;
2954+
match->mask.tp.dst = v6_m_spec->pdst;
2955+
}
2956+
if (v6_m_spec->psrc ||
2957+
v6_m_spec->pdst) {
2958+
match->dissector.used_keys |=
2959+
BIT(FLOW_DISSECTOR_KEY_PORTS);
2960+
match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
2961+
offsetof(struct ethtool_rx_flow_key, tp);
2962+
}
2963+
if (v6_m_spec->tclass) {
2964+
match->key.ip.tos = v6_spec->tclass;
2965+
match->mask.ip.tos = v6_m_spec->tclass;
2966+
match->dissector.used_keys |=
2967+
BIT(FLOW_DISSECTOR_KEY_IP);
2968+
match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
2969+
offsetof(struct ethtool_rx_flow_key, ip);
2970+
}
2971+
}
2972+
break;
2973+
default:
2974+
ethtool_rx_flow_rule_destroy(flow);
2975+
return ERR_PTR(-EINVAL);
2976+
}
2977+
2978+
switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
2979+
case TCP_V4_FLOW:
2980+
case TCP_V6_FLOW:
2981+
match->key.basic.ip_proto = IPPROTO_TCP;
2982+
break;
2983+
case UDP_V4_FLOW:
2984+
case UDP_V6_FLOW:
2985+
match->key.basic.ip_proto = IPPROTO_UDP;
2986+
break;
2987+
}
2988+
match->mask.basic.ip_proto = 0xff;
2989+
2990+
match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
2991+
match->dissector.offset[FLOW_DISSECTOR_KEY_BASIC] =
2992+
offsetof(struct ethtool_rx_flow_key, basic);
2993+
2994+
if (fs->flow_type & FLOW_EXT) {
2995+
const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
2996+
const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
2997+
2998+
if (ext_m_spec->vlan_etype &&
2999+
ext_m_spec->vlan_tci) {
3000+
match->key.vlan.vlan_tpid = ext_h_spec->vlan_etype;
3001+
match->mask.vlan.vlan_tpid = ext_m_spec->vlan_etype;
3002+
3003+
match->key.vlan.vlan_id =
3004+
ntohs(ext_h_spec->vlan_tci) & 0x0fff;
3005+
match->mask.vlan.vlan_id =
3006+
ntohs(ext_m_spec->vlan_tci) & 0x0fff;
3007+
3008+
match->key.vlan.vlan_priority =
3009+
(ntohs(ext_h_spec->vlan_tci) & 0xe000) >> 13;
3010+
match->mask.vlan.vlan_priority =
3011+
(ntohs(ext_m_spec->vlan_tci) & 0xe000) >> 13;
3012+
3013+
match->dissector.used_keys |=
3014+
BIT(FLOW_DISSECTOR_KEY_VLAN);
3015+
match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] =
3016+
offsetof(struct ethtool_rx_flow_key, vlan);
3017+
}
3018+
}
3019+
if (fs->flow_type & FLOW_MAC_EXT) {
3020+
const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
3021+
const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
3022+
3023+
if (ext_m_spec->h_dest) {
3024+
memcpy(match->key.eth_addrs.dst, ext_h_spec->h_dest,
3025+
ETH_ALEN);
3026+
memcpy(match->mask.eth_addrs.dst, ext_m_spec->h_dest,
3027+
ETH_ALEN);
3028+
3029+
match->dissector.used_keys |=
3030+
BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
3031+
match->dissector.offset[FLOW_DISSECTOR_KEY_ETH_ADDRS] =
3032+
offsetof(struct ethtool_rx_flow_key, eth_addrs);
3033+
}
3034+
}
3035+
3036+
act = &flow->rule->action.entries[0];
3037+
switch (fs->ring_cookie) {
3038+
case RX_CLS_FLOW_DISC:
3039+
act->id = FLOW_ACTION_DROP;
3040+
break;
3041+
case RX_CLS_FLOW_WAKE:
3042+
act->id = FLOW_ACTION_WAKE;
3043+
break;
3044+
default:
3045+
act->id = FLOW_ACTION_QUEUE;
3046+
if (fs->flow_type & FLOW_RSS)
3047+
act->queue.ctx = input->rss_ctx;
3048+
3049+
act->queue.vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
3050+
act->queue.index = ethtool_get_flow_spec_ring(fs->ring_cookie);
3051+
break;
3052+
}
3053+
3054+
return flow;
3055+
}
3056+
EXPORT_SYMBOL(ethtool_rx_flow_rule_create);
3057+
3058+
void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *flow)
3059+
{
3060+
kfree(flow->rule);
3061+
kfree(flow);
3062+
}
3063+
EXPORT_SYMBOL(ethtool_rx_flow_rule_destroy);

0 commit comments

Comments
 (0)