nfp: abm: add support for more threshold actions
authorJakub Kicinski <jakub.kicinski@netronome.com>
Mon, 19 Nov 2018 23:21:50 +0000 (15:21 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 20 Nov 2018 02:53:46 +0000 (18:53 -0800)
Original FW only allowed us to perform ECN marking.  Newer releases
also support plain old drop.  Add the ability to configure drop
policy.  This is particularly useful in combination with GRED,
because different bands can have different ECN marking setting.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: John Hurley <john.hurley@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/abm/ctrl.c
drivers/net/ethernet/netronome/nfp/abm/main.c
drivers/net/ethernet/netronome/nfp/abm/main.h
drivers/net/ethernet/netronome/nfp/abm/qdisc.c

index 2447e935e2d9529ceca5ee88d38c7437254d2d53..ad6c2a621c7a6c508483c37f190175c710dcc2ff 100644 (file)
 
 #define NFP_NUM_PRIOS_SYM_NAME "_abi_pci_dscp_num_prio_%u"
 #define NFP_NUM_BANDS_SYM_NAME "_abi_pci_dscp_num_band_%u"
+#define NFP_ACT_MASK_SYM_NAME  "_abi_nfd_out_q_actions_%u"
 
 #define NFP_QLVL_SYM_NAME      "_abi_nfd_out_q_lvls_%u%s"
 #define NFP_QLVL_STRIDE                16
 #define NFP_QLVL_BLOG_BYTES    0
 #define NFP_QLVL_BLOG_PKTS     4
 #define NFP_QLVL_THRS          8
+#define NFP_QLVL_ACT           12
 
 #define NFP_QMSTAT_SYM_NAME    "_abi_nfdqm%u_stats%s"
 #define NFP_QMSTAT_STRIDE      32
@@ -101,6 +103,39 @@ int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int band,
        return __nfp_abm_ctrl_set_q_lvl(alink->abm, threshold, val);
 }
 
+int __nfp_abm_ctrl_set_q_act(struct nfp_abm *abm, unsigned int id,
+                            enum nfp_abm_q_action act)
+{
+       struct nfp_cpp *cpp = abm->app->cpp;
+       u64 sym_offset;
+       int err;
+
+       if (abm->actions[id] == act)
+               return 0;
+
+       sym_offset = id * NFP_QLVL_STRIDE + NFP_QLVL_ACT;
+       err = __nfp_rtsym_writel(cpp, abm->q_lvls, 4, 0, sym_offset, act);
+       if (err) {
+               nfp_err(cpp,
+                       "RED offload setting action failed on subqueue %d\n",
+                       id);
+               return err;
+       }
+
+       abm->actions[id] = act;
+       return 0;
+}
+
+int nfp_abm_ctrl_set_q_act(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, enum nfp_abm_q_action act)
+{
+       unsigned int qid;
+
+       qid = band * NFP_NET_MAX_RX_RINGS + alink->queue_base + queue;
+
+       return __nfp_abm_ctrl_set_q_act(alink->abm, qid, act);
+}
+
 u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int queue)
 {
        unsigned int band;
@@ -334,6 +369,13 @@ int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm)
                return res;
        abm->num_prios = res;
 
+       /* Read available actions */
+       res = nfp_pf_rtsym_read_optional(pf, NFP_ACT_MASK_SYM_NAME,
+                                        BIT(NFP_ABM_ACT_MARK_DROP));
+       if (res < 0)
+               return res;
+       abm->action_mask = res;
+
        abm->prio_map_len = nfp_abm_ctrl_prio_map_size(abm);
        abm->dscp_mask = GENMASK(7, 8 - order_base_2(abm->num_prios));
 
index ecdef63a20f3679adc8ee5c24cebca7edbfac0e4..7a4d55f794c23cc946766bd802cac188ae9172f9 100644 (file)
@@ -459,15 +459,22 @@ static int nfp_abm_init(struct nfp_app *app)
        for (i = 0; i < abm->num_bands * NFP_NET_MAX_RX_RINGS; i++)
                __nfp_abm_ctrl_set_q_lvl(abm, i, NFP_ABM_LVL_INFINITY);
 
+       abm->actions = kvcalloc(abm->num_thresholds, sizeof(*abm->actions),
+                               GFP_KERNEL);
+       if (!abm->actions)
+               goto err_free_thresh;
+       for (i = 0; i < abm->num_bands * NFP_NET_MAX_RX_RINGS; i++)
+               __nfp_abm_ctrl_set_q_act(abm, i, NFP_ABM_ACT_DROP);
+
        /* We start in legacy mode, make sure advanced queuing is disabled */
        err = nfp_abm_ctrl_qm_disable(abm);
        if (err)
