// SPDX-License-Identifier: GPL-2.0-only /**************************************************************************** * Driver for Solarflare network controllers and boards * Copyright 2023, Advanced Micro Devices, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference.
*/
/* Only call this in init failure teardown. * Normal exit should fini instead as there may be entries in the table.
*/ void efx_tc_destroy_encap_actions(struct efx_nic *efx)
{
rhashtable_destroy(&efx->tc->encap_ht);
rhashtable_destroy(&efx->tc->neigh_ht);
}
/* GCC stupidly thinks that only values explicitly listed in the enum * definition can _possibly_ be sensible case values, so without this * cast it complains about the IPv6 versions.
*/ switch ((int)encap->type) { case EFX_ENCAP_TYPE_VXLAN: case EFX_ENCAP_TYPE_GENEVE:
flow4.flowi4_proto = IPPROTO_UDP;
flow4.fl4_dport = encap->key.tp_dst;
flow4.flowi4_tos = encap->key.tos;
flow4.daddr = encap->key.u.ipv4.dst;
flow4.saddr = encap->key.u.ipv4.src; break; case EFX_ENCAP_TYPE_VXLAN | EFX_ENCAP_FLAG_IPV6: case EFX_ENCAP_TYPE_GENEVE | EFX_ENCAP_FLAG_IPV6:
flow6.flowi6_proto = IPPROTO_UDP;
flow6.fl6_dport = encap->key.tp_dst;
flow6.flowlabel = ip6_make_flowinfo(encap->key.tos,
encap->key.label);
flow6.daddr = encap->key.u.ipv6.dst;
flow6.saddr = encap->key.u.ipv6.src; break; default:
NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported encap type %d",
(int)encap->type); return -EOPNOTSUPP;
}
old = rhashtable_lookup_get_insert_fast(&efx->tc->neigh_ht,
&neigh->linkage,
efx_neigh_ht_params); if (old) { /* don't need our new entry */
put_net_track(neigh->net, &neigh->ns_tracker);
kfree(neigh); if (IS_ERR(old)) /* oh dear, it's actually an error */ return PTR_ERR(old); if (!refcount_inc_not_zero(&old->ref)) return -EAGAIN; /* existing entry found, ref taken */
neigh = old;
} else { /* New entry. We need to initiate a lookup */ struct neighbour *n; struct rtable *rt;
if (encap->type & EFX_ENCAP_FLAG_IPV6) { #if IS_ENABLED(CONFIG_IPV6) struct dst_entry *dst;
dst = ipv6_stub->ipv6_dst_lookup_flow(net, NULL, &flow6,
NULL);
rc = PTR_ERR_OR_ZERO(dst); if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to lookup route for IPv6 encap"); goto out_free;
}
neigh->egdev = dst->dev;
netdev_hold(neigh->egdev, &neigh->dev_tracker,
GFP_KERNEL_ACCOUNT);
neigh->ttl = ip6_dst_hoplimit(dst);
n = dst_neigh_lookup(dst, &flow6.daddr);
dst_release(dst); #else /* We shouldn't ever get here, because if IPv6 isn't * enabled how did someone create an IPv6 tunnel_key?
*/
rc = -EOPNOTSUPP;
NL_SET_ERR_MSG_MOD(extack, "No IPv6 support (neigh bind)"); goto out_free; #endif
} else {
rt = ip_route_output_key(net, &flow4); if (IS_ERR_OR_NULL(rt)) {
rc = PTR_ERR_OR_ZERO(rt); if (!rc)
rc = -EIO;
NL_SET_ERR_MSG_MOD(extack, "Failed to lookup route for encap"); goto out_free;
}
neigh->egdev = rt->dst.dev;
netdev_hold(neigh->egdev, &neigh->dev_tracker,
GFP_KERNEL_ACCOUNT);
neigh->ttl = ip4_dst_hoplimit(&rt->dst);
n = dst_neigh_lookup(&rt->dst, &flow4.daddr);
ip_rt_put(rt);
} if (!n) {
rc = -ENETUNREACH;
NL_SET_ERR_MSG_MOD(extack, "Failed to lookup neighbour for encap");
netdev_put(neigh->egdev, &neigh->dev_tracker); goto out_free;
}
refcount_set(&neigh->ref, 1);
INIT_LIST_HEAD(&neigh->users);
read_lock_bh(&n->lock);
ether_addr_copy(neigh->ha, n->ha);
neigh->n_valid = n->nud_state & NUD_VALID;
read_unlock_bh(&n->lock);
rwlock_init(&neigh->lock);
INIT_WORK(&neigh->work, efx_neigh_update);
neigh->efx = efx;
neigh->used = jiffies; if (!neigh->n_valid) /* Prod ARP to find us a neighbour */
neigh_event_send(n, NULL);
neigh_release(n);
} /* Add us to this neigh */
encap->neigh = neigh;
list_add_tail(&encap->list, &neigh->users); return 0;
out_free: /* cleanup common to several error paths */
rhashtable_remove_fast(&efx->tc->neigh_ht, &neigh->linkage,
efx_neigh_ht_params);
synchronize_rcu();
put_net_track(net, &neigh->ns_tracker);
kfree(neigh); return rc;
}
if (!neigh) return;
list_del(&encap->list);
encap->neigh = NULL; if (!refcount_dec_and_test(&neigh->ref)) return; /* still in use */
efx_free_neigh(neigh);
}
/* GCC stupidly thinks that only values explicitly listed in the enum * definition can _possibly_ be sensible case values, so without this * cast it complains about the IPv6 versions.
*/ switch ((int)encap->type) { case EFX_ENCAP_TYPE_VXLAN:
efx_gen_vxlan_header_ipv4(encap); break; case EFX_ENCAP_TYPE_GENEVE:
efx_gen_geneve_header_ipv4(encap); break; #ifdef CONFIG_IPV6 case EFX_ENCAP_TYPE_VXLAN | EFX_ENCAP_FLAG_IPV6:
efx_gen_vxlan_header_ipv6(encap); break; case EFX_ENCAP_TYPE_GENEVE | EFX_ENCAP_FLAG_IPV6:
efx_gen_geneve_header_ipv6(encap); break; #endif default: /* unhandled encap type, can't happen */ if (net_ratelimit())
netif_err(efx, drv, efx->net_dev, "Bogus encap type %d, can't generate\n",
encap->type);
if (encap->n_valid) { /* Make sure no rules are using this encap while we change it */
list_for_each_entry(act, &encap->users, encap_user) {
acts = act->user; if (WARN_ON(!acts)) /* can't happen */ continue;
rule = container_of(acts, struct efx_tc_flow_rule, acts); if (rule->fallback)
fallback = rule->fallback; else/* fallback of the fallback: deliver to PF */
fallback = &efx->tc->facts.pf;
rc = efx_mae_update_rule(efx, fallback->fw_id,
rule->fw_id); if (rc)
netif_err(efx, drv, efx->net_dev, "Failed to update (f) rule %08x rc %d\n",
rule->fw_id, rc); else
netif_dbg(efx, drv, efx->net_dev, "Updated (f) rule %08x\n",
rule->fw_id);
}
}
/* Make sure we don't leak arbitrary bytes on the wire; * set an all-0s ethernet header. A successful call to * efx_gen_encap_header() will overwrite this.
*/
memset(encap->encap_hdr, 0, sizeof(encap->encap_hdr));
encap->encap_hdr_len = ETH_HLEN;
/* Encap actions can only be offloaded if they have valid * neighbour info for the outer Ethernet header.
*/
list_for_each_entry(act, &rule->acts.list, list) if (act->encap_md && !act->encap_md->n_valid) returnfalse; returntrue;
}
list_for_each_entry_safe(encap, next, &neigh->users, list) { /* Should cause neigh usage count to fall to zero, freeing it */
efx_release_neigh(efx, encap); /* The encap has lost its neigh, so it's now unready */
efx_tc_update_encap(efx, encap);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.