Commit c9ad2057 authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-refactor-qdisc-offload'

Petr Machata says:

====================
mlxsw: Refactor qdisc offload

Currently, mlxsw admits for offload a suitable root qdisc, and its
children. Thus up to two levels of hierarchy are offloaded. Often, this is
enough: one can configure TCs with RED and TCs with a shaper, and can even
see counters for each TC by looking at a qdisc at a sufficiently shallow
position.

While simple, the system has obvious shortcomings. It is not possible to
configure both RED and shaping on one TC. It is not possible to place a
PRIO below root TBF, which would then be offloaded as port shaper. FIFOs
are only offloaded at root or directly below, which is confusing to users,
because RED and TBF of course have their own FIFO.

This patchset is a step towards the end goal of allowing more comprehensive
qdisc tree offload and cleans up the qdisc offload code.

- Patches #1-#4 contain small cleanups.

- Up until now, since mlxsw offloaded only a very simple qdisc
  configurations, basically all bookkeeping was done using one container
  for the root qdisc, and 8 containers for its children. Patches #5, #6, #8
  and #9 gradually introduce a more dynamic structure, where parent-child
  relationships are tracked directly at qdiscs, instead of being implicit.

- This tree management assumes only one qdisc is created at a time. In FIFO
  handlers, this condition was enforced simply by asserting RTNL lock. But
  instead of furthering this RTNL dependence, patch #7 converts the whole
  qdisc offload logic to a per-port mutex.

