This is cleaner than I was expecting.  Do you think we want to backport it to 
2.3?

--Justin


> On Aug 6, 2015, at 12:30 PM, Jarno Rajahalme <jrajaha...@nicira.com> wrote:
> 
> Ethan,
> 
> This ended up being a very clean patch, thank you!
> 
> IMO this is good to go, but I’d like to see a test case that shows the modify 
> operation in the logs to prevent regression in the future. Maybe work off 
> some of the bond rebalance test cases to prove that modify is used in the 
> case we currently know suffers from the lack of modify support?
> 
> Also, there are some minor comments below you could address at the same time.
> 
>  Jarno
> 
>> On Aug 5, 2015, at 4:07 PM, Ethan Jackson <et...@nicira.com> wrote:
>> 
>> There are certain use cases (such as bond rebalancing) where a
>> datapath flow's actions may change, while it's wildcard pattern
>> remains the same.  Before this patch, revalidators would note the
>> change, delete the flow, and wait for the handlers to install an
>> updated version.  This is inefficient, as many packets could get
>> punted to userspace before the new flow is finally installed.
>> 
>> To improve the situation, this patch implements in place modification
>> of datapath flows.  If the revalidators detect the only change to a
>> given ukey is its actions, instead of deleting it, it does a put with
>> the MODIFY flag set.
>> 
>> Signed-off-by: Ethan Jackson <et...@nicira.com>
>> ---
>> ofproto/ofproto-dpif-upcall.c | 118 
>> +++++++++++++++++++++++++++++++-----------
>> 1 file changed, 87 insertions(+), 31 deletions(-)
>> 
>> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
>> index 59010c2..7abc97d 100644
>> --- a/ofproto/ofproto-dpif-upcall.c
>> +++ b/ofproto/ofproto-dpif-upcall.c
>> @@ -150,6 +150,12 @@ enum upcall_type {
>>    IPFIX_UPCALL                /* Per-bridge sampling. */
>> };
>> 
>> +enum reval_result {
>> +    UKEY_KEEP,
>> +    UKEY_DELETE,
>> +    UKEY_MODIFY
>> +};
>> +
>> struct upcall {
>>    struct ofproto_dpif *ofproto;  /* Parent ofproto. */
>>    const struct recirc_id_node *recirc; /* Recirculation context. */
>> @@ -1663,33 +1669,41 @@ should_revalidate(const struct udpif *udpif, 
>> uint64_t packets,
>>    return false;
>> }
>> 
>> -static bool
>> +/* Verifies that the datapath actions of 'ukey' are still correct, and 
>> pushes
>> + * 'stats' for it.
>> + *
>> + * Returns a recommended action for 'ukey', options include: UKEY_DELETE,
>> + * meaning the ukey should be deleted.  UKEY_KEEP, meaning the ukey is fine 
>> as
>> + * is.  Or UKEY_MODIFY, meaning the ukey's actions should be changed but is
>> + * otherwise fine.  If UKEY_MODIFY, is returned, callers should change the
>> + * ukey's actions to those found in the caller supplied 'odp_actions' 
>> buffer.
>> + * */
> 
> This would be easier to read if you reformat this with each option listed in 
> it’s own line, and drop the repeating “meaning the”.
> 
>> +static enum reval_result
>> revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey,
>> -                const struct dpif_flow_stats *stats, uint64_t reval_seq)
>> +                const struct dpif_flow_stats *stats,
>> +                struct ofpbuf *odp_actions, uint64_t reval_seq)
>>    OVS_REQUIRES(ukey->mutex)
>> {
>> -    uint64_t odp_actions_stub[1024 / 8];
>> -    struct ofpbuf odp_actions = OFPBUF_STUB_INITIALIZER(odp_actions_stub);
>> -
>>    struct xlate_out xout, *xoutp;
>>    struct netflow *netflow;
>>    struct ofproto_dpif *ofproto;
>>    struct dpif_flow_stats push;
>>    struct flow flow, dp_mask;
>>    struct flow_wildcards wc;
>> +    enum reval_result result;
>>    uint64_t *dp64, *xout64;
>>    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;
>> 
>> -    ok = false;
>> +    result = UKEY_DELETE;
>>    xoutp = NULL;
>>    netflow = NULL;
>> 
>> +    ofpbuf_clear(odp_actions);
>>    need_revalidate = (ukey->reval_seq != reval_seq);
>>    last_used = ukey->stats.used;
>>    push.used = stats->used;
>> @@ -1703,20 +1717,19 @@ revalidate_ukey(struct udpif *udpif, struct 
>> udpif_key *ukey,
>> 
>>    if (need_revalidate && last_used
>>        && !should_revalidate(udpif, push.n_packets, last_used)) {
>> -        ok = false;
>>        goto exit;
>>    }
>> 
>>    /* We will push the stats, so update the ukey stats cache. */
>>    ukey->stats = *stats;
>>    if (!push.n_packets && !need_revalidate) {
>> -        ok = true;
>> +        result = UKEY_KEEP;
>>        goto exit;
>>    }
>> 
>>    if (ukey->xcache && !need_revalidate) {
>>        xlate_push_stats(ukey->xcache, &push);
>> -        ok = true;
>> +        result = UKEY_KEEP;
>>        goto exit;
>>    }
>> 
>> @@ -1739,7 +1752,7 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key 
>> *ukey,
>>    }
>> 
>>    xlate_in_init(&xin, ofproto, &flow, ofp_in_port, NULL, push.tcp_flags,
>> -                  NULL, need_revalidate ? &wc : NULL, &odp_actions);
>> +                  NULL, need_revalidate ? &wc : NULL, odp_actions);
>>    if (push.n_packets) {
>>        xin.resubmit_stats = &push;
>>        xin.may_learn = true;
>> @@ -1749,18 +1762,14 @@ revalidate_ukey(struct udpif *udpif, struct 
>> udpif_key *ukey,
>>    xoutp = &xout;
>> 
>>    if (!need_revalidate) {
>> -        ok = true;
>> +        result = UKEY_KEEP;
>>        goto exit;
>>    }
>> 
>>    if (xout.slow) {
>> -        ofpbuf_clear(&odp_actions);
>> +        ofpbuf_clear(odp_actions);
>>        compose_slow_path(udpif, &xout, &flow, flow.in_port.odp_port,
>> -                          &odp_actions);
>> -    }
>> -
>> -    if (!ofpbuf_equal(&odp_actions, ukey->actions)) {
>> -        goto exit;
>> +                          odp_actions);
>>    }
>> 
>>    if (odp_flow_key_to_mask(ukey->mask, ukey->mask_len, ukey->key,
>> @@ -1781,18 +1790,24 @@ revalidate_ukey(struct udpif *udpif, struct 
>> udpif_key *ukey,
>>        }
>>    }
>> 
>> -    ok = true;
>> +    if (!ofpbuf_equal(odp_actions, ukey->actions)) {
>> +        /* The datpaath mask was OK, but the actions seem to have changed.
> 
> Typo on “datpaath”
> 
>> +         * Let's modify it in place. */
>> +        result = UKEY_MODIFY;
>> +        goto exit;
>> +    }
>> +
>> +    result = UKEY_KEEP;
>> 
>> exit:
>> -    if (ok) {
>> +    if (result != UKEY_DELETE) {
>>        ukey->reval_seq = reval_seq;
>>    }
>> -    if (netflow && !ok) {
>> +    if (netflow && result != UKEY_DELETE) {
>>        netflow_flow_clear(netflow, &flow);
>>    }
>>    xlate_out_uninit(xoutp);
>> -    ofpbuf_uninit(&odp_actions);
>> -    return ok;
>> +    return result;
>> }
>> 
>> static void
>> @@ -1823,6 +1838,23 @@ delete_op_init(struct udpif *udpif, struct ukey_op 
>> *op, struct udpif_key *ukey)
>> }
>> 
>> static void
>> +modify_op_init(struct ukey_op *op, struct udpif_key *ukey)
>> +{
>> +    op->ukey = ukey;
>> +    op->dop.type = DPIF_OP_FLOW_PUT;
>> +    op->dop.u.flow_put.flags = DPIF_FP_MODIFY;
>> +    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.ufid = &ukey->ufid;
>> +    op->dop.u.flow_put.pmd_id = ukey->pmd_id;
>> +    op->dop.u.flow_put.stats = NULL;
>> +    op->dop.u.flow_put.actions = ukey->actions->data;
>> +    op->dop.u.flow_put.actions_len = ukey->actions->size;
>> +}
>> +
>> +static void
>> push_ukey_ops__(struct udpif *udpif, struct ukey_op *ops, size_t n_ops)
>> {
>>    struct dpif_op *opsp[REVALIDATE_MAX_BATCH];
>> @@ -1841,6 +1873,12 @@ push_ukey_ops__(struct udpif *udpif, struct ukey_op 
>> *ops, size_t n_ops)
>>        stats = op->dop.u.flow_del.stats;
>>        push = &push_buf;
>> 
>> +        if (op->dop.type == DPIF_OP_FLOW_PUT) {
>> +            /* This was an in place modification.  Don't bother pushing 
>> stats
>> +             * for now. */
> 
> As the stats were just pushed in revalidate_ukey()? If so, mention it here.
> 
>> +            continue;
>> +        }
>> +
>>        if (op->ukey) {
>>            ovs_mutex_lock(&op->ukey->mutex);
>>            push->used = MAX(stats->used, op->ukey->stats.used);
>> @@ -1926,6 +1964,9 @@ log_unexpected_flow(const struct dpif_flow *flow, int 
>> error)
>> static void
>> revalidate(struct revalidator *revalidator)
>> {
>> +    uint64_t odp_actions_stub[1024 / 8];
>> +    struct ofpbuf odp_actions = OFPBUF_STUB_INITIALIZER(odp_actions_stub);
>> +
>>    struct udpif *udpif = revalidator->udpif;
>>    struct dpif_flow_dump_thread *dump_thread;
>>    uint64_t dump_seq, reval_seq;
>> @@ -1973,8 +2014,9 @@ revalidate(struct revalidator *revalidator)
>> 
>>        for (f = flows; f < &flows[n_dumped]; f++) {
>>            long long int used = f->stats.used;
>> +            enum reval_result result;
>>            struct udpif_key *ukey;
>> -            bool already_dumped, keep;
>> +            bool already_dumped;
>>            int error;
>> 
>>            if (ukey_acquire(udpif, f, &ukey, &error)) {
>> @@ -2008,15 +2050,20 @@ revalidate(struct revalidator *revalidator)
>>                used = ukey->created;
>>            }
>>            if (kill_them_all || (used && used < now - max_idle)) {
>> -                keep = false;
>> +                result = UKEY_DELETE;
>>            } else {
>> -                keep = revalidate_ukey(udpif, ukey, &f->stats, reval_seq);
>> +                result = revalidate_ukey(udpif, ukey, &f->stats, 
>> &odp_actions,
>> +                                         reval_seq);
>>            }
>>            ukey->dump_seq = dump_seq;
>> -            ukey->flow_exists = keep;
>> +            ukey->flow_exists = result != UKEY_DELETE;
>> 
>> -            if (!keep) {
>> +            if (result == UKEY_DELETE) {
>>                delete_op_init(udpif, &ops[n_ops++], ukey);
>> +            } else if (result == UKEY_MODIFY){
> 
> Add space between “){“
> 
>> +                ofpbuf_delete(ukey->actions);
>> +                ukey->actions = ofpbuf_clone(&odp_actions);
>> +                modify_op_init(&ops[n_ops++], ukey);
>>            }
>>            ovs_mutex_unlock(&ukey->mutex);
>>        }
>> @@ -2027,23 +2074,32 @@ revalidate(struct revalidator *revalidator)
>>        ovsrcu_quiesce();
>>    }
>>    dpif_flow_dump_thread_destroy(dump_thread);
>> +    ofpbuf_uninit(&odp_actions);
>> }
>> 
>> static bool
>> handle_missed_revalidation(struct udpif *udpif, uint64_t reval_seq,
>>                           struct udpif_key *ukey)
>> {
>> +    uint64_t odp_actions_stub[1024 / 8];
>> +    struct ofpbuf odp_actions = OFPBUF_STUB_INITIALIZER(odp_actions_stub);
>> +
>>    struct dpif_flow_stats stats;
>> -    bool keep;
>> +    enum reval_result result;
>> 
>>    COVERAGE_INC(revalidate_missed_dp_flow);
>> 
>>    memset(&stats, 0, sizeof stats);
>>    ovs_mutex_lock(&ukey->mutex);
>> -    keep = revalidate_ukey(udpif, ukey, &stats, reval_seq);
>> +    result = revalidate_ukey(udpif, ukey, &stats, &odp_actions, reval_seq);
>>    ovs_mutex_unlock(&ukey->mutex);
>> 
>> -    return keep;
>> +    ofpbuf_uninit(&odp_actions);
>> +
>> +    /* XXX: We somewhat conversvatively don't bother with in place
>> +     * modifications in this case.  Probably could be made to work, but 
>> this is
>> +     * simpler, and the edge case is rare. */
>> +    return result == UKEY_KEEP;
> 
> There is only one caller of this function, and the call site seems simple 
> enough to support modification, it would be nice if it did. If for nothing 
> else than getting rid of one extra “XXX” (and the typo in the comment at the 
> same time :-).
> 
>> }
>> 
>> static void
>> -- 
>> 2.1.0
>> 
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> http://openvswitch.org/mailman/listinfo/dev
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to