-               goto err_free_thresh;
+               goto err_free_act;
 
        err = -ENOMEM;
        reprs = nfp_reprs_alloc(pf->max_data_vnics);
        if (!reprs)
-               goto err_free_thresh;
+               goto err_free_act;
        RCU_INIT_POINTER(app->reprs[NFP_REPR_TYPE_PHYS_PORT], reprs);
 
        reprs = nfp_reprs_alloc(pf->max_data_vnics);
@@ -479,6 +486,8 @@ static int nfp_abm_init(struct nfp_app *app)
 
 err_free_phys:
        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
+err_free_act:
+       kvfree(abm->actions);
 err_free_thresh:
        kvfree(abm->thresholds);
 err_free_thresh_umap:
@@ -497,6 +506,7 @@ static void nfp_abm_clean(struct nfp_app *app)
        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF);
        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
        bitmap_free(abm->threshold_undef);
+       kvfree(abm->actions);
        kvfree(abm->thresholds);
        kfree(abm);
        app->priv = NULL;
index 9352992ab38601a310426d51068124c722346962..4dcf5881fb4bff8d1e149ad6c9e5494b27a3bf46 100644 (file)
@@ -24,6 +24,17 @@ struct nfp_net;
 #define NFP_ABM_PORTID_TYPE    GENMASK(23, 16)
 #define NFP_ABM_PORTID_ID      GENMASK(7, 0)
 
+/* The possible actions if thresholds are exceeded */
+enum nfp_abm_q_action {
+       /* mark if ECN capable, otherwise drop */
+       NFP_ABM_ACT_MARK_DROP           = 0,
+       /* mark if ECN capable, otherwise goto QM */
+       NFP_ABM_ACT_MARK_QUEUE          = 1,
+       NFP_ABM_ACT_DROP                = 2,
+       NFP_ABM_ACT_QUEUE               = 3,
+       NFP_ABM_ACT_NOQUEUE             = 4,
+};
+
 /**
  * struct nfp_abm - ABM NIC app structure
  * @app:       back pointer to nfp_app
@@ -31,9 +42,11 @@ struct nfp_net;
  *
  * @num_prios: number of supported DSCP priorities
  * @num_bands: number of supported DSCP priority bands
+ * @action_mask:       bitmask of supported actions
  *
  * @thresholds:                current threshold configuration
  * @threshold_undef:   bitmap of thresholds which have not been set
+ * @actions:           current FW action configuration
  * @num_thresholds:    number of @thresholds and bits in @threshold_undef
  *
  * @prio_map_len:      computed length of FW priority map (in bytes)
@@ -52,9 +65,11 @@ struct nfp_abm {
 
        unsigned int num_prios;
        unsigned int num_bands;
+       unsigned int action_mask;
 
        u32 *thresholds;
        unsigned long *threshold_undef;
+       u8 *actions;
        size_t num_thresholds;
 
        unsigned int prio_map_len;
@@ -125,6 +140,7 @@ enum nfp_qdisc_type {
  * @red:               RED Qdisc specific parameters and state
  * @red.num_bands:     Number of valid entries in the @red.band table
  * @red.band:          Per-band array of RED instances
+ * @red.band.ecn:              ECN marking is enabled (rather than drop)
  * @red.band.threshold:                ECN marking threshold
  * @red.band.stats:            current stats of the RED Qdisc
  * @red.band.prev_stats:       previously reported @red.stats
@@ -155,6 +171,7 @@ struct nfp_qdisc {
                        unsigned int num_bands;
 
                        struct {
+                               bool ecn;
                                u32 threshold;
                                struct nfp_alink_stats stats;
                                struct nfp_alink_stats prev_stats;
@@ -208,6 +225,16 @@ static inline bool nfp_abm_has_prio(struct nfp_abm *abm)
        return abm->num_bands > 1;
 }
 
+static inline bool nfp_abm_has_drop(struct nfp_abm *abm)
+{
+       return abm->action_mask & BIT(NFP_ABM_ACT_DROP);
+}
+
+static inline bool nfp_abm_has_mark(struct nfp_abm *abm)
+{
+       return abm->action_mask & BIT(NFP_ABM_ACT_MARK_DROP);
+}
+
 void nfp_abm_qdisc_offload_update(struct nfp_abm_link *alink);
 int nfp_abm_setup_root(struct net_device *netdev, struct nfp_abm_link *alink,
                       struct tc_root_qopt_offload *opt);
@@ -225,6 +252,10 @@ int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm);
 int __nfp_abm_ctrl_set_q_lvl(struct nfp_abm *abm, unsigned int id, u32 val);
 int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int band,
                           unsigned int queue, u32 val);
+int __nfp_abm_ctrl_set_q_act(struct nfp_abm *abm, unsigned int id,
+                            enum nfp_abm_q_action act);
+int nfp_abm_ctrl_set_q_act(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, enum nfp_abm_q_action act);
 int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink,
                              unsigned int band, unsigned int queue,
                              struct nfp_alink_stats *stats);
index 8f6e4366775704a264a9ca870c7f2b6c825f3861..2473fb5f75e5e5d4cac8ffd92e1d3784243c1bbb 100644 (file)
@@ -212,9 +212,15 @@ nfp_abm_offload_compile_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc,
        if (!qdisc->offload_mark)
                return;
 
-       for (i = 0; i < alink->abm->num_bands; i++)
+       for (i = 0; i < alink->abm->num_bands; i++) {
+               enum nfp_abm_q_action act;
+
                nfp_abm_ctrl_set_q_lvl(alink, i, queue,
                                       qdisc->red.band[i].threshold);
+               act = qdisc->red.band[i].ecn ?
+                       NFP_ABM_ACT_MARK_DROP : NFP_ABM_ACT_DROP;
+               nfp_abm_ctrl_set_q_act(alink, i, queue, act);
+       }
 }
 
 static void
@@ -535,11 +541,16 @@ nfp_abm_gred_check_params(struct nfp_abm_link *alink,
 
                if (!band->present)
                        return false;
-               if (!band->is_ecn) {
+               if (!band->is_ecn && !nfp_abm_has_drop(abm)) {
                        nfp_warn(cpp, "GRED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x vq:%d)\n",
                                 opt->parent, opt->handle, i);
                        return false;
                }
+               if (band->is_ecn && !nfp_abm_has_mark(abm)) {
+                       nfp_warn(cpp, "GRED offload failed - ECN marking not supported (p:%08x h:%08x vq:%d)\n",
+                                opt->parent, opt->handle, i);
+                       return false;
+               }
                if (band->is_harddrop) {
                        nfp_warn(cpp, "GRED offload failed - harddrop is not supported (p:%08x h:%08x vq:%d)\n",
                                 opt->parent, opt->handle, i);
@@ -577,8 +588,10 @@ nfp_abm_gred_replace(struct net_device *netdev, struct nfp_abm_link *alink,
        qdisc->params_ok = nfp_abm_gred_check_params(alink, opt);
        if (qdisc->params_ok) {
                qdisc->red.num_bands = opt->set.dp_cnt;
-               for (i = 0; i < qdisc->red.num_bands; i++)
+               for (i = 0; i < qdisc->red.num_bands; i++) {
+                       qdisc->red.band[i].ecn = opt->set.tab[i].is_ecn;
                        qdisc->red.band[i].threshold = opt->set.tab[i].min;
+               }
        }
 
        if (qdisc->use_cnt)
@@ -649,12 +662,18 @@ nfp_abm_red_check_params(struct nfp_abm_link *alink,
                         struct tc_red_qopt_offload *opt)
 {
        struct nfp_cpp *cpp = alink->abm->app->cpp;
+       struct nfp_abm *abm = alink->abm;
 
-       if (!opt->set.is_ecn) {
+       if (!opt->set.is_ecn && !nfp_abm_has_drop(abm)) {
                nfp_warn(cpp, "RED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x)\n",
                         opt->parent, opt->handle);
                return false;
        }
+       if (opt->set.is_ecn && !nfp_abm_has_mark(abm)) {
+               nfp_warn(cpp, "RED offload failed - ECN marking not supported (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
        if (opt->set.is_harddrop) {
                nfp_warn(cpp, "RED offload failed - harddrop is not supported (p:%08x h:%08x)\n",
                         opt->parent, opt->handle);
@@ -703,6 +722,7 @@ nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink,
        qdisc->params_ok = nfp_abm_red_check_params(alink, opt);
        if (qdisc->params_ok) {
                qdisc->red.num_bands = 1;
+               qdisc->red.band[0].ecn = opt->set.is_ecn;
                qdisc->red.band[0].threshold = opt->set.min;
        }