- Patch #10 adds a selftest.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 790aad0e 0a4d0cb1
...@@ -29,7 +29,6 @@ struct mlxsw_sp_qdisc; ...@@ -29,7 +29,6 @@ struct mlxsw_sp_qdisc;
struct mlxsw_sp_qdisc_ops { struct mlxsw_sp_qdisc_ops {
enum mlxsw_sp_qdisc_type type; enum mlxsw_sp_qdisc_type type;
int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port, int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
void *params); void *params);
int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
...@@ -48,11 +47,14 @@ struct mlxsw_sp_qdisc_ops { ...@@ -48,11 +47,14 @@ struct mlxsw_sp_qdisc_ops {
*/ */
void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port, void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
u32 parent);
unsigned int num_classes;
}; };
struct mlxsw_sp_qdisc { struct mlxsw_sp_qdisc {
u32 handle; u32 handle;
u8 tclass_num; int tclass_num;
u8 prio_bitmap; u8 prio_bitmap;
union { union {
struct red_stats red; struct red_stats red;
...@@ -66,11 +68,13 @@ struct mlxsw_sp_qdisc { ...@@ -66,11 +68,13 @@ struct mlxsw_sp_qdisc {
} stats_base; } stats_base;
struct mlxsw_sp_qdisc_ops *ops; struct mlxsw_sp_qdisc_ops *ops;
struct mlxsw_sp_qdisc *parent;
struct mlxsw_sp_qdisc *qdiscs;
unsigned int num_classes;
}; };
struct mlxsw_sp_qdisc_state { struct mlxsw_sp_qdisc_state {
struct mlxsw_sp_qdisc root_qdisc; struct mlxsw_sp_qdisc root_qdisc;
struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS];
/* When a PRIO or ETS are added, the invisible FIFOs in their bands are /* When a PRIO or ETS are added, the invisible FIFOs in their bands are
* created first. When notifications for these FIFOs arrive, it is not * created first. When notifications for these FIFOs arrive, it is not
...@@ -85,15 +89,55 @@ struct mlxsw_sp_qdisc_state { ...@@ -85,15 +89,55 @@ struct mlxsw_sp_qdisc_state {
*/ */
u32 future_handle; u32 future_handle;
bool future_fifos[IEEE_8021QAZ_MAX_TCS]; bool future_fifos[IEEE_8021QAZ_MAX_TCS];
struct mutex lock; /* Protects qdisc state. */
}; };
static bool static bool
mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle, mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle)
enum mlxsw_sp_qdisc_type type) {
return mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->handle == handle;
}
static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_walk(struct mlxsw_sp_qdisc *qdisc,
struct mlxsw_sp_qdisc *(*pre)(struct mlxsw_sp_qdisc *,
void *),
void *data)
{
struct mlxsw_sp_qdisc *tmp;
unsigned int i;
if (pre) {
tmp = pre(qdisc, data);
if (tmp)
return tmp;
}
if (qdisc->ops) {
for (i = 0; i < qdisc->num_classes; i++) {
tmp = &qdisc->qdiscs[i];
if (qdisc->ops) {
tmp = mlxsw_sp_qdisc_walk(tmp, pre, data);
if (tmp)
return tmp;
}
}
}
return NULL;
}
static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc *qdisc, void *data)
{ {
return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && u32 parent = *(u32 *)data;
mlxsw_sp_qdisc->ops->type == type &&
mlxsw_sp_qdisc->handle == handle; if (qdisc->ops && TC_H_MAJ(qdisc->handle) == TC_H_MAJ(parent)) {
if (qdisc->ops->find_class)
return qdisc->ops->find_class(qdisc, parent);
}
return NULL;
} }
static struct mlxsw_sp_qdisc * static struct mlxsw_sp_qdisc *
...@@ -101,39 +145,46 @@ mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent, ...@@ -101,39 +145,46 @@ mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
bool root_only) bool root_only)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
int tclass, child_index;
if (!qdisc_state)
return NULL;
if (parent == TC_H_ROOT) if (parent == TC_H_ROOT)
return &qdisc_state->root_qdisc; return &qdisc_state->root_qdisc;
if (root_only)
if (root_only || !qdisc_state ||
!qdisc_state->root_qdisc.ops ||
TC_H_MAJ(parent) != qdisc_state->root_qdisc.handle ||
TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS)
return NULL; return NULL;
return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,
mlxsw_sp_qdisc_walk_cb_find, &parent);
}
child_index = TC_H_MIN(parent); static struct mlxsw_sp_qdisc *
tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); mlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc *qdisc, void *data)
return &qdisc_state->tclass_qdiscs[tclass]; {
u32 handle = *(u32 *)data;
if (qdisc->ops && qdisc->handle == handle)
return qdisc;
return NULL;
} }
static struct mlxsw_sp_qdisc * static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle) mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
int i;
if (qdisc_state->root_qdisc.handle == handle)
return &qdisc_state->root_qdisc;
if (qdisc_state->root_qdisc.handle == TC_H_UNSPEC) if (!qdisc_state)
return NULL; return NULL;
return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,
mlxsw_sp_qdisc_walk_cb_find_by_handle,
&handle);
}
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) static void
if (qdisc_state->tclass_qdiscs[i].handle == handle) mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
return &qdisc_state->tclass_qdiscs[i]; {
struct mlxsw_sp_qdisc *tmp;
return NULL; for (tmp = mlxsw_sp_qdisc->parent; tmp; tmp = tmp->parent)
tmp->stats_base.backlog -= mlxsw_sp_qdisc->stats_base.backlog;
} }
static int static int
...@@ -157,32 +208,48 @@ mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -157,32 +208,48 @@ mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
} }
if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy) if (!mlxsw_sp_qdisc->ops)
return 0;
mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc);
if (mlxsw_sp_qdisc->ops->destroy)
err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
mlxsw_sp_qdisc); mlxsw_sp_qdisc);
if (mlxsw_sp_qdisc->ops->clean_stats)
mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
mlxsw_sp_qdisc->handle = TC_H_UNSPEC; mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
mlxsw_sp_qdisc->ops = NULL; mlxsw_sp_qdisc->ops = NULL;
mlxsw_sp_qdisc->num_classes = 0;
kfree(mlxsw_sp_qdisc->qdiscs);
mlxsw_sp_qdisc->qdiscs = NULL;
return err_hdroom ?: err; return err_hdroom ?: err;
} }
static int static int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, u32 handle,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
struct mlxsw_sp_qdisc_ops *ops, void *params) struct mlxsw_sp_qdisc_ops *ops, void *params)
{ {
struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
struct mlxsw_sp_hdroom orig_hdroom; struct mlxsw_sp_hdroom orig_hdroom;
unsigned int i;
int err; int err;
if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) err = ops->check_params(mlxsw_sp_port, params);
/* In case this location contained a different qdisc of the if (err)
* same type we can override the old qdisc configuration. return err;
* Otherwise, we need to remove the old qdisc before setting the
* new one. if (ops->num_classes) {
*/ mlxsw_sp_qdisc->qdiscs = kcalloc(ops->num_classes,
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); sizeof(*mlxsw_sp_qdisc->qdiscs),
GFP_KERNEL);
if (!mlxsw_sp_qdisc->qdiscs)
return -ENOMEM;
for (i = 0; i < ops->num_classes; i++)
mlxsw_sp_qdisc->qdiscs[i].parent = mlxsw_sp_qdisc;
}
orig_hdroom = *mlxsw_sp_port->hdroom; orig_hdroom = *mlxsw_sp_port->hdroom;
if (root_qdisc == mlxsw_sp_qdisc) { if (root_qdisc == mlxsw_sp_qdisc) {
...@@ -198,20 +265,46 @@ mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -198,20 +265,46 @@ mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
goto err_hdroom_configure; goto err_hdroom_configure;
} }
err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params); mlxsw_sp_qdisc->num_classes = ops->num_classes;
mlxsw_sp_qdisc->ops = ops;
mlxsw_sp_qdisc->handle = handle;
err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
if (err)
goto err_replace;
return 0;
err_replace:
mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
mlxsw_sp_qdisc->ops = NULL;
mlxsw_sp_qdisc->num_classes = 0;
mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
err_hdroom_configure:
kfree(mlxsw_sp_qdisc->qdiscs);
mlxsw_sp_qdisc->qdiscs = NULL;
return err;
}
static int
mlxsw_sp_qdisc_change(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params)
{
struct mlxsw_sp_qdisc_ops *ops = mlxsw_sp_qdisc->ops;
int err;
err = ops->check_params(mlxsw_sp_port, params);
if (err) if (err)
goto err_bad_param; goto unoffload;
err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
if (err) if (err)
goto err_config; goto unoffload;
/* Check if the Qdisc changed. That includes a situation where an /* Check if the Qdisc changed. That includes a situation where an
* invisible Qdisc replaces another one, or is being added for the * invisible Qdisc replaces another one, or is being added for the
* first time. * first time.
*/ */
if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) { if (mlxsw_sp_qdisc->handle != handle) {
mlxsw_sp_qdisc->ops = ops;
if (ops->clean_stats) if (ops->clean_stats)
ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
} }
...@@ -219,17 +312,35 @@ mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -219,17 +312,35 @@ mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
mlxsw_sp_qdisc->handle = handle; mlxsw_sp_qdisc->handle = handle;
return 0; return 0;
err_bad_param: unoffload:
err_config: if (ops->unoffload)
mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
err_hdroom_configure:
if (mlxsw_sp_qdisc->handle == handle && ops->unoffload)
ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
return err; return err;
} }
static int
mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
struct mlxsw_sp_qdisc_ops *ops, void *params)
{
if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
/* In case this location contained a different qdisc of the
* same type we can override the old qdisc configuration.
* Otherwise, we need to remove the old qdisc before setting the
* new one.
*/
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
if (!mlxsw_sp_qdisc->ops)
return mlxsw_sp_qdisc_create(mlxsw_sp_port, handle,
mlxsw_sp_qdisc, ops, params);
else
return mlxsw_sp_qdisc_change(mlxsw_sp_port, handle,
mlxsw_sp_qdisc, params);
}
static int static int
mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
...@@ -295,7 +406,7 @@ mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -295,7 +406,7 @@ mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
u64 *p_tx_bytes, u64 *p_tx_packets, u64 *p_tx_bytes, u64 *p_tx_packets,
u64 *p_drops, u64 *p_backlog) u64 *p_drops, u64 *p_backlog)
{ {
u8 tclass_num = mlxsw_sp_qdisc->tclass_num; int tclass_num = mlxsw_sp_qdisc->tclass_num;
struct mlxsw_sp_port_xstats *xstats; struct mlxsw_sp_port_xstats *xstats;
u64 tx_bytes, tx_packets; u64 tx_bytes, tx_packets;
...@@ -395,7 +506,7 @@ static void ...@@ -395,7 +506,7 @@ static void
mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
{ {
u8 tclass_num = mlxsw_sp_qdisc->tclass_num; int tclass_num = mlxsw_sp_qdisc->tclass_num;
struct mlxsw_sp_qdisc_stats *stats_base; struct mlxsw_sp_qdisc_stats *stats_base;
struct mlxsw_sp_port_xstats *xstats; struct mlxsw_sp_port_xstats *xstats;
struct red_stats *red_base; struct red_stats *red_base;
...@@ -421,20 +532,12 @@ static int ...@@ -421,20 +532,12 @@ static int
mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc;
if (root_qdisc != mlxsw_sp_qdisc)
root_qdisc->stats_base.backlog -=
mlxsw_sp_qdisc->stats_base.backlog;
return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
mlxsw_sp_qdisc->tclass_num); mlxsw_sp_qdisc->tclass_num);
} }
static int static int
mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
void *params) void *params)
{ {
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
...@@ -467,7 +570,7 @@ mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -467,7 +570,7 @@ mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
{ {
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct tc_red_qopt_offload_params *p = params; struct tc_red_qopt_offload_params *p = params;
u8 tclass_num = mlxsw_sp_qdisc->tclass_num; int tclass_num = mlxsw_sp_qdisc->tclass_num;
u32 min, max; u32 min, max;
u64 prob; u64 prob;
...@@ -512,7 +615,7 @@ mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -512,7 +615,7 @@ mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
void *xstats_ptr) void *xstats_ptr)
{ {
struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
u8 tclass_num = mlxsw_sp_qdisc->tclass_num; int tclass_num = mlxsw_sp_qdisc->tclass_num;
struct mlxsw_sp_port_xstats *xstats; struct mlxsw_sp_port_xstats *xstats;
struct red_stats *res = xstats_ptr; struct red_stats *res = xstats_ptr;
int early_drops, pdrops; int early_drops, pdrops;
...@@ -536,7 +639,7 @@ mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -536,7 +639,7 @@ mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
struct tc_qopt_offload_stats *stats_ptr) struct tc_qopt_offload_stats *stats_ptr)
{ {
u8 tclass_num = mlxsw_sp_qdisc->tclass_num; int tclass_num = mlxsw_sp_qdisc->tclass_num;
struct mlxsw_sp_qdisc_stats *stats_base; struct mlxsw_sp_qdisc_stats *stats_base;
struct mlxsw_sp_port_xstats *xstats; struct mlxsw_sp_port_xstats *xstats;
u64 overlimits; u64 overlimits;
...@@ -553,6 +656,13 @@ mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -553,6 +656,13 @@ mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
return 0; return 0;
} }
static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
u32 parent)
{
return NULL;
}
#define MLXSW_SP_PORT_DEFAULT_TCLASS 0 #define MLXSW_SP_PORT_DEFAULT_TCLASS 0
static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
...@@ -564,10 +674,11 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { ...@@ -564,10 +674,11 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
.get_stats = mlxsw_sp_qdisc_get_red_stats, .get_stats = mlxsw_sp_qdisc_get_red_stats,
.get_xstats = mlxsw_sp_qdisc_get_red_xstats, .get_xstats = mlxsw_sp_qdisc_get_red_xstats,
.clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
.find_class = mlxsw_sp_qdisc_leaf_find_class,
}; };
int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, static int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_red_qopt_offload *p) struct tc_red_qopt_offload *p)
{ {
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
...@@ -581,8 +692,7 @@ int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -581,8 +692,7 @@ int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
&mlxsw_sp_qdisc_ops_red, &mlxsw_sp_qdisc_ops_red,
&p->set); &p->set);
if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
MLXSW_SP_QDISC_RED))
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (p->command) { switch (p->command) {
...@@ -599,6 +709,18 @@ int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -599,6 +709,18 @@ int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
} }
} }
int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_red_qopt_offload *p)
{
int err;
mutex_lock(&mlxsw_sp_port->qdisc->lock);
err = __mlxsw_sp_setup_tc_red(mlxsw_sp_port, p);
mutex_unlock(&mlxsw_sp_port->qdisc->lock);
return err;
}
static void static void
mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
...@@ -622,13 +744,6 @@ static int ...@@ -622,13 +744,6 @@ static int
mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc;
if (root_qdisc != mlxsw_sp_qdisc)
root_qdisc->stats_base.backlog -=
mlxsw_sp_qdisc->stats_base.backlog;
return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
MLXSW_REG_QEEC_HR_SUBGROUP, MLXSW_REG_QEEC_HR_SUBGROUP,
mlxsw_sp_qdisc->tclass_num, 0, mlxsw_sp_qdisc->tclass_num, 0,
...@@ -678,7 +793,6 @@ mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p) ...@@ -678,7 +793,6 @@ mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p)
static int static int
mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
void *params) void *params)
{ {
struct tc_tbf_qopt_offload_replace_params *p = params; struct tc_tbf_qopt_offload_replace_params *p = params;
...@@ -766,10 +880,11 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = { ...@@ -766,10 +880,11 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = {
.destroy = mlxsw_sp_qdisc_tbf_destroy, .destroy = mlxsw_sp_qdisc_tbf_destroy,
.get_stats = mlxsw_sp_qdisc_get_tbf_stats, .get_stats = mlxsw_sp_qdisc_get_tbf_stats,
.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
.find_class = mlxsw_sp_qdisc_leaf_find_class,
}; };
int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_tbf_qopt_offload *p) struct tc_tbf_qopt_offload *p)
{ {
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
...@@ -783,8 +898,7 @@ int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -783,8 +898,7 @@ int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
&mlxsw_sp_qdisc_ops_tbf, &mlxsw_sp_qdisc_ops_tbf,
&p->replace_params); &p->replace_params);
if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
MLXSW_SP_QDISC_TBF))
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (p->command) { switch (p->command) {
...@@ -798,22 +912,20 @@ int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -798,22 +912,20 @@ int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
} }
} }
static int int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_qdisc_fifo_destroy(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_tbf_qopt_offload *p)
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; int err;
struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc;
if (root_qdisc != mlxsw_sp_qdisc) mutex_lock(&mlxsw_sp_port->qdisc->lock);
root_qdisc->stats_base.backlog -= err = __mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, p);
mlxsw_sp_qdisc->stats_base.backlog; mutex_unlock(&mlxsw_sp_port->qdisc->lock);
return 0;
return err;
} }
static int static int
mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
void *params) void *params)
{ {
return 0; return 0;
...@@ -841,25 +953,18 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = { ...@@ -841,25 +953,18 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = {
.type = MLXSW_SP_QDISC_FIFO, .type = MLXSW_SP_QDISC_FIFO,
.check_params = mlxsw_sp_qdisc_fifo_check_params, .check_params = mlxsw_sp_qdisc_fifo_check_params,
.replace = mlxsw_sp_qdisc_fifo_replace, .replace = mlxsw_sp_qdisc_fifo_replace,
.destroy = mlxsw_sp_qdisc_fifo_destroy,
.get_stats = mlxsw_sp_qdisc_get_fifo_stats, .get_stats = mlxsw_sp_qdisc_get_fifo_stats,
.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
}; };
int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, static int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_fifo_qopt_offload *p) struct tc_fifo_qopt_offload *p)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
int tclass, child_index; unsigned int band;
u32 parent_handle; u32 parent_handle;
/* Invisible FIFOs are tracked in future_handle and future_fifos. Make
* sure that not more than one qdisc is created for a port at a time.
* RTNL is a simple proxy for that.
*/
ASSERT_RTNL();
mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) {
parent_handle = TC_H_MAJ(p->parent); parent_handle = TC_H_MAJ(p->parent);
...@@ -872,13 +977,12 @@ int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -872,13 +977,12 @@ int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
qdisc_state->future_handle = parent_handle; qdisc_state->future_handle = parent_handle;
} }
child_index = TC_H_MIN(p->parent); band = TC_H_MIN(p->parent) - 1;
tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); if (band < IEEE_8021QAZ_MAX_TCS) {
if (tclass < IEEE_8021QAZ_MAX_TCS) {
if (p->command == TC_FIFO_REPLACE) if (p->command == TC_FIFO_REPLACE)
qdisc_state->future_fifos[tclass] = true; qdisc_state->future_fifos[band] = true;
else if (p->command == TC_FIFO_DESTROY) else if (p->command == TC_FIFO_DESTROY)
qdisc_state->future_fifos[tclass] = false; qdisc_state->future_fifos[band] = false;
} }
} }
if (!mlxsw_sp_qdisc) if (!mlxsw_sp_qdisc)
...@@ -890,16 +994,12 @@ int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -890,16 +994,12 @@ int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
&mlxsw_sp_qdisc_ops_fifo, NULL); &mlxsw_sp_qdisc_ops_fifo, NULL);
} }
if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
MLXSW_SP_QDISC_FIFO))
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (p->command) { switch (p->command) {
case TC_FIFO_DESTROY: case TC_FIFO_DESTROY:
if (p->handle == mlxsw_sp_qdisc->handle) return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
return mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
mlxsw_sp_qdisc);
return 0;
case TC_FIFO_STATS: case TC_FIFO_STATS:
return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
&p->stats); &p->stats);
...@@ -910,21 +1010,32 @@ int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -910,21 +1010,32 @@ int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static int int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
__mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port) struct tc_fifo_qopt_offload *p)
{
int err;
mutex_lock(&mlxsw_sp_port->qdisc->lock);
err = __mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, p);
mutex_unlock(&mlxsw_sp_port->qdisc->lock);
return err;
}
static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
int i; int i;
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
MLXSW_SP_PORT_DEFAULT_TCLASS); MLXSW_SP_PORT_DEFAULT_TCLASS);
mlxsw_sp_port_ets_set(mlxsw_sp_port, mlxsw_sp_port_ets_set(mlxsw_sp_port,
MLXSW_REG_QEEC_HR_SUBGROUP, MLXSW_REG_QEEC_HR_SUBGROUP,
i, 0, false, 0); i, 0, false, 0);
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
&qdisc_state->tclass_qdiscs[i]); &mlxsw_sp_qdisc->qdiscs[i]);
qdisc_state->tclass_qdiscs[i].prio_bitmap = 0; mlxsw_sp_qdisc->qdiscs[i].prio_bitmap = 0;
} }
return 0; return 0;
...@@ -934,7 +1045,7 @@ static int ...@@ -934,7 +1045,7 @@ static int
mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
{ {
return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
} }
static int static int
...@@ -948,7 +1059,6 @@ __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands) ...@@ -948,7 +1059,6 @@ __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)
static int static int
mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
void *params) void *params)
{ {
struct tc_prio_qopt_offload_params *p = params; struct tc_prio_qopt_offload_params *p = params;
...@@ -957,8 +1067,9 @@ mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -957,8 +1067,9 @@ mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
} }
static int static int
__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
unsigned int nbands, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
u32 handle, unsigned int nbands,
const unsigned int *quanta, const unsigned int *quanta,
const unsigned int *weights, const unsigned int *weights,
const u8 *priomap) const u8 *priomap)
...@@ -971,7 +1082,7 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -971,7 +1082,7 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
for (band = 0; band < nbands; band++) { for (band = 0; band < nbands; band++) {
tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
old_priomap = child_qdisc->prio_bitmap; old_priomap = child_qdisc->prio_bitmap;
child_qdisc->prio_bitmap = 0; child_qdisc->prio_bitmap = 0;
...@@ -993,6 +1104,9 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -993,6 +1104,9 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
return err; return err;
} }
} }
child_qdisc->tclass_num = tclass;
if (old_priomap != child_qdisc->prio_bitmap && if (old_priomap != child_qdisc->prio_bitmap &&
child_qdisc->ops && child_qdisc->ops->clean_stats) { child_qdisc->ops && child_qdisc->ops->clean_stats) {
backlog = child_qdisc->stats_base.backlog; backlog = child_qdisc->stats_base.backlog;
...@@ -1002,7 +1116,7 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -1002,7 +1116,7 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
} }
if (handle == qdisc_state->future_handle && if (handle == qdisc_state->future_handle &&
qdisc_state->future_fifos[tclass]) { qdisc_state->future_fifos[band]) {
err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC,
child_qdisc, child_qdisc,
&mlxsw_sp_qdisc_ops_fifo, &mlxsw_sp_qdisc_ops_fifo,
...@@ -1013,7 +1127,7 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -1013,7 +1127,7 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
} }
for (; band < IEEE_8021QAZ_MAX_TCS; band++) { for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
child_qdisc->prio_bitmap = 0; child_qdisc->prio_bitmap = 0;
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
mlxsw_sp_port_ets_set(mlxsw_sp_port, mlxsw_sp_port_ets_set(mlxsw_sp_port,
...@@ -1034,8 +1148,9 @@ mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -1034,8 +1148,9 @@ mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
struct tc_prio_qopt_offload_params *p = params; struct tc_prio_qopt_offload_params *p = params;
unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0};
return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
zeroes, zeroes, p->priomap); handle, p->bands, zeroes,
zeroes, p->priomap);
} }
static void static void
...@@ -1066,7 +1181,6 @@ mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1066,7 +1181,6 @@ mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
struct tc_qopt_offload_stats *stats_ptr) struct tc_qopt_offload_stats *stats_ptr)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
struct mlxsw_sp_qdisc *tc_qdisc; struct mlxsw_sp_qdisc *tc_qdisc;
u64 tx_packets = 0; u64 tx_packets = 0;
u64 tx_bytes = 0; u64 tx_bytes = 0;
...@@ -1074,8 +1188,8 @@ mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1074,8 +1188,8 @@ mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
u64 drops = 0; u64 drops = 0;
int i; int i;
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
tc_qdisc = &qdisc_state->tclass_qdiscs[i]; tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i];
mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc, mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc,
&tx_bytes, &tx_packets, &tx_bytes, &tx_packets,
&drops, &backlog); &drops, &backlog);
...@@ -1112,6 +1226,18 @@ mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1112,6 +1226,18 @@ mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_qdisc->stats_base.backlog = 0; mlxsw_sp_qdisc->stats_base.backlog = 0;
} }
static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
u32 parent)
{
int child_index = TC_H_MIN(parent);
int band = child_index - 1;
if (band < 0 || band >= mlxsw_sp_qdisc->num_classes)
return NULL;
return &mlxsw_sp_qdisc->qdiscs[band];
}
static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
.type = MLXSW_SP_QDISC_PRIO, .type = MLXSW_SP_QDISC_PRIO,
.check_params = mlxsw_sp_qdisc_prio_check_params, .check_params = mlxsw_sp_qdisc_prio_check_params,
...@@ -1120,11 +1246,12 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { ...@@ -1120,11 +1246,12 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
.destroy = mlxsw_sp_qdisc_prio_destroy, .destroy = mlxsw_sp_qdisc_prio_destroy,
.get_stats = mlxsw_sp_qdisc_get_prio_stats, .get_stats = mlxsw_sp_qdisc_get_prio_stats,
.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
.find_class = mlxsw_sp_qdisc_prio_find_class,
.num_classes = IEEE_8021QAZ_MAX_TCS,
}; };
static int static int
mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
void *params) void *params)
{ {
struct tc_ets_qopt_offload_replace_params *p = params; struct tc_ets_qopt_offload_replace_params *p = params;
...@@ -1139,8 +1266,9 @@ mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -1139,8 +1266,9 @@ mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
{ {
struct tc_ets_qopt_offload_replace_params *p = params; struct tc_ets_qopt_offload_replace_params *p = params;
return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
p->quanta, p->weights, p->priomap); handle, p->bands, p->quanta,
p->weights, p->priomap);
} }
static void static void
...@@ -1158,7 +1286,7 @@ static int ...@@ -1158,7 +1286,7 @@ static int
mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
{ {
return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
} }
static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {
...@@ -1169,6 +1297,8 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { ...@@ -1169,6 +1297,8 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {
.destroy = mlxsw_sp_qdisc_ets_destroy, .destroy = mlxsw_sp_qdisc_ets_destroy,
.get_stats = mlxsw_sp_qdisc_get_prio_stats, .get_stats = mlxsw_sp_qdisc_get_prio_stats,
.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
.find_class = mlxsw_sp_qdisc_prio_find_class,
.num_classes = IEEE_8021QAZ_MAX_TCS,
}; };
/* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting
...@@ -1201,12 +1331,10 @@ __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1201,12 +1331,10 @@ __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
u8 band, u32 child_handle) u8 band, u32 child_handle)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
struct mlxsw_sp_qdisc *old_qdisc; struct mlxsw_sp_qdisc *old_qdisc;
if (band < IEEE_8021QAZ_MAX_TCS && if (band < mlxsw_sp_qdisc->num_classes &&
qdisc_state->tclass_qdiscs[tclass_num].handle == child_handle) mlxsw_sp_qdisc->qdiscs[band].handle == child_handle)
return 0; return 0;
if (!child_handle) { if (!child_handle) {
...@@ -1224,8 +1352,10 @@ __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1224,8 +1352,10 @@ __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port,
if (old_qdisc) if (old_qdisc)
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc, band);
&qdisc_state->tclass_qdiscs[tclass_num]); if (!WARN_ON(!mlxsw_sp_qdisc))
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -1238,8 +1368,8 @@ mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1238,8 +1368,8 @@ mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
p->band, p->child_handle); p->band, p->child_handle);
} }
int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, static int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_prio_qopt_offload *p) struct tc_prio_qopt_offload *p)
{ {
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
...@@ -1253,8 +1383,7 @@ int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1253,8 +1383,7 @@ int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
&mlxsw_sp_qdisc_ops_prio, &mlxsw_sp_qdisc_ops_prio,
&p->replace_params); &p->replace_params);
if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
MLXSW_SP_QDISC_PRIO))
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (p->command) { switch (p->command) {
...@@ -1271,8 +1400,20 @@ int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1271,8 +1400,20 @@ int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
} }
} }
int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_ets_qopt_offload *p) struct tc_prio_qopt_offload *p)
{
int err;
mutex_lock(&mlxsw_sp_port->qdisc->lock);
err = __mlxsw_sp_setup_tc_prio(mlxsw_sp_port, p);
mutex_unlock(&mlxsw_sp_port->qdisc->lock);
return err;
}
static int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_ets_qopt_offload *p)
{ {
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
...@@ -1286,8 +1427,7 @@ int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1286,8 +1427,7 @@ int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
&mlxsw_sp_qdisc_ops_ets, &mlxsw_sp_qdisc_ops_ets,
&p->replace_params); &p->replace_params);
if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
MLXSW_SP_QDISC_ETS))
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (p->command) { switch (p->command) {
...@@ -1305,6 +1445,18 @@ int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1305,6 +1445,18 @@ int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
} }
} }
int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_ets_qopt_offload *p)
{
int err;
mutex_lock(&mlxsw_sp_port->qdisc->lock);
err = __mlxsw_sp_setup_tc_ets(mlxsw_sp_port, p);
mutex_unlock(&mlxsw_sp_port->qdisc->lock);
return err;
}
struct mlxsw_sp_qevent_block { struct mlxsw_sp_qevent_block {
struct list_head binding_list; struct list_head binding_list;
struct list_head mall_entry_list; struct list_head mall_entry_list;
...@@ -1834,22 +1986,20 @@ int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_por ...@@ -1834,22 +1986,20 @@ int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_por
int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
{ {
struct mlxsw_sp_qdisc_state *qdisc_state; struct mlxsw_sp_qdisc_state *qdisc_state;
int i;
qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL); qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL);
if (!qdisc_state) if (!qdisc_state)
return -ENOMEM; return -ENOMEM;
mutex_init(&qdisc_state->lock);
qdisc_state->root_qdisc.prio_bitmap = 0xff; qdisc_state->root_qdisc.prio_bitmap = 0xff;
qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
qdisc_state->tclass_qdiscs[i].tclass_num = i;
mlxsw_sp_port->qdisc = qdisc_state; mlxsw_sp_port->qdisc = qdisc_state;
return 0; return 0;
} }
void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
{ {
mutex_destroy(&mlxsw_sp_port->qdisc->lock);
kfree(mlxsw_sp_port->qdisc); kfree(mlxsw_sp_port->qdisc);
} }
...@@ -67,6 +67,13 @@ red_test() ...@@ -67,6 +67,13 @@ red_test()
{ {
install_qdisc install_qdisc
# Make sure that we get the non-zero value if there is any.
local cur=$(busywait 1100 until_counter_is "> 0" \
qdisc_stats_get $swp3 10: .backlog)
(( cur == 0 ))
check_err $? "backlog of $cur observed on non-busy qdisc"
log_test "$QDISC backlog properly cleaned"
do_red_test 10 $BACKLOG1 do_red_test 10 $BACKLOG1
do_red_test 11 $BACKLOG2 do_red_test 11 $BACKLOG2
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment