On 16-08-29 06:38 AM, Jamal Hadi Salim wrote:
Let me see if i can
try something. Trickery with RCU is not one of my virtues -
so i may get it wrong but hope you can comment.

Something like attached? Compile tested.
I tried to emulate gact - but greping around I havent
seen sample code which uses  rcu_read_un/lock + synchronize_rcu
this way
[so be gentle if i got it wrong ;->]

I am not sure where Cong is going but probably introducing
a rcu head in each policy will allow to swap and free old one
with call_rcu may be a better approach. The cost may come in
slowing down replace operations.

cheers,
jamal
static int tcf_skbmod_run(struct sk_buff *skb, const struct tc_action *a,
                          struct tcf_result *res)
{
        struct tcf_skbmod *d = to_skbmod(a);
        int action = READ_ONCE(d->tcf_action);
        u64 flags = READ_ONCE(d->flags);

        tcf_lastuse_update(&d->tcf_tm);
        bstats_cpu_update(this_cpu_ptr(d->common.cpu_bstats), skb);
        if (unlikely(action == TC_ACT_SHOT)) {
                qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats));
                return action;
        }

        flags = READ_ONCE(d->flags);
        rcu_read_lock();
        if (flags & SKBMOD_F_DMAC)
                ether_addr_copy(eth_hdr(skb)->h_dest, d->eth_dst);
        if (flags & SKBMOD_F_SMAC)
                ether_addr_copy(eth_hdr(skb)->h_source, d->eth_src);
        if (flags & SKBMOD_F_ETYPE)
                eth_hdr(skb)->h_proto = d->eth_type;
        if (flags & SKBMOD_F_SWAPMAC) {
                u8 tmpaddr[ETH_ALEN];
                /*XXX: I am sure we can come up with something more efficient */
                ether_addr_copy(tmpaddr, eth_hdr(skb)->h_dest);
                ether_addr_copy(eth_hdr(skb)->h_dest, eth_hdr(skb)->h_source);
                ether_addr_copy(eth_hdr(skb)->h_source, tmpaddr);
        }
        rcu_read_unlock();

        return action;
}

static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
                           struct nlattr *est, struct tc_action **a,
                           int ovr, int bind)
{
        struct tc_action_net *tn = net_generic(net, skbmod_net_id);
        struct nlattr *tb[TCA_SKBMOD_MAX + 1];
        struct tc_skbmod *parm;
        struct tcf_skbmod *d;
        u32 lflags = 0;
        u8 *daddr = NULL;
        u8 *saddr = NULL;
        u16 eth_type = 0;

        bool exists = false;
        int ret = 0, err;

        if (nla == NULL)
                return -EINVAL;

        err = nla_parse_nested(tb, TCA_SKBMOD_MAX, nla, skbmod_policy);
        if (err < 0)
                return err;

        if (tb[TCA_SKBMOD_PARMS] == NULL)
                return -EINVAL;

        if (tb[TCA_SKBMOD_DMAC]) {
                daddr = nla_data(tb[TCA_SKBMOD_DMAC]);
                lflags |= SKBMOD_F_DMAC;
        }

        if (tb[TCA_SKBMOD_SMAC]) {
                saddr = nla_data(tb[TCA_SKBMOD_SMAC]);
                lflags |= SKBMOD_F_SMAC;
        }

        if (tb[TCA_SKBMOD_ETYPE]) {
                eth_type = nla_get_u16(tb[TCA_SKBMOD_ETYPE]);
                lflags |= SKBMOD_F_ETYPE;
        }

        parm = nla_data(tb[TCA_SKBMOD_PARMS]);

        if (parm->flags & SKBMOD_F_SWAPMAC)
                lflags = SKBMOD_F_SWAPMAC;

        exists = tcf_hash_check(tn, parm->index, a, bind);
        if (exists && bind)
                return 0;

        if (!lflags) {
                return -EINVAL;
        }

        if (!exists) {
                ret = tcf_hash_create(tn, parm->index, est, a,
                                      &act_skbmod_ops, bind, false);
                if (ret)
                        return ret;

                d = to_skbmod(*a);
                ret = ACT_P_CREATED;
        } else {
                d = to_skbmod(*a);
                tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
        }

        ASSERT_RTNL();
        d->flags = lflags;
        d->tcf_action = parm->action;

        if (ovr)
                spin_lock_bh(&d->tcf_lock);

        if (lflags & SKBMOD_F_DMAC)
                ether_addr_copy(d->eth_dst, daddr);
        if (lflags & SKBMOD_F_SMAC)
                ether_addr_copy(d->eth_src, saddr);
        if (lflags & SKBMOD_F_ETYPE)
                d->eth_type = htons(eth_type);


        if (ovr) {
                spin_unlock_bh(&d->tcf_lock);
                synchronize_rcu();
        }

        if (ret == ACT_P_CREATED)
                tcf_hash_insert(tn, *a);
        return ret;
}

Reply via email to