#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
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;
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));
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);
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:
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;
#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
*
* @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)
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;
* @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
unsigned int num_bands;
struct {
+ bool ecn;
u32 threshold;
struct nfp_alink_stats stats;
struct nfp_alink_stats prev_stats;
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);
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);
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
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);
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)
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);
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;
}