Commit 5cbd9602 authored by Petr Machata's avatar Petr Machata Committed by David S. Miller

mlxsw: spectrum_qdisc: Allocate child qdiscs dynamically

Instead of keeping qdiscs in globally-preallocated arrays, introduce a
per-qdisc-kind value num_classes, and then allocate the necessary child
qdiscs (if any) based on that value. Since now dynamic allocation is
involved, mlxsw_sp_qdisc_replace() gets messy enough that it is worth it to
split it to two cases: a new qdisc allocation and a change of existing
qdisc. (Note that the change also includes what TC formally calls replace,
if the qdisc kind is the same.)
Signed-off-by: default avatarPetr Machata <petrm@nvidia.com>
Reviewed-by: default avatarIdo Schimmel <idosch@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cff99e20
...@@ -49,6 +49,7 @@ struct mlxsw_sp_qdisc_ops { ...@@ -49,6 +49,7 @@ struct mlxsw_sp_qdisc_ops {
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, struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
u32 parent); u32 parent);
unsigned int num_classes;
}; };
struct mlxsw_sp_qdisc { struct mlxsw_sp_qdisc {
...@@ -74,7 +75,6 @@ struct mlxsw_sp_qdisc { ...@@ -74,7 +75,6 @@ struct mlxsw_sp_qdisc {
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
...@@ -215,29 +215,41 @@ mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -215,29 +215,41 @@ mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
if (mlxsw_sp_qdisc->ops->destroy) 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) {
...@@ -253,20 +265,46 @@ mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -253,20 +265,46 @@ mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
goto err_hdroom_configure; goto err_hdroom_configure;
} }
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); 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);
} }
...@@ -274,17 +312,35 @@ mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, ...@@ -274,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,
...@@ -1049,6 +1105,9 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1049,6 +1105,9 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
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;
...@@ -1189,6 +1248,7 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { ...@@ -1189,6 +1248,7 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
.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, .find_class = mlxsw_sp_qdisc_prio_find_class,
.num_classes = IEEE_8021QAZ_MAX_TCS,
}; };
static int static int
...@@ -1239,6 +1299,7 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { ...@@ -1239,6 +1299,7 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {
.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, .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
...@@ -1926,7 +1987,6 @@ int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_por ...@@ -1926,7 +1987,6 @@ 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)
...@@ -1935,15 +1995,6 @@ int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) ...@@ -1935,15 +1995,6 @@ int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
mutex_init(&qdisc_state->lock); 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;
qdisc_state->root_qdisc.qdiscs = qdisc_state->tclass_qdiscs;
qdisc_state->root_qdisc.num_classes = IEEE_8021QAZ_MAX_TCS;
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(i);
qdisc_state->tclass_qdiscs[i].tclass_num = tclass_num;
qdisc_state->tclass_qdiscs[i].parent = &qdisc_state->root_qdisc;
}
mlxsw_sp_port->qdisc = qdisc_state; mlxsw_sp_port->qdisc = qdisc_state;
return 0; return 0;
} }
......
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