net: sch_ets: Make the ETS qdisc offloadable
authorPetr Machata <petrm@mellanox.com>
Wed, 18 Dec 2019 14:55:15 +0000 (14:55 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 18 Dec 2019 21:32:29 +0000 (13:32 -0800)
Add hooks at appropriate points to make it possible to offload the ETS
Qdisc.

Signed-off-by: Petr Machata <petrm@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
include/net/pkt_cls.h
net/sched/sch_ets.c

index 3074506..7a8ed11 100644 (file)
@@ -849,6 +849,7 @@ enum tc_setup_type {
        TC_SETUP_QDISC_GRED,
        TC_SETUP_QDISC_TAPRIO,
        TC_SETUP_FT,
+       TC_SETUP_QDISC_ETS,
 };
 
 /* These structures hold the attributes of bpf state that are being passed
index a7c5d49..47b115e 100644 (file)
@@ -823,4 +823,35 @@ struct tc_root_qopt_offload {
        bool ingress;
 };
 
+enum tc_ets_command {
+       TC_ETS_REPLACE,
+       TC_ETS_DESTROY,
+       TC_ETS_STATS,
+       TC_ETS_GRAFT,
+};
+
+struct tc_ets_qopt_offload_replace_params {
+       unsigned int bands;
+       u8 priomap[TC_PRIO_MAX + 1];
+       unsigned int quanta[TCQ_ETS_MAX_BANDS]; /* 0 for strict bands. */
+       unsigned int weights[TCQ_ETS_MAX_BANDS];
+       struct gnet_stats_queue *qstats;
+};
+
+struct tc_ets_qopt_offload_graft_params {
+       u8 band;
+       u32 child_handle;
+};
+
+struct tc_ets_qopt_offload {
+       enum tc_ets_command command;
+       u32 handle;
+       u32 parent;
+       union {
+               struct tc_ets_qopt_offload_replace_params replace_params;
+               struct tc_qopt_offload_stats stats;
+               struct tc_ets_qopt_offload_graft_params graft_params;
+       };
+};
+
 #endif
index e6194b2..a87e915 100644 (file)
@@ -102,6 +102,91 @@ static u32 ets_class_id(struct Qdisc *sch, const struct ets_class *cl)
        return TC_H_MAKE(sch->handle, band + 1);
 }
 
+static void ets_offload_change(struct Qdisc *sch)
+{
+       struct net_device *dev = qdisc_dev(sch);
+       struct ets_sched *q = qdisc_priv(sch);
+       struct tc_ets_qopt_offload qopt;
+       unsigned int w_psum_prev = 0;
+       unsigned int q_psum = 0;
+       unsigned int q_sum = 0;
+       unsigned int quantum;
+       unsigned int w_psum;
+       unsigned int weight;
+       unsigned int i;
+
+       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
+               return;
+
+       qopt.command = TC_ETS_REPLACE;
+       qopt.handle = sch->handle;
+       qopt.parent = sch->parent;
+       qopt.replace_params.bands = q->nbands;
+       qopt.replace_params.qstats = &sch->qstats;
+       memcpy(&qopt.replace_params.priomap,
+              q->prio2band, sizeof(q->prio2band));
+
+       for (i = 0; i < q->nbands; i++)
+               q_sum += q->classes[i].quantum;
+
+       for (i = 0; i < q->nbands; i++) {
+               quantum = q->classes[i].quantum;
+               q_psum += quantum;
+               w_psum = quantum ? q_psum * 100 / q_sum : 0;
+               weight = w_psum - w_psum_prev;
+               w_psum_prev = w_psum;
+
+               qopt.replace_params.quanta[i] = quantum;
+               qopt.replace_params.weights[i] = weight;
+       }
+
+       dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETS, &qopt);
+}
+
+static void ets_offload_destroy(struct Qdisc *sch)
+{
+       struct net_device *dev = qdisc_dev(sch);
+       struct tc_ets_qopt_offload qopt;
+
+       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
+               return;
+
+       qopt.command = TC_ETS_DESTROY;
+       qopt.handle = sch->handle;
+       qopt.parent = sch->parent;
+       dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETS, &qopt);
+}
+
+static void ets_offload_graft(struct Qdisc *sch, struct Qdisc *new,
+                             struct Qdisc *old, unsigned long arg,
+                             struct netlink_ext_ack *extack)
+{
+       struct net_device *dev = qdisc_dev(sch);
+       struct tc_ets_qopt_offload qopt;
+
+       qopt.command = TC_ETS_GRAFT;
+       qopt.handle = sch->handle;
+       qopt.parent = sch->parent;
+       qopt.graft_params.band = arg - 1;
+       qopt.graft_params.child_handle = new->handle;
+
+       qdisc_offload_graft_helper(dev, sch, new, old, TC_SETUP_QDISC_ETS,
+                                  &qopt, extack);
+}
+
+static int ets_offload_dump(struct Qdisc *sch)
+{
+       struct tc_ets_qopt_offload qopt;
+
+       qopt.command = TC_ETS_STATS;
+       qopt.handle = sch->handle;
+       qopt.parent = sch->parent;
+       qopt.stats.bstats = &sch->bstats;
+       qopt.stats.qstats = &sch->qstats;
+
+       return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_ETS, &qopt);
+}
+
 static bool ets_class_is_strict(struct ets_sched *q, const struct ets_class *cl)
 {
        unsigned int band = cl - q->classes;
@@ -154,6 +239,8 @@ static int ets_class_change(struct Qdisc *sch, u32 classid, u32 parentid,
        sch_tree_lock(sch);
        cl->quantum = quantum;
        sch_tree_unlock(sch);
+
+       ets_offload_change(sch);
        return 0;
 }
 
@@ -173,6 +260,7 @@ static int ets_class_graft(struct Qdisc *sch, unsigned long arg,
        }
 
        *old = qdisc_replace(sch, new, &cl->qdisc);
+       ets_offload_graft(sch, new, *old, arg, extack);
        return 0;
 }
 
@@ -589,6 +677,7 @@ static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt,
 
        sch_tree_unlock(sch);
 
+       ets_offload_change(sch);
        for (i = q->nbands; i < oldbands; i++) {
                qdisc_put(q->classes[i].qdisc);
                memset(&q->classes[i], 0, sizeof(q->classes[i]));
@@ -633,6 +722,7 @@ static void ets_qdisc_destroy(struct Qdisc *sch)
        struct ets_sched *q = qdisc_priv(sch);
        int band;
 
+       ets_offload_destroy(sch);
        tcf_block_put(q->block);
        for (band = 0; band < q->nbands; band++)
                qdisc_put(q->classes[band].qdisc);
@@ -645,6 +735,11 @@ static int ets_qdisc_dump(struct Qdisc *sch, struct sk_buff *skb)
        struct nlattr *nest;
        int band;
        int prio;
+       int err;
+
+       err = ets_offload_dump(sch);
+       if (err)
+               return err;
 
        opts = nla_nest_start_noflag(skb, TCA_OPTIONS);
        if (!opts)