mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 06:15:47 +00:00
dpif-netdev: Translate Geneve options per-flow, not per-packet.
The kernel implementation of Geneve options stores the TLV option data in the flow exactly as received, without any further parsing. This is then translated to known options for the purposes of matching on flow setup (which will then install a datapath flow in the form the kernel is expecting). The userspace implementation behaves a little bit differently - it looks up known options as each packet is received. The reason for this is there is a much tighter coupling between datapath and flow translation and the representation is generally expected to be the same. This works but it incurs work on a per-packet basis that could be done per-flow instead. This introduces a small translation step for Geneve packets between datapath and flow lookup for the userspace datapath in order to allow the same kind of processing that the kernel does. A side effect of this is that unknown options are now shown when flows dumped via ovs-appctl dpif/dump-flows, similar to the kernel. There is a second benefit to this as well: for some operations it is preferable to keep the options exactly as they were received on the wire, which this enables. One example is that for packets that are executed from ofproto-dpif-upcall to the datapath, this avoids the translation of Geneve metadata. Since this conversion is potentially lossy (for unknown options), keeping everything in the same format removes the possibility of dropping options if the packet comes back up to userspace and the Geneve option translation table has changed. To help with these types of operations, most functions can understand both formats of data and seamlessly do the right thing. Signed-off-by: Jesse Gross <jesse@nicira.com> Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
This commit is contained in:
@@ -1884,8 +1884,8 @@ dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
|
||||
if (mask_key_len) {
|
||||
enum odp_key_fitness fitness;
|
||||
|
||||
fitness = odp_flow_key_to_mask(mask_key, mask_key_len, key, key_len,
|
||||
&wc->masks, flow);
|
||||
fitness = odp_flow_key_to_mask_udpif(mask_key, mask_key_len, key,
|
||||
key_len, &wc->masks, flow);
|
||||
if (fitness) {
|
||||
/* This should not happen: it indicates that
|
||||
* odp_flow_key_from_mask() and odp_flow_key_to_mask()
|
||||
@@ -1919,7 +1919,7 @@ dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
|
||||
{
|
||||
odp_port_t in_port;
|
||||
|
||||
if (odp_flow_key_to_flow(key, key_len, flow)) {
|
||||
if (odp_flow_key_to_flow_udpif(key, key_len, flow)) {
|
||||
/* This should not happen: it indicates that odp_flow_key_from_flow()
|
||||
* and odp_flow_key_to_flow() disagree on the acceptable form of a
|
||||
* flow. Log the problem as an error, with enough details to enable
|
||||
@@ -3022,11 +3022,27 @@ dp_netdev_upcall(struct dp_netdev_pmd_thread *pmd, struct dp_packet *packet_,
|
||||
struct ofpbuf *actions, struct ofpbuf *put_actions)
|
||||
{
|
||||
struct dp_netdev *dp = pmd->dp;
|
||||
struct flow_tnl orig_tunnel;
|
||||
int err;
|
||||
|
||||
if (OVS_UNLIKELY(!dp->upcall_cb)) {
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
/* Upcall processing expects the Geneve options to be in the translated
|
||||
* format but we need to retain the raw format for datapath use. */
|
||||
orig_tunnel.flags = flow->tunnel.flags;
|
||||
if (flow->tunnel.flags & FLOW_TNL_F_UDPIF) {
|
||||
orig_tunnel.metadata.present.len = flow->tunnel.metadata.present.len;
|
||||
memcpy(orig_tunnel.metadata.opts.gnv, flow->tunnel.metadata.opts.gnv,
|
||||
flow->tunnel.metadata.present.len);
|
||||
err = tun_metadata_from_geneve_udpif(&orig_tunnel, &orig_tunnel,
|
||||
&flow->tunnel);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (OVS_UNLIKELY(!VLOG_DROP_DBG(&upcall_rl))) {
|
||||
struct ds ds = DS_EMPTY_INITIALIZER;
|
||||
char *packet_str;
|
||||
@@ -3054,8 +3070,44 @@ dp_netdev_upcall(struct dp_netdev_pmd_thread *pmd, struct dp_packet *packet_,
|
||||
ds_destroy(&ds);
|
||||
}
|
||||
|
||||
return dp->upcall_cb(packet_, flow, ufid, pmd->core_id, type, userdata,
|
||||
actions, wc, put_actions, dp->upcall_aux);
|
||||
err = dp->upcall_cb(packet_, flow, ufid, pmd->core_id, type, userdata,
|
||||
actions, wc, put_actions, dp->upcall_aux);
|
||||
if (err && err != ENOSPC) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Translate tunnel metadata masks to datapath format. */
|
||||
if (wc) {
|
||||
if (wc->masks.tunnel.metadata.present.map) {
|
||||
struct geneve_opt opts[GENEVE_TOT_OPT_SIZE /
|
||||
sizeof(struct geneve_opt)];
|
||||
|
||||
tun_metadata_to_geneve_udpif_mask(&flow->tunnel,
|
||||
&wc->masks.tunnel,
|
||||
orig_tunnel.metadata.opts.gnv,
|
||||
orig_tunnel.metadata.present.len,
|
||||
opts);
|
||||
|
||||
memset(&wc->masks.tunnel.metadata, 0,
|
||||
sizeof wc->masks.tunnel.metadata);
|
||||
memcpy(&wc->masks.tunnel.metadata.opts.gnv, opts,
|
||||
orig_tunnel.metadata.present.len);
|
||||
}
|
||||
wc->masks.tunnel.metadata.present.len = 0xff;
|
||||
}
|
||||
|
||||
/* Restore tunnel metadata. We need to use the saved options to ensure
|
||||
* that any unknown options are not lost. The generated mask will have
|
||||
* the same structure, matching on types and lengths but wildcarding
|
||||
* option data we don't care about. */
|
||||
if (orig_tunnel.flags & FLOW_TNL_F_UDPIF) {
|
||||
memcpy(&flow->tunnel.metadata.opts.gnv, orig_tunnel.metadata.opts.gnv,
|
||||
orig_tunnel.metadata.present.len);
|
||||
flow->tunnel.metadata.present.len = orig_tunnel.metadata.present.len;
|
||||
flow->tunnel.flags |= FLOW_TNL_F_UDPIF;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
|
Reference in New Issue
Block a user