mirror of
https://github.com/torvalds/linux.git
synced 2024-12-24 03:42:52 +00:00
Merge branch 'mlxsw-Offload-FIFO'
Ido Schimmel says: ==================== mlxsw: Offload FIFO Petr says: If an ETS or PRIO band contains an offloaded qdisc, it is possible to obtain offloaded counters for that band. However, some of the bands will likely simply contain the default invisible FIFO qdisc, which does not present the counters. To remedy this situation, make FIFO offloadable, and offload it by mlxsw when below PRIO and ETS for the sole purpose of providing counters for the bands that do not include other qdiscs. - In patch #1, FIFO is extended to support offloading. - Patches #2 and #3 restructure bits of mlxsw to facilitate the offload logic. - Patch #4 then implements the offload itself. - Patch #5 changes the ETS selftest to use the new counters. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
6349021701
@ -1783,6 +1783,8 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
||||
return mlxsw_sp_setup_tc_ets(mlxsw_sp_port, type_data);
|
||||
case TC_SETUP_QDISC_TBF:
|
||||
return mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, type_data);
|
||||
case TC_SETUP_QDISC_FIFO:
|
||||
return mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, type_data);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -139,6 +139,7 @@ struct mlxsw_sp_port_type_speed_ops;
|
||||
struct mlxsw_sp_ptp_state;
|
||||
struct mlxsw_sp_ptp_ops;
|
||||
struct mlxsw_sp_span_ops;
|
||||
struct mlxsw_sp_qdisc_state;
|
||||
|
||||
struct mlxsw_sp_port_mapping {
|
||||
u8 module;
|
||||
@ -276,8 +277,7 @@ struct mlxsw_sp_port {
|
||||
struct mlxsw_sp_port_sample *sample;
|
||||
struct list_head vlans_list;
|
||||
struct mlxsw_sp_port_vlan *default_vlan;
|
||||
struct mlxsw_sp_qdisc *root_qdisc;
|
||||
struct mlxsw_sp_qdisc *tclass_qdiscs;
|
||||
struct mlxsw_sp_qdisc_state *qdisc;
|
||||
unsigned acl_rule_count;
|
||||
struct mlxsw_sp_acl_block *ing_acl_block;
|
||||
struct mlxsw_sp_acl_block *eg_acl_block;
|
||||
@ -867,6 +867,8 @@ int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct tc_ets_qopt_offload *p);
|
||||
int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct tc_tbf_qopt_offload *p);
|
||||
int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct tc_fifo_qopt_offload *p);
|
||||
|
||||
/* spectrum_fid.c */
|
||||
bool mlxsw_sp_fid_is_dummy(struct mlxsw_sp *mlxsw_sp, u16 fid_index);
|
||||
|
@ -20,14 +20,17 @@ enum mlxsw_sp_qdisc_type {
|
||||
MLXSW_SP_QDISC_PRIO,
|
||||
MLXSW_SP_QDISC_ETS,
|
||||
MLXSW_SP_QDISC_TBF,
|
||||
MLXSW_SP_QDISC_FIFO,
|
||||
};
|
||||
|
||||
struct mlxsw_sp_qdisc;
|
||||
|
||||
struct mlxsw_sp_qdisc_ops {
|
||||
enum mlxsw_sp_qdisc_type type;
|
||||
int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
void *params);
|
||||
int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
|
||||
int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
|
||||
@ -64,6 +67,25 @@ struct mlxsw_sp_qdisc {
|
||||
struct mlxsw_sp_qdisc_ops *ops;
|
||||
};
|
||||
|
||||
struct mlxsw_sp_qdisc_state {
|
||||
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
|
||||
* created first. When notifications for these FIFOs arrive, it is not
|
||||
* known what qdisc their parent handle refers to. It could be a
|
||||
* newly-created PRIO that will replace the currently-offloaded one, or
|
||||
* it could be e.g. a RED that will be attached below it.
|
||||
*
|
||||
* As the notifications start to arrive, use them to note what the
|
||||
* future parent handle is, and keep track of which child FIFOs were
|
||||
* seen. Then when the parent is known, retroactively offload those
|
||||
* FIFOs.
|
||||
*/
|
||||
u32 future_handle;
|
||||
bool future_fifos[IEEE_8021QAZ_MAX_TCS];
|
||||
};
|
||||
|
||||
static bool
|
||||
mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle,
|
||||
enum mlxsw_sp_qdisc_type type)
|
||||
@ -77,36 +99,38 @@ static struct mlxsw_sp_qdisc *
|
||||
mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
|
||||
bool root_only)
|
||||
{
|
||||
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
|
||||
int tclass, child_index;
|
||||
|
||||
if (parent == TC_H_ROOT)
|
||||
return mlxsw_sp_port->root_qdisc;
|
||||
return &qdisc_state->root_qdisc;
|
||||
|
||||
if (root_only || !mlxsw_sp_port->root_qdisc ||
|
||||
!mlxsw_sp_port->root_qdisc->ops ||
|
||||
TC_H_MAJ(parent) != mlxsw_sp_port->root_qdisc->handle ||
|
||||
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;
|
||||
|
||||
child_index = TC_H_MIN(parent);
|
||||
tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
|
||||
return &mlxsw_sp_port->tclass_qdiscs[tclass];
|
||||
return &qdisc_state->tclass_qdiscs[tclass];
|
||||
}
|
||||
|
||||
static struct mlxsw_sp_qdisc *
|
||||
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;
|
||||
int i;
|
||||
|
||||
if (mlxsw_sp_port->root_qdisc->handle == handle)
|
||||
return mlxsw_sp_port->root_qdisc;
|
||||
if (qdisc_state->root_qdisc.handle == handle)
|
||||
return &qdisc_state->root_qdisc;
|
||||
|
||||
if (mlxsw_sp_port->root_qdisc->handle == TC_H_UNSPEC)
|
||||
if (qdisc_state->root_qdisc.handle == TC_H_UNSPEC)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
|
||||
if (mlxsw_sp_port->tclass_qdiscs[i].handle == handle)
|
||||
return &mlxsw_sp_port->tclass_qdiscs[i];
|
||||
if (qdisc_state->tclass_qdiscs[i].handle == handle)
|
||||
return &qdisc_state->tclass_qdiscs[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -147,11 +171,15 @@ mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
||||
if (err)
|
||||
goto err_bad_param;
|
||||
|
||||
err = ops->replace(mlxsw_sp_port, mlxsw_sp_qdisc, params);
|
||||
err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
|
||||
if (err)
|
||||
goto err_config;
|
||||
|
||||
if (mlxsw_sp_qdisc->handle != handle) {
|
||||
/* Check if the Qdisc changed. That includes a situation where an
|
||||
* invisible Qdisc replaces another one, or is being added for the
|
||||
* first time.
|
||||
*/
|
||||
if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) {
|
||||
mlxsw_sp_qdisc->ops = ops;
|
||||
if (ops->clean_stats)
|
||||
ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
|
||||
@ -360,7 +388,8 @@ static int
|
||||
mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
|
||||
{
|
||||
struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_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 -=
|
||||
@ -399,7 +428,7 @@ mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
void *params)
|
||||
{
|
||||
@ -559,7 +588,8 @@ static int
|
||||
mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
|
||||
{
|
||||
struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_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 -=
|
||||
@ -646,7 +676,7 @@ mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
void *params)
|
||||
{
|
||||
@ -734,9 +764,122 @@ int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_qdisc_fifo_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;
|
||||
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 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
void *params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
void *params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
struct tc_qopt_offload_stats *stats_ptr)
|
||||
{
|
||||
mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
|
||||
stats_ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = {
|
||||
.type = MLXSW_SP_QDISC_FIFO,
|
||||
.check_params = mlxsw_sp_qdisc_fifo_check_params,
|
||||
.replace = mlxsw_sp_qdisc_fifo_replace,
|
||||
.destroy = mlxsw_sp_qdisc_fifo_destroy,
|
||||
.get_stats = mlxsw_sp_qdisc_get_fifo_stats,
|
||||
.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
|
||||
};
|
||||
|
||||
int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct tc_fifo_qopt_offload *p)
|
||||
{
|
||||
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
|
||||
int tclass, child_index;
|
||||
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);
|
||||
if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) {
|
||||
parent_handle = TC_H_MAJ(p->parent);
|
||||
if (parent_handle != qdisc_state->future_handle) {
|
||||
/* This notifications is for a different Qdisc than
|
||||
* previously. Wipe the future cache.
|
||||
*/
|
||||
memset(qdisc_state->future_fifos, 0,
|
||||
sizeof(qdisc_state->future_fifos));
|
||||
qdisc_state->future_handle = parent_handle;
|
||||
}
|
||||
|
||||
child_index = TC_H_MIN(p->parent);
|
||||
tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
|
||||
if (tclass < IEEE_8021QAZ_MAX_TCS) {
|
||||
if (p->command == TC_FIFO_REPLACE)
|
||||
qdisc_state->future_fifos[tclass] = true;
|
||||
else if (p->command == TC_FIFO_DESTROY)
|
||||
qdisc_state->future_fifos[tclass] = false;
|
||||
}
|
||||
}
|
||||
if (!mlxsw_sp_qdisc)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (p->command == TC_FIFO_REPLACE) {
|
||||
return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
|
||||
mlxsw_sp_qdisc,
|
||||
&mlxsw_sp_qdisc_ops_fifo, NULL);
|
||||
}
|
||||
|
||||
if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
|
||||
MLXSW_SP_QDISC_FIFO))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (p->command) {
|
||||
case TC_FIFO_DESTROY:
|
||||
if (p->handle == mlxsw_sp_qdisc->handle)
|
||||
return mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
|
||||
mlxsw_sp_qdisc);
|
||||
return 0;
|
||||
case TC_FIFO_STATS:
|
||||
return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
|
||||
&p->stats);
|
||||
case TC_FIFO_REPLACE: /* Handled above. */
|
||||
break;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int
|
||||
__mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port)
|
||||
{
|
||||
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
|
||||
@ -746,8 +889,8 @@ __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port)
|
||||
MLXSW_REG_QEEC_HR_SUBGROUP,
|
||||
i, 0, false, 0);
|
||||
mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
|
||||
&mlxsw_sp_port->tclass_qdiscs[i]);
|
||||
mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0;
|
||||
&qdisc_state->tclass_qdiscs[i]);
|
||||
qdisc_state->tclass_qdiscs[i].prio_bitmap = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -780,12 +923,13 @@ mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
}
|
||||
|
||||
static int
|
||||
__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
||||
unsigned int nbands,
|
||||
const unsigned int *quanta,
|
||||
const unsigned int *weights,
|
||||
const u8 *priomap)
|
||||
{
|
||||
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
|
||||
struct mlxsw_sp_qdisc *child_qdisc;
|
||||
int tclass, i, band, backlog;
|
||||
u8 old_priomap;
|
||||
@ -793,7 +937,7 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
|
||||
for (band = 0; band < nbands; band++) {
|
||||
tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
|
||||
child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
|
||||
child_qdisc = &qdisc_state->tclass_qdiscs[tclass];
|
||||
old_priomap = child_qdisc->prio_bitmap;
|
||||
child_qdisc->prio_bitmap = 0;
|
||||
|
||||
@ -822,28 +966,41 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
child_qdisc);
|
||||
child_qdisc->stats_base.backlog = backlog;
|
||||
}
|
||||
|
||||
if (handle == qdisc_state->future_handle &&
|
||||
qdisc_state->future_fifos[tclass]) {
|
||||
err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC,
|
||||
child_qdisc,
|
||||
&mlxsw_sp_qdisc_ops_fifo,
|
||||
NULL);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
|
||||
tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
|
||||
child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
|
||||
child_qdisc = &qdisc_state->tclass_qdiscs[tclass];
|
||||
child_qdisc->prio_bitmap = 0;
|
||||
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
|
||||
mlxsw_sp_port_ets_set(mlxsw_sp_port,
|
||||
MLXSW_REG_QEEC_HR_SUBGROUP,
|
||||
tclass, 0, false, 0);
|
||||
}
|
||||
|
||||
qdisc_state->future_handle = TC_H_UNSPEC;
|
||||
memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
void *params)
|
||||
{
|
||||
struct tc_prio_qopt_offload_params *p = params;
|
||||
unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0};
|
||||
|
||||
return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, p->bands,
|
||||
return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands,
|
||||
zeroes, zeroes, p->priomap);
|
||||
}
|
||||
|
||||
@ -875,6 +1032,7 @@ mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
struct tc_qopt_offload_stats *stats_ptr)
|
||||
{
|
||||
struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
|
||||
struct mlxsw_sp_qdisc *tc_qdisc;
|
||||
u64 tx_packets = 0;
|
||||
u64 tx_bytes = 0;
|
||||
@ -883,7 +1041,7 @@ mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
|
||||
tc_qdisc = &mlxsw_sp_port->tclass_qdiscs[i];
|
||||
tc_qdisc = &qdisc_state->tclass_qdiscs[i];
|
||||
mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc,
|
||||
&tx_bytes, &tx_packets,
|
||||
&drops, &backlog);
|
||||
@ -941,13 +1099,13 @@ mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
void *params)
|
||||
{
|
||||
struct tc_ets_qopt_offload_replace_params *p = params;
|
||||
|
||||
return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, p->bands,
|
||||
return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands,
|
||||
p->quanta, p->weights, p->priomap);
|
||||
}
|
||||
|
||||
@ -1009,11 +1167,12 @@ __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
||||
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;
|
||||
|
||||
if (band < IEEE_8021QAZ_MAX_TCS &&
|
||||
mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == child_handle)
|
||||
qdisc_state->tclass_qdiscs[tclass_num].handle == child_handle)
|
||||
return 0;
|
||||
|
||||
if (!child_handle) {
|
||||
@ -1032,7 +1191,7 @@ __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
|
||||
|
||||
mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
|
||||
&mlxsw_sp_port->tclass_qdiscs[tclass_num]);
|
||||
&qdisc_state->tclass_qdiscs[tclass_num]);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -1114,37 +1273,23 @@ int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
|
||||
int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
|
||||
{
|
||||
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
|
||||
struct mlxsw_sp_qdisc_state *qdisc_state;
|
||||
int i;
|
||||
|
||||
mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc), GFP_KERNEL);
|
||||
if (!mlxsw_sp_qdisc)
|
||||
goto err_root_qdisc_init;
|
||||
qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL);
|
||||
if (!qdisc_state)
|
||||
return -ENOMEM;
|
||||
|
||||
mlxsw_sp_port->root_qdisc = mlxsw_sp_qdisc;
|
||||
mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff;
|
||||
mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
|
||||
|
||||
mlxsw_sp_qdisc = kcalloc(IEEE_8021QAZ_MAX_TCS,
|
||||
sizeof(*mlxsw_sp_qdisc),
|
||||
GFP_KERNEL);
|
||||
if (!mlxsw_sp_qdisc)
|
||||
goto err_tclass_qdiscs_init;
|
||||
|
||||
mlxsw_sp_port->tclass_qdiscs = mlxsw_sp_qdisc;
|
||||
qdisc_state->root_qdisc.prio_bitmap = 0xff;
|
||||
qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
|
||||
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
|
||||
mlxsw_sp_port->tclass_qdiscs[i].tclass_num = i;
|
||||
qdisc_state->tclass_qdiscs[i].tclass_num = i;
|
||||
|
||||
mlxsw_sp_port->qdisc = qdisc_state;
|
||||
return 0;
|
||||
|
||||
err_tclass_qdiscs_init:
|
||||
kfree(mlxsw_sp_port->root_qdisc);
|
||||
err_root_qdisc_init:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
|
||||
{
|
||||
kfree(mlxsw_sp_port->tclass_qdiscs);
|
||||
kfree(mlxsw_sp_port->root_qdisc);
|
||||
kfree(mlxsw_sp_port->qdisc);
|
||||
}
|
||||
|
@ -853,6 +853,7 @@ enum tc_setup_type {
|
||||
TC_SETUP_FT,
|
||||
TC_SETUP_QDISC_ETS,
|
||||
TC_SETUP_QDISC_TBF,
|
||||
TC_SETUP_QDISC_FIFO,
|
||||
};
|
||||
|
||||
/* These structures hold the attributes of bpf state that are being passed
|
||||
|
@ -881,4 +881,19 @@ struct tc_tbf_qopt_offload {
|
||||
};
|
||||
};
|
||||
|
||||
enum tc_fifo_command {
|
||||
TC_FIFO_REPLACE,
|
||||
TC_FIFO_DESTROY,
|
||||
TC_FIFO_STATS,
|
||||
};
|
||||
|
||||
struct tc_fifo_qopt_offload {
|
||||
enum tc_fifo_command command;
|
||||
u32 handle;
|
||||
u32 parent;
|
||||
union {
|
||||
struct tc_qopt_offload_stats stats;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/pkt_sched.h>
|
||||
#include <net/pkt_cls.h>
|
||||
|
||||
/* 1 band FIFO pseudo-"scheduler" */
|
||||
|
||||
@ -51,8 +52,49 @@ static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
||||
return NET_XMIT_CN;
|
||||
}
|
||||
|
||||
static int fifo_init(struct Qdisc *sch, struct nlattr *opt,
|
||||
struct netlink_ext_ack *extack)
|
||||
static void fifo_offload_init(struct Qdisc *sch)
|
||||
{
|
||||
struct net_device *dev = qdisc_dev(sch);
|
||||
struct tc_fifo_qopt_offload qopt;
|
||||
|
||||
if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
|
||||
return;
|
||||
|
||||
qopt.command = TC_FIFO_REPLACE;
|
||||
qopt.handle = sch->handle;
|
||||
qopt.parent = sch->parent;
|
||||
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt);
|
||||
}
|
||||
|
||||
static void fifo_offload_destroy(struct Qdisc *sch)
|
||||
{
|
||||
struct net_device *dev = qdisc_dev(sch);
|
||||
struct tc_fifo_qopt_offload qopt;
|
||||
|
||||
if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
|
||||
return;
|
||||
|
||||
qopt.command = TC_FIFO_DESTROY;
|
||||
qopt.handle = sch->handle;
|
||||
qopt.parent = sch->parent;
|
||||
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt);
|
||||
}
|
||||
|
||||
static int fifo_offload_dump(struct Qdisc *sch)
|
||||
{
|
||||
struct tc_fifo_qopt_offload qopt;
|
||||
|
||||
qopt.command = TC_FIFO_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_FIFO, &qopt);
|
||||
}
|
||||
|
||||
static int __fifo_init(struct Qdisc *sch, struct nlattr *opt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
bool bypass;
|
||||
bool is_bfifo = sch->ops == &bfifo_qdisc_ops;
|
||||
@ -82,10 +124,35 @@ static int fifo_init(struct Qdisc *sch, struct nlattr *opt,
|
||||
sch->flags |= TCQ_F_CAN_BYPASS;
|
||||
else
|
||||
sch->flags &= ~TCQ_F_CAN_BYPASS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
|
||||
static int fifo_init(struct Qdisc *sch, struct nlattr *opt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __fifo_init(sch, opt, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fifo_offload_init(sch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fifo_hd_init(struct Qdisc *sch, struct nlattr *opt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return __fifo_init(sch, opt, extack);
|
||||
}
|
||||
|
||||
static void fifo_destroy(struct Qdisc *sch)
|
||||
{
|
||||
fifo_offload_destroy(sch);
|
||||
}
|
||||
|
||||
static int __fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
|
||||
{
|
||||
struct tc_fifo_qopt opt = { .limit = sch->limit };
|
||||
|
||||
@ -97,6 +164,22 @@ nla_put_failure:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = fifo_offload_dump(sch);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return __fifo_dump(sch, skb);
|
||||
}
|
||||
|
||||
static int fifo_hd_dump(struct Qdisc *sch, struct sk_buff *skb)
|
||||
{
|
||||
return __fifo_dump(sch, skb);
|
||||
}
|
||||
|
||||
struct Qdisc_ops pfifo_qdisc_ops __read_mostly = {
|
||||
.id = "pfifo",
|
||||
.priv_size = 0,
|
||||
@ -104,6 +187,7 @@ struct Qdisc_ops pfifo_qdisc_ops __read_mostly = {
|
||||
.dequeue = qdisc_dequeue_head,
|
||||
.peek = qdisc_peek_head,
|
||||
.init = fifo_init,
|
||||
.destroy = fifo_destroy,
|
||||
.reset = qdisc_reset_queue,
|
||||
.change = fifo_init,
|
||||
.dump = fifo_dump,
|
||||
@ -118,6 +202,7 @@ struct Qdisc_ops bfifo_qdisc_ops __read_mostly = {
|
||||
.dequeue = qdisc_dequeue_head,
|
||||
.peek = qdisc_peek_head,
|
||||
.init = fifo_init,
|
||||
.destroy = fifo_destroy,
|
||||
.reset = qdisc_reset_queue,
|
||||
.change = fifo_init,
|
||||
.dump = fifo_dump,
|
||||
@ -131,10 +216,10 @@ struct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = {
|
||||
.enqueue = pfifo_tail_enqueue,
|
||||
.dequeue = qdisc_dequeue_head,
|
||||
.peek = qdisc_peek_head,
|
||||
.init = fifo_init,
|
||||
.init = fifo_hd_init,
|
||||
.reset = qdisc_reset_queue,
|
||||
.change = fifo_init,
|
||||
.dump = fifo_dump,
|
||||
.change = fifo_hd_init,
|
||||
.dump = fifo_hd_dump,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -56,11 +56,19 @@ switch_destroy()
|
||||
}
|
||||
|
||||
# Callback from sch_ets_tests.sh
|
||||
get_stats()
|
||||
collect_stats()
|
||||
{
|
||||
local band=$1; shift
|
||||
local -a streams=("$@")
|
||||
local stream
|
||||
|
||||
ethtool_stats_get "$h2" rx_octets_prio_$band
|
||||
# Wait for qdisc counter update so that we don't get it mid-way through.
|
||||
busywait_for_counter 1000 +1 \
|
||||
qdisc_parent_stats_get $swp2 10:$((${streams[0]} + 1)) .bytes \
|
||||
> /dev/null
|
||||
|
||||
for stream in ${streams[@]}; do
|
||||
qdisc_parent_stats_get $swp2 10:$((stream + 1)) .bytes
|
||||
done
|
||||
}
|
||||
|
||||
bail_on_lldpad
|
||||
|
@ -655,6 +655,16 @@ qdisc_stats_get()
|
||||
| jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
|
||||
}
|
||||
|
||||
qdisc_parent_stats_get()
|
||||
{
|
||||
local dev=$1; shift
|
||||
local parent=$1; shift
|
||||
local selector=$1; shift
|
||||
|
||||
tc -j -s qdisc show dev "$dev" invisible \
|
||||
| jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
|
||||
}
|
||||
|
||||
humanize()
|
||||
{
|
||||
local speed=$1; shift
|
||||
|
@ -34,11 +34,14 @@ switch_destroy()
|
||||
}
|
||||
|
||||
# Callback from sch_ets_tests.sh
|
||||
get_stats()
|
||||
collect_stats()
|
||||
{
|
||||
local stream=$1; shift
|
||||
local -a streams=("$@")
|
||||
local stream
|
||||
|
||||
link_stats_get $h2.1$stream rx bytes
|
||||
for stream in ${streams[@]}; do
|
||||
qdisc_parent_stats_get $swp2 10:$((stream + 1)) .bytes
|
||||
done
|
||||
}
|
||||
|
||||
ets_run
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Global interface:
|
||||
# $put -- port under test (e.g. $swp2)
|
||||
# get_stats($band) -- A function to collect stats for band
|
||||
# collect_stats($streams...) -- A function to get stats for individual streams
|
||||
# ets_start_traffic($band) -- Start traffic for this band
|
||||
# ets_change_qdisc($op, $dev, $nstrict, $quanta...) -- Add or change qdisc
|
||||
|
||||
@ -94,15 +94,11 @@ __ets_dwrr_test()
|
||||
|
||||
sleep 10
|
||||
|
||||
t0=($(for stream in ${streams[@]}; do
|
||||
get_stats $stream
|
||||
done))
|
||||
t0=($(collect_stats "${streams[@]}"))
|
||||
|
||||
sleep 10
|
||||
|
||||
t1=($(for stream in ${streams[@]}; do
|
||||
get_stats $stream
|
||||
done))
|
||||
t1=($(collect_stats "${streams[@]}"))
|
||||
d=($(for ((i = 0; i < ${#streams[@]}; i++)); do
|
||||
echo $((${t1[$i]} - ${t0[$i]}))
|
||||
done))
|
||||
|
Loading…
Reference in New Issue
Block a user