|
@@ -41,6 +41,138 @@
|
|
#include "spectrum.h"
|
|
#include "spectrum.h"
|
|
#include "reg.h"
|
|
#include "reg.h"
|
|
|
|
|
|
|
|
+enum mlxsw_sp_qdisc_type {
|
|
|
|
+ MLXSW_SP_QDISC_NO_QDISC,
|
|
|
|
+ MLXSW_SP_QDISC_RED,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+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,
|
|
|
|
+ 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);
|
|
|
|
+ int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
|
|
+ struct tc_qopt_offload_stats *stats_ptr);
|
|
|
|
+ int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
|
|
+ void *xstats_ptr);
|
|
|
|
+ void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct mlxsw_sp_qdisc {
|
|
|
|
+ u32 handle;
|
|
|
|
+ u8 tclass_num;
|
|
|
|
+ union {
|
|
|
|
+ struct red_stats red;
|
|
|
|
+ } xstats_base;
|
|
|
|
+ struct mlxsw_sp_qdisc_stats {
|
|
|
|
+ u64 tx_bytes;
|
|
|
|
+ u64 tx_packets;
|
|
|
|
+ u64 drops;
|
|
|
|
+ u64 overlimits;
|
|
|
|
+ } stats_base;
|
|
|
|
+
|
|
|
|
+ struct mlxsw_sp_qdisc_ops *ops;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle,
|
|
|
|
+ enum mlxsw_sp_qdisc_type type)
|
|
|
|
+{
|
|
|
|
+ return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
|
|
|
|
+ mlxsw_sp_qdisc->ops->type == type &&
|
|
|
|
+ mlxsw_sp_qdisc->handle == handle;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
|
|
|
|
+{
|
|
|
|
+ int err = 0;
|
|
|
|
+
|
|
|
|
+ if (!mlxsw_sp_qdisc)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy)
|
|
|
|
+ err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
|
|
|
|
+ mlxsw_sp_qdisc);
|
|
|
|
+
|
|
|
|
+ mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
|
|
|
|
+ mlxsw_sp_qdisc->ops = NULL;
|
|
|
|
+ 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)
|
|
|
|
+{
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params);
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_bad_param;
|
|
|
|
+
|
|
|
|
+ err = ops->replace(mlxsw_sp_port, mlxsw_sp_qdisc, params);
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_config;
|
|
|
|
+
|
|
|
|
+ if (mlxsw_sp_qdisc->handle != handle) {
|
|
|
|
+ mlxsw_sp_qdisc->ops = ops;
|
|
|
|
+ if (ops->clean_stats)
|
|
|
|
+ ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mlxsw_sp_qdisc->handle = handle;
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+err_bad_param:
|
|
|
|
+err_config:
|
|
|
|
+ mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
|
|
+ struct tc_qopt_offload_stats *stats_ptr)
|
|
|
|
+{
|
|
|
|
+ if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
|
|
|
|
+ mlxsw_sp_qdisc->ops->get_stats)
|
|
|
|
+ return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
|
|
|
|
+ mlxsw_sp_qdisc,
|
|
|
|
+ stats_ptr);
|
|
|
|
+
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
|
|
+ void *xstats_ptr)
|
|
|
|
+{
|
|
|
|
+ if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
|
|
|
|
+ mlxsw_sp_qdisc->ops->get_xstats)
|
|
|
|
+ return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
|
|
|
|
+ mlxsw_sp_qdisc,
|
|
|
|
+ xstats_ptr);
|
|
|
|
+
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+}
|
|
|
|
+
|
|
static int
|
|
static int
|
|
mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
int tclass_num, u32 min, u32 max,
|
|
int tclass_num, u32 min, u32 max,
|
|
@@ -79,80 +211,76 @@ mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
static void
|
|
-mlxsw_sp_setup_tc_qdisc_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
- struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
|
|
- int tclass_num)
|
|
|
|
|
|
+mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
|
|
{
|
|
{
|
|
- struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base;
|
|
|
|
|
|
+ u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
|
|
|
|
+ struct mlxsw_sp_qdisc_stats *stats_base;
|
|
struct mlxsw_sp_port_xstats *xstats;
|
|
struct mlxsw_sp_port_xstats *xstats;
|
|
struct rtnl_link_stats64 *stats;
|
|
struct rtnl_link_stats64 *stats;
|
|
|
|
+ struct red_stats *red_base;
|
|
|
|
|
|
xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
|
|
xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
|
|
stats = &mlxsw_sp_port->periodic_hw_stats.stats;
|
|
stats = &mlxsw_sp_port->periodic_hw_stats.stats;
|
|
|
|
+ stats_base = &mlxsw_sp_qdisc->stats_base;
|
|
|
|
+ red_base = &mlxsw_sp_qdisc->xstats_base.red;
|
|
|
|
|
|
- mlxsw_sp_qdisc->tx_packets = stats->tx_packets;
|
|
|
|
- mlxsw_sp_qdisc->tx_bytes = stats->tx_bytes;
|
|
|
|
|
|
+ stats_base->tx_packets = stats->tx_packets;
|
|
|
|
+ stats_base->tx_bytes = stats->tx_bytes;
|
|
|
|
|
|
- switch (mlxsw_sp_qdisc->type) {
|
|
|
|
- case MLXSW_SP_QDISC_RED:
|
|
|
|
- xstats_base->prob_mark = xstats->ecn;
|
|
|
|
- xstats_base->prob_drop = xstats->wred_drop[tclass_num];
|
|
|
|
- xstats_base->pdrop = xstats->tail_drop[tclass_num];
|
|
|
|
|
|
+ red_base->prob_mark = xstats->ecn;
|
|
|
|
+ red_base->prob_drop = xstats->wred_drop[tclass_num];
|
|
|
|
+ red_base->pdrop = xstats->tail_drop[tclass_num];
|
|
|
|
|
|
- mlxsw_sp_qdisc->overlimits = xstats_base->prob_drop +
|
|
|
|
- xstats_base->prob_mark;
|
|
|
|
- mlxsw_sp_qdisc->drops = xstats_base->prob_drop +
|
|
|
|
- xstats_base->pdrop;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
|
|
+ stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
|
|
|
|
+ stats_base->drops = red_base->prob_drop + red_base->pdrop;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
static int
|
|
-mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
|
|
|
- struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
|
|
- int tclass_num)
|
|
|
|
|
|
+mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
|
|
{
|
|
{
|
|
- int err;
|
|
|
|
-
|
|
|
|
- if (mlxsw_sp_qdisc->handle != handle)
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
- err = mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, tclass_num);
|
|
|
|
- mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
|
|
|
|
- mlxsw_sp_qdisc->type = MLXSW_SP_QDISC_NO_QDISC;
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
|
|
+ return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
|
|
|
|
+ mlxsw_sp_qdisc->tclass_num);
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
static int
|
|
-mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
|
|
|
- struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
|
|
- int tclass_num,
|
|
|
|
- struct tc_red_qopt_offload_params *p)
|
|
|
|
|
|
+mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
|
|
+ void *params)
|
|
{
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
- u32 min, max;
|
|
|
|
- u64 prob;
|
|
|
|
- int err = 0;
|
|
|
|
|
|
+ struct tc_red_qopt_offload_params *p = params;
|
|
|
|
|
|
if (p->min > p->max) {
|
|
if (p->min > p->max) {
|
|
dev_err(mlxsw_sp->bus_info->dev,
|
|
dev_err(mlxsw_sp->bus_info->dev,
|
|
"spectrum: RED: min %u is bigger then max %u\n", p->min,
|
|
"spectrum: RED: min %u is bigger then max %u\n", p->min,
|
|
p->max);
|
|
p->max);
|
|
- goto err_bad_param;
|
|
|
|
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) {
|
|
if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) {
|
|
dev_err(mlxsw_sp->bus_info->dev,
|
|
dev_err(mlxsw_sp->bus_info->dev,
|
|
"spectrum: RED: max value %u is too big\n", p->max);
|
|
"spectrum: RED: max value %u is too big\n", p->max);
|
|
- goto err_bad_param;
|
|
|
|
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
if (p->min == 0 || p->max == 0) {
|
|
if (p->min == 0 || p->max == 0) {
|
|
dev_err(mlxsw_sp->bus_info->dev,
|
|
dev_err(mlxsw_sp->bus_info->dev,
|
|
"spectrum: RED: 0 value is illegal for min and max\n");
|
|
"spectrum: RED: 0 value is illegal for min and max\n");
|
|
- goto err_bad_param;
|
|
|
|
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
|
|
+ void *params)
|
|
|
|
+{
|
|
|
|
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
|
+ struct tc_red_qopt_offload_params *p = params;
|
|
|
|
+ u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
|
|
|
|
+ u32 min, max;
|
|
|
|
+ u64 prob;
|
|
|
|
|
|
/* calculate probability in percentage */
|
|
/* calculate probability in percentage */
|
|
prob = p->probability;
|
|
prob = p->probability;
|
|
@@ -161,116 +289,132 @@ mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
|
prob = DIV_ROUND_UP(prob, 1 << 16);
|
|
prob = DIV_ROUND_UP(prob, 1 << 16);
|
|
min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
|
|
min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
|
|
max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
|
|
max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
|
|
- err = mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min,
|
|
|
|
- max, prob, p->is_ecn);
|
|
|
|
- if (err)
|
|
|
|
- goto err_config;
|
|
|
|
-
|
|
|
|
- mlxsw_sp_qdisc->type = MLXSW_SP_QDISC_RED;
|
|
|
|
- if (mlxsw_sp_qdisc->handle != handle)
|
|
|
|
- mlxsw_sp_setup_tc_qdisc_clean_stats(mlxsw_sp_port,
|
|
|
|
- mlxsw_sp_qdisc,
|
|
|
|
- tclass_num);
|
|
|
|
-
|
|
|
|
- mlxsw_sp_qdisc->handle = handle;
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
-err_bad_param:
|
|
|
|
- err = -EINVAL;
|
|
|
|
-err_config:
|
|
|
|
- mlxsw_sp_qdisc_red_destroy(mlxsw_sp_port, mlxsw_sp_qdisc->handle,
|
|
|
|
- mlxsw_sp_qdisc, tclass_num);
|
|
|
|
- return err;
|
|
|
|
|
|
+ return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min,
|
|
|
|
+ max, prob, p->is_ecn);
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
static int
|
|
-mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
|
|
|
|
|
+mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
|
|
- int tclass_num, struct red_stats *res)
|
|
|
|
|
|
+ void *xstats_ptr)
|
|
{
|
|
{
|
|
- struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base;
|
|
|
|
|
|
+ struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
|
|
|
|
+ u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
|
|
struct mlxsw_sp_port_xstats *xstats;
|
|
struct mlxsw_sp_port_xstats *xstats;
|
|
-
|
|
|
|
- if (mlxsw_sp_qdisc->handle != handle ||
|
|
|
|
- mlxsw_sp_qdisc->type != MLXSW_SP_QDISC_RED)
|
|
|
|
- return -EOPNOTSUPP;
|
|
|
|
|
|
+ struct red_stats *res = xstats_ptr;
|
|
|
|
+ int early_drops, marks, pdrops;
|
|
|
|
|
|
xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
|
|
xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
|
|
|
|
|
|
- res->prob_drop = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
|
|
|
|
- res->prob_mark = xstats->ecn - xstats_base->prob_mark;
|
|
|
|
- res->pdrop = xstats->tail_drop[tclass_num] - xstats_base->pdrop;
|
|
|
|
|
|
+ early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
|
|
|
|
+ marks = xstats->ecn - xstats_base->prob_mark;
|
|
|
|
+ pdrops = xstats->tail_drop[tclass_num] - xstats_base->pdrop;
|
|
|
|
+
|
|
|
|
+ res->pdrop += pdrops;
|
|
|
|
+ res->prob_drop += early_drops;
|
|
|
|
+ res->prob_mark += marks;
|
|
|
|
+
|
|
|
|
+ xstats_base->pdrop += pdrops;
|
|
|
|
+ xstats_base->prob_drop += early_drops;
|
|
|
|
+ xstats_base->prob_mark += marks;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
static int
|
|
-mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
|
|
|
|
|
|
+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,
|
|
- int tclass_num,
|
|
|
|
- struct tc_red_qopt_offload_stats *res)
|
|
|
|
|
|
+ struct tc_qopt_offload_stats *stats_ptr)
|
|
{
|
|
{
|
|
u64 tx_bytes, tx_packets, overlimits, drops;
|
|
u64 tx_bytes, tx_packets, overlimits, drops;
|
|
|
|
+ u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
|
|
|
|
+ struct mlxsw_sp_qdisc_stats *stats_base;
|
|
struct mlxsw_sp_port_xstats *xstats;
|
|
struct mlxsw_sp_port_xstats *xstats;
|
|
struct rtnl_link_stats64 *stats;
|
|
struct rtnl_link_stats64 *stats;
|
|
|
|
|
|
- if (mlxsw_sp_qdisc->handle != handle ||
|
|
|
|
- mlxsw_sp_qdisc->type != MLXSW_SP_QDISC_RED)
|
|
|
|
- return -EOPNOTSUPP;
|
|
|
|
-
|
|
|
|
xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
|
|
xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
|
|
stats = &mlxsw_sp_port->periodic_hw_stats.stats;
|
|
stats = &mlxsw_sp_port->periodic_hw_stats.stats;
|
|
|
|
+ stats_base = &mlxsw_sp_qdisc->stats_base;
|
|
|
|
|
|
- tx_bytes = stats->tx_bytes - mlxsw_sp_qdisc->tx_bytes;
|
|
|
|
- tx_packets = stats->tx_packets - mlxsw_sp_qdisc->tx_packets;
|
|
|
|
|
|
+ tx_bytes = stats->tx_bytes - stats_base->tx_bytes;
|
|
|
|
+ tx_packets = stats->tx_packets - stats_base->tx_packets;
|
|
overlimits = xstats->wred_drop[tclass_num] + xstats->ecn -
|
|
overlimits = xstats->wred_drop[tclass_num] + xstats->ecn -
|
|
- mlxsw_sp_qdisc->overlimits;
|
|
|
|
|
|
+ stats_base->overlimits;
|
|
drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] -
|
|
drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] -
|
|
- mlxsw_sp_qdisc->drops;
|
|
|
|
-
|
|
|
|
- _bstats_update(res->bstats, tx_bytes, tx_packets);
|
|
|
|
- res->qstats->overlimits += overlimits;
|
|
|
|
- res->qstats->drops += drops;
|
|
|
|
- res->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
|
|
|
|
- xstats->backlog[tclass_num]);
|
|
|
|
-
|
|
|
|
- mlxsw_sp_qdisc->drops += drops;
|
|
|
|
- mlxsw_sp_qdisc->overlimits += overlimits;
|
|
|
|
- mlxsw_sp_qdisc->tx_bytes += tx_bytes;
|
|
|
|
- mlxsw_sp_qdisc->tx_packets += tx_packets;
|
|
|
|
|
|
+ stats_base->drops;
|
|
|
|
+
|
|
|
|
+ _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
|
|
|
|
+ stats_ptr->qstats->overlimits += overlimits;
|
|
|
|
+ stats_ptr->qstats->drops += drops;
|
|
|
|
+ stats_ptr->qstats->backlog +=
|
|
|
|
+ mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
|
|
|
|
+ xstats->backlog[tclass_num]);
|
|
|
|
+
|
|
|
|
+ stats_base->drops += drops;
|
|
|
|
+ stats_base->overlimits += overlimits;
|
|
|
|
+ stats_base->tx_bytes += tx_bytes;
|
|
|
|
+ stats_base->tx_packets += tx_packets;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
#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 = {
|
|
|
|
+ .type = MLXSW_SP_QDISC_RED,
|
|
|
|
+ .check_params = mlxsw_sp_qdisc_red_check_params,
|
|
|
|
+ .replace = mlxsw_sp_qdisc_red_replace,
|
|
|
|
+ .destroy = mlxsw_sp_qdisc_red_destroy,
|
|
|
|
+ .get_stats = mlxsw_sp_qdisc_get_red_stats,
|
|
|
|
+ .get_xstats = mlxsw_sp_qdisc_get_red_xstats,
|
|
|
|
+ .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
|
|
|
|
+};
|
|
|
|
+
|
|
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)
|
|
struct tc_red_qopt_offload *p)
|
|
{
|
|
{
|
|
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
|
|
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
|
|
- int tclass_num;
|
|
|
|
|
|
|
|
if (p->parent != TC_H_ROOT)
|
|
if (p->parent != TC_H_ROOT)
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
- mlxsw_sp_qdisc = &mlxsw_sp_port->root_qdisc;
|
|
|
|
- tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
|
|
|
|
|
|
+ mlxsw_sp_qdisc = mlxsw_sp_port->root_qdisc;
|
|
|
|
+
|
|
|
|
+ if (p->command == TC_RED_REPLACE)
|
|
|
|
+ return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
|
|
|
|
+ mlxsw_sp_qdisc,
|
|
|
|
+ &mlxsw_sp_qdisc_ops_red,
|
|
|
|
+ &p->set);
|
|
|
|
+
|
|
|
|
+ if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
|
|
|
|
+ MLXSW_SP_QDISC_RED))
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
|
|
switch (p->command) {
|
|
switch (p->command) {
|
|
- case TC_RED_REPLACE:
|
|
|
|
- return mlxsw_sp_qdisc_red_replace(mlxsw_sp_port, p->handle,
|
|
|
|
- mlxsw_sp_qdisc, tclass_num,
|
|
|
|
- &p->set);
|
|
|
|
case TC_RED_DESTROY:
|
|
case TC_RED_DESTROY:
|
|
- return mlxsw_sp_qdisc_red_destroy(mlxsw_sp_port, p->handle,
|
|
|
|
- mlxsw_sp_qdisc, tclass_num);
|
|
|
|
|
|
+ return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
|
|
case TC_RED_XSTATS:
|
|
case TC_RED_XSTATS:
|
|
- return mlxsw_sp_qdisc_get_red_xstats(mlxsw_sp_port, p->handle,
|
|
|
|
- mlxsw_sp_qdisc, tclass_num,
|
|
|
|
- p->xstats);
|
|
|
|
|
|
+ return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
|
|
|
|
+ p->xstats);
|
|
case TC_RED_STATS:
|
|
case TC_RED_STATS:
|
|
- return mlxsw_sp_qdisc_get_red_stats(mlxsw_sp_port, p->handle,
|
|
|
|
- mlxsw_sp_qdisc, tclass_num,
|
|
|
|
- &p->stats);
|
|
|
|
|
|
+ return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
|
|
|
|
+ &p->stats);
|
|
default:
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
|
|
|
|
+{
|
|
|
|
+ mlxsw_sp_port->root_qdisc = kzalloc(sizeof(*mlxsw_sp_port->root_qdisc),
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ if (!mlxsw_sp_port->root_qdisc)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
|
|
|
|
+{
|
|
|
|
+ kfree(mlxsw_sp_port->root_qdisc);
|
|
|
|
+}
|