Previously we stored the netlink-formatted version of a flow key and mask in 'struct udpif_key'. This patch stores the original key,mask in the 'struct match' format, which reduces the size of a ukey from 1216 bytes to 560. Furthermore, this reduces netlink conversions required and simplifies mask comparison in revalidate_ukey().
Signed-off-by: Joe Stringer <joestrin...@nicira.com> --- For reference, prior to this patchset the size of a udpif_key is 656, but the actions are not cached on master. This patch allows the entire patchset to have a negligible impact on the memory footprint while boosting performance. Some cases may even see an improvement in memory usage. v6: First post. --- ofproto/netflow.c | 3 +- ofproto/netflow.h | 2 +- ofproto/ofproto-dpif-upcall.c | 144 ++++++++++++++++------------------------- ofproto/ofproto-dpif.c | 6 ++ ofproto/ofproto-dpif.h | 1 + 5 files changed, 65 insertions(+), 91 deletions(-) diff --git a/ofproto/netflow.c b/ofproto/netflow.c index 5ab522f..640846b 100644 --- a/ofproto/netflow.c +++ b/ofproto/netflow.c @@ -276,7 +276,8 @@ netflow_expire__(struct netflow *nf, struct netflow_flow *nf_flow) } void -netflow_flow_clear(struct netflow *nf, struct flow *flow) OVS_EXCLUDED(mutex) +netflow_flow_clear(struct netflow *nf, const struct flow *flow) + OVS_EXCLUDED(mutex) { struct netflow_flow *nf_flow; diff --git a/ofproto/netflow.h b/ofproto/netflow.h index 94dd3ff..048eb20 100644 --- a/ofproto/netflow.h +++ b/ofproto/netflow.h @@ -52,7 +52,7 @@ void netflow_wait(struct netflow *); void netflow_mask_wc(struct flow *, struct flow_wildcards *); -void netflow_flow_clear(struct netflow *netflow, struct flow *flow); +void netflow_flow_clear(struct netflow *netflow, const struct flow *flow); void netflow_flow_update(struct netflow *nf, const struct flow *flow, ofp_port_t output_iface, diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c index 079e0e4..bab0f67 100644 --- a/ofproto/ofproto-dpif-upcall.c +++ b/ofproto/ofproto-dpif-upcall.c @@ -200,10 +200,7 @@ struct udpif_key { /* These elements are read only once created, and therefore aren't * protected by a mutex. */ - const struct nlattr *key; /* Datapath flow key. */ - size_t key_len; /* Length of 'key'. */ - const struct nlattr *mask; /* Datapath flow mask. */ - size_t mask_len; /* Length of 'mask'. */ + struct match match; /* Flow key/mask. */ struct ofpbuf *actions; /* Datapath flow actions as nlattrs. */ ovs_u128 uid; /* Unique flow identifier. */ uint32_t hash; /* Pre-computed hash for 'key'. */ @@ -219,18 +216,16 @@ struct udpif_key { struct xlate_cache *xcache OVS_GUARDED; /* Cache for xlate entries that * are affected by this ukey. * Used for stats and learning.*/ - union { - struct odputil_keybuf buf; - struct nlattr nla; - } keybuf, maskbuf; }; /* Datapath operation with optional ukey attached. */ struct ukey_op { struct udpif_key *ukey; struct dpif_flow_stats stats; /* Stats for 'op'. */ - struct odputil_uidbuf uid; /* Storage for Netlink-formatted uid. */ struct dpif_op dop; /* Flow operation. */ + struct odputil_uidbuf uid; /* Storage for Netlink-formatted uid. */ + struct odputil_keybuf key; /* Storage for Netlink-formatted key. */ + struct odputil_keybuf mask; /* Storage for Netlink-formatted mask. */ }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); @@ -1159,17 +1154,30 @@ handle_upcalls(struct udpif *udpif, struct upcall *upcalls, * already. */ if (may_put && upcall->type == DPIF_UC_MISS) { struct udpif_key *ukey = upcall->ukey; + struct ofpbuf mask; upcall->ukey = NULL; op = &ops[n_ops++]; + ofpbuf_use_stack(&mask, &op->mask, sizeof op->mask); + if (megaflow) { + size_t max_mpls; + bool recirc; + + recirc = ofproto_dpif_get_enable_recirc(upcall->ofproto); + max_mpls = ofproto_dpif_get_max_mpls_depth(upcall->ofproto); + odp_flow_key_from_mask(&mask, &upcall->xout.wc.masks, + upcall->flow, UINT32_MAX, max_mpls, + recirc); + } + op->ukey = ukey; op->dop.type = DPIF_OP_FLOW_PUT; op->dop.u.flow_put.flags = DPIF_FP_CREATE; - op->dop.u.flow_put.key = ukey->key; - op->dop.u.flow_put.key_len = ukey->key_len; - op->dop.u.flow_put.mask = ukey->mask; - op->dop.u.flow_put.mask_len = ukey->mask_len; + op->dop.u.flow_put.key = upcall->key; + op->dop.u.flow_put.key_len = upcall->key_len; + op->dop.u.flow_put.mask = ofpbuf_data(&mask); + op->dop.u.flow_put.mask_len = ofpbuf_size(&mask); op->dop.u.flow_put.uid = upcall->uid; op->dop.u.flow_put.stats = NULL; op->dop.u.flow_put.actions = ofpbuf_data(ukey->actions); @@ -1243,34 +1251,10 @@ ukey_create(struct upcall *upcall) OVS_NO_THREAD_SAFETY_ANALYSIS { struct udpif_key *ukey = xmalloc(sizeof *ukey); - struct ofpbuf key, mask; - bool recirc, megaflow; - ofpbuf_use_stack(&key, &ukey->keybuf, sizeof ukey->keybuf); - if (upcall->key_len) { - ofpbuf_put(&key, upcall->key, upcall->key_len); - } else { - /* dpif-netdev doesn't provide a netlink-formatted flow key in the - * upcall, so convert the upcall's flow here. */ - odp_flow_key_from_flow(&key, upcall->flow, &upcall->xout.wc.masks, - upcall->flow->in_port.odp_port, true); - } - ukey->key = ofpbuf_data(&key); - ukey->key_len = ofpbuf_size(&key); - - atomic_read_relaxed(&enable_megaflows, &megaflow); - recirc = ofproto_dpif_get_enable_recirc(upcall->ofproto); - ofpbuf_use_stack(&mask, &ukey->maskbuf, sizeof ukey->maskbuf); - if (megaflow) { - size_t max_mpls; - - max_mpls = ofproto_dpif_get_max_mpls_depth(upcall->ofproto); - odp_flow_key_from_mask(&mask, &upcall->xout.wc.masks, - upcall->flow, UINT32_MAX, max_mpls, - recirc); - } - ukey->mask = ofpbuf_data(&mask); - ukey->mask_len = ofpbuf_size(&mask); + memcpy(&ukey->match.flow, upcall->flow, sizeof ukey->match.flow); + memcpy(&ukey->match.wc.masks, &upcall->xout.wc.masks, + sizeof ukey->match.wc.masks); memcpy(&ukey->uid, upcall->uid, sizeof ukey->uid); ukey->hash = get_uid_hash(&ukey->uid); ukey->actions = ofpbuf_clone(&upcall->put_actions); @@ -1305,19 +1289,18 @@ ukey_install_start(struct udpif *udpif, struct udpif_key *new_ukey) old_ukey = ukey_lookup(udpif, &new_ukey->uid); if (old_ukey) { /* Uncommon case: A ukey is already installed with the same UID. */ - if (old_ukey->key_len == new_ukey->key_len - && !memcmp(old_ukey->key, new_ukey->key, new_ukey->key_len)) { + if (flow_equal(&old_ukey->match.flow, &new_ukey->match.flow)) { COVERAGE_INC(handler_duplicate_upcall); } else { struct ds ds = DS_EMPTY_INITIALIZER; odp_format_uid(&old_ukey->uid, &ds); ds_put_cstr(&ds, " "); - odp_flow_key_format(old_ukey->key, old_ukey->key_len, &ds); + flow_format(&ds, &old_ukey->match.flow); ds_put_cstr(&ds, "\n"); odp_format_uid(&new_ukey->uid, &ds); ds_put_cstr(&ds, " "); - odp_flow_key_format(new_ukey->key, new_ukey->key_len, &ds); + flow_format(&ds, &new_ukey->match.flow); VLOG_WARN("Conflicting ukey for flows:\n%s", ds_cstr(&ds)); ds_destroy(&ds); @@ -1448,13 +1431,10 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey, struct ofproto_dpif *ofproto; struct dpif_flow_stats push; struct ofpbuf xout_actions; - struct flow flow, dp_mask; - uint32_t *dp32, *xout32; ofp_port_t ofp_in_port; struct xlate_in xin; long long int last_used; int error; - size_t i; bool ok; bool need_revalidate; @@ -1492,13 +1472,8 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey, goto exit; } - if (odp_flow_key_to_flow(ukey->key, ukey->key_len, &flow) - == ODP_FIT_ERROR) { - goto exit; - } - - error = xlate_lookup(udpif->backer, &flow, &ofproto, NULL, NULL, &netflow, - &ofp_in_port); + error = xlate_lookup(udpif->backer, &ukey->match.flow, &ofproto, NULL, + NULL, &netflow, &ofp_in_port); if (error) { goto exit; } @@ -1510,8 +1485,8 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey, ukey->xcache = xlate_cache_new(); } - xlate_in_init(&xin, ofproto, &flow, ofp_in_port, NULL, push.tcp_flags, - NULL); + xlate_in_init(&xin, ofproto, &ukey->match.flow, ofp_in_port, NULL, + push.tcp_flags, NULL); if (push.n_packets) { xin.resubmit_stats = &push; xin.may_learn = true; @@ -1531,32 +1506,19 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey, ofpbuf_size(xout.odp_actions)); } else { ofpbuf_use_stack(&xout_actions, slow_path_buf, sizeof slow_path_buf); - compose_slow_path(udpif, &xout, &flow, flow.in_port.odp_port, - &xout_actions); + compose_slow_path(udpif, &xout, &ukey->match.flow, + ukey->match.flow.in_port.odp_port, &xout_actions); } if (!ofpbuf_equal(&xout_actions, ukey->actions)) { goto exit; } - if (odp_flow_key_to_mask(ukey->mask, ukey->mask_len, &dp_mask, &flow) - == ODP_FIT_ERROR) { + if (memcmp(&xout.wc.masks, &ukey->match.wc.masks, + sizeof ukey->match.wc.masks)) { goto exit; } - /* Since the kernel is free to ignore wildcarded bits in the mask, we can't - * directly check that the masks are the same. Instead we check that the - * mask in the kernel is more specific i.e. less wildcarded, than what - * we've calculated here. This guarantees we don't catch any packets we - * shouldn't with the megaflow. */ - dp32 = (uint32_t *) &dp_mask; - xout32 = (uint32_t *) &xout.wc.masks; - for (i = 0; i < FLOW_U32S; i++) { - if ((dp32[i] | xout32[i]) != dp32[i]) { - goto exit; - } - } - ok = true; exit: @@ -1564,19 +1526,28 @@ exit: ukey->reval_seq = reval_seq; } if (netflow && !ok) { - netflow_flow_clear(netflow, &flow); + netflow_flow_clear(netflow, &ukey->match.flow); } xlate_out_uninit(xoutp); return ok; } static void -delete_op_init(struct ukey_op *op, struct udpif_key *ukey, bool terse_dump) +delete_op_init(struct udpif *udpif, struct ukey_op *op, struct udpif_key *ukey, + bool terse_dump) { + struct ofpbuf key; + bool recirc; + + recirc = dpif_backer_get_enable_recirc(udpif->backer); + ofpbuf_use_stack(&key, &op->key, sizeof op->key); + odp_flow_key_from_flow(&key, &ukey->match.flow, &ukey->match.wc.masks, + ukey->match.flow.in_port.odp_port, recirc); + op->ukey = ukey; op->dop.type = DPIF_OP_FLOW_DEL; - op->dop.u.flow_del.key = ukey->key; - op->dop.u.flow_del.key_len = ukey->key_len; + op->dop.u.flow_del.key = ofpbuf_data(&key); + op->dop.u.flow_del.key_len = ofpbuf_size(&key); op->dop.u.flow_del.uid = &ukey->uid; op->dop.u.flow_del.terse = terse_dump; op->dop.u.flow_del.stats = &op->stats; @@ -1610,10 +1581,10 @@ push_ukey_ops__(struct udpif *udpif, struct ukey_op *ops, size_t n_ops) ovs_mutex_unlock(&op->ukey->mutex); if (push->n_packets || netflow_exists()) { + const struct flow *flow = &op->ukey->match.flow; struct ofproto_dpif *ofproto; struct netflow *netflow; ofp_port_t ofp_in_port; - struct flow flow; int error; ovs_mutex_lock(&op->ukey->mutex); @@ -1624,17 +1595,12 @@ push_ukey_ops__(struct udpif *udpif, struct ukey_op *ops, size_t n_ops) } ovs_mutex_unlock(&op->ukey->mutex); - if (odp_flow_key_to_flow(op->ukey->key, op->ukey->key_len, &flow) - == ODP_FIT_ERROR) { - continue; - } - - error = xlate_lookup(udpif->backer, &flow, &ofproto, - NULL, NULL, &netflow, &ofp_in_port); + error = xlate_lookup(udpif->backer, flow, &ofproto, NULL, NULL, + &netflow, &ofp_in_port); if (!error) { struct xlate_in xin; - xlate_in_init(&xin, ofproto, &flow, ofp_in_port, NULL, + xlate_in_init(&xin, ofproto, flow, ofp_in_port, NULL, push->tcp_flags, NULL); xin.resubmit_stats = push->n_packets ? push : NULL; xin.may_learn = push->n_packets > 0; @@ -1642,7 +1608,7 @@ push_ukey_ops__(struct udpif *udpif, struct ukey_op *ops, size_t n_ops) xlate_actions_for_side_effects(&xin); if (netflow) { - netflow_flow_clear(netflow, &flow); + netflow_flow_clear(netflow, flow); } } } @@ -1769,7 +1735,7 @@ revalidate(struct revalidator *revalidator) ukey->flow_exists = keep; if (!keep) { - delete_op_init(&ops[n_ops++], ukey, terse_dump); + delete_op_init(udpif, &ops[n_ops++], ukey, terse_dump); } ovs_mutex_unlock(&ukey->mutex); } @@ -1840,7 +1806,7 @@ revalidator_sweep__(struct revalidator *revalidator, bool purge) ukey)))) { struct ukey_op *op = &ops[n_ops++]; - delete_op_init(op, ukey, terse_dump); + delete_op_init(udpif, op, ukey, terse_dump); if (n_ops == REVALIDATE_MAX_BATCH) { push_ukey_ops(udpif, umap, ops, n_ops); n_ops = 0; diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 6749d9d..c08bbd7 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -366,6 +366,12 @@ ofproto_dpif_get_enable_recirc(const struct ofproto_dpif *ofproto) } bool +dpif_backer_get_enable_recirc(const struct dpif_backer *backer) +{ + return backer->enable_recirc; +} + +bool ofproto_dpif_get_enable_uid(const struct ofproto_dpif *ofproto) { return ofproto->backer->enable_uid; diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h index 7db18d4..1752dda 100644 --- a/ofproto/ofproto-dpif.h +++ b/ofproto/ofproto-dpif.h @@ -88,6 +88,7 @@ enum rule_dpif_lookup_verdict { size_t ofproto_dpif_get_max_mpls_depth(const struct ofproto_dpif *); bool ofproto_dpif_get_enable_recirc(const struct ofproto_dpif *); bool ofproto_dpif_get_enable_uid(const struct ofproto_dpif *); +bool dpif_backer_get_enable_recirc(const struct dpif_backer *); bool dpif_backer_get_enable_uid(const struct dpif_backer *); uint8_t rule_dpif_lookup(struct ofproto_dpif *, struct flow *, -- 1.7.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev