Skip to content

Commit 8d0f7d3

Browse files
pmachatadavem330
authored andcommitted
mlxsw: Add support for IFLA_OFFLOAD_XSTATS_L3_STATS
Spectrum machines support L3 stats by binding a counter to a RIF, a hardware object representing a router interface. Recognize the netdevice notifier events, NETDEV_OFFLOAD_XSTATS_*, to support enablement, disablement, and reporting back to core. As a netdevice gains a RIF, if L3 stats are enabled, install the counters, and ping the core so that a userspace notification can be emitted. Similarly, as a netdevice loses a RIF, push the as-yet-unreported statistics to the core, so that they are not lost, and ping the core to emit userspace notification. Signed-off-by: Petr Machata <[email protected]> Signed-off-by: Ido Schimmel <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c1de13f commit 8d0f7d3

File tree

2 files changed

+293
-2
lines changed

2 files changed

+293
-2
lines changed

drivers/net/ethernet/mellanox/mlxsw/spectrum.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4829,6 +4829,10 @@ static bool mlxsw_sp_netdevice_event_is_router(unsigned long event)
48294829
case NETDEV_PRE_CHANGEADDR:
48304830
case NETDEV_CHANGEADDR:
48314831
case NETDEV_CHANGEMTU:
4832+
case NETDEV_OFFLOAD_XSTATS_ENABLE:
4833+
case NETDEV_OFFLOAD_XSTATS_DISABLE:
4834+
case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
4835+
case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
48324836
return true;
48334837
default:
48344838
return false;

drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c

Lines changed: 289 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,64 @@ int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
225225
return 0;
226226
}
227227

228+
struct mlxsw_sp_rif_counter_set_basic {
229+
u64 good_unicast_packets;
230+
u64 good_multicast_packets;
231+
u64 good_broadcast_packets;
232+
u64 good_unicast_bytes;
233+
u64 good_multicast_bytes;
234+
u64 good_broadcast_bytes;
235+
u64 error_packets;
236+
u64 discard_packets;
237+
u64 error_bytes;
238+
u64 discard_bytes;
239+
};
240+
241+
static int
242+
mlxsw_sp_rif_counter_fetch_clear(struct mlxsw_sp_rif *rif,
243+
enum mlxsw_sp_rif_counter_dir dir,
244+
struct mlxsw_sp_rif_counter_set_basic *set)
245+
{
246+
struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
247+
char ricnt_pl[MLXSW_REG_RICNT_LEN];
248+
unsigned int *p_counter_index;
249+
int err;
250+
251+
if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
252+
return -EINVAL;
253+
254+
p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
255+
if (!p_counter_index)
256+
return -EINVAL;
257+
258+
mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
259+
MLXSW_REG_RICNT_OPCODE_CLEAR);
260+
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
261+
if (err)
262+
return err;
263+
264+
if (!set)
265+
return 0;
266+
267+
#define MLXSW_SP_RIF_COUNTER_EXTRACT(NAME) \
268+
(set->NAME = mlxsw_reg_ricnt_ ## NAME ## _get(ricnt_pl))
269+
270+
MLXSW_SP_RIF_COUNTER_EXTRACT(good_unicast_packets);
271+
MLXSW_SP_RIF_COUNTER_EXTRACT(good_multicast_packets);
272+
MLXSW_SP_RIF_COUNTER_EXTRACT(good_broadcast_packets);
273+
MLXSW_SP_RIF_COUNTER_EXTRACT(good_unicast_bytes);
274+
MLXSW_SP_RIF_COUNTER_EXTRACT(good_multicast_bytes);
275+
MLXSW_SP_RIF_COUNTER_EXTRACT(good_broadcast_bytes);
276+
MLXSW_SP_RIF_COUNTER_EXTRACT(error_packets);
277+
MLXSW_SP_RIF_COUNTER_EXTRACT(discard_packets);
278+
MLXSW_SP_RIF_COUNTER_EXTRACT(error_bytes);
279+
MLXSW_SP_RIF_COUNTER_EXTRACT(discard_bytes);
280+
281+
#undef MLXSW_SP_RIF_COUNTER_EXTRACT
282+
283+
return 0;
284+
}
285+
228286
static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
229287
unsigned int counter_index)
230288
{
@@ -242,9 +300,13 @@ int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp_rif *rif,
242300
unsigned int *p_counter_index;
243301
int err;
244302

303+
if (mlxsw_sp_rif_counter_valid_get(rif, dir))
304+
return 0;
305+
245306
p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
246307
if (!p_counter_index)
247308
return -EINVAL;
309+
248310
err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
249311
p_counter_index);
250312
if (err)
@@ -8146,6 +8208,166 @@ u16 mlxsw_sp_ipip_lb_ul_rif_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
81468208
return lb_rif->ul_rif_id;
81478209
}
81488210

8211+
static bool
8212+
mlxsw_sp_router_port_l3_stats_enabled(struct mlxsw_sp_rif *rif)
8213+
{
8214+
return mlxsw_sp_rif_counter_valid_get(rif,
8215+
MLXSW_SP_RIF_COUNTER_EGRESS) &&
8216+
mlxsw_sp_rif_counter_valid_get(rif,
8217+
MLXSW_SP_RIF_COUNTER_INGRESS);
8218+
}
8219+
8220+
static int
8221+
mlxsw_sp_router_port_l3_stats_enable(struct mlxsw_sp_rif *rif)
8222+
{
8223+
int err;
8224+
8225+
err = mlxsw_sp_rif_counter_alloc(rif, MLXSW_SP_RIF_COUNTER_INGRESS);
8226+
if (err)
8227+
return err;
8228+
8229+
/* Clear stale data. */
8230+
err = mlxsw_sp_rif_counter_fetch_clear(rif,
8231+
MLXSW_SP_RIF_COUNTER_INGRESS,
8232+
NULL);
8233+
if (err)
8234+
goto err_clear_ingress;
8235+
8236+
err = mlxsw_sp_rif_counter_alloc(rif, MLXSW_SP_RIF_COUNTER_EGRESS);
8237+
if (err)
8238+
goto err_alloc_egress;
8239+
8240+
/* Clear stale data. */
8241+
err = mlxsw_sp_rif_counter_fetch_clear(rif,
8242+
MLXSW_SP_RIF_COUNTER_EGRESS,
8243+
NULL);
8244+
if (err)
8245+
goto err_clear_egress;
8246+
8247+
return 0;
8248+
8249+
err_clear_egress:
8250+
mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_EGRESS);
8251+
err_alloc_egress:
8252+
err_clear_ingress:
8253+
mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_INGRESS);
8254+
return err;
8255+
}
8256+
8257+
static void
8258+
mlxsw_sp_router_port_l3_stats_disable(struct mlxsw_sp_rif *rif)
8259+
{
8260+
mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_EGRESS);
8261+
mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_INGRESS);
8262+
}
8263+
8264+
static void
8265+
mlxsw_sp_router_port_l3_stats_report_used(struct mlxsw_sp_rif *rif,
8266+
struct netdev_notifier_offload_xstats_info *info)
8267+
{
8268+
if (!mlxsw_sp_router_port_l3_stats_enabled(rif))
8269+
return;
8270+
netdev_offload_xstats_report_used(info->report_used);
8271+
}
8272+
8273+
static int
8274+
mlxsw_sp_router_port_l3_stats_fetch(struct mlxsw_sp_rif *rif,
8275+
struct rtnl_hw_stats64 *p_stats)
8276+
{
8277+
struct mlxsw_sp_rif_counter_set_basic ingress;
8278+
struct mlxsw_sp_rif_counter_set_basic egress;
8279+
int err;
8280+
8281+
err = mlxsw_sp_rif_counter_fetch_clear(rif,
8282+
MLXSW_SP_RIF_COUNTER_INGRESS,
8283+
&ingress);
8284+
if (err)
8285+
return err;
8286+
8287+
err = mlxsw_sp_rif_counter_fetch_clear(rif,
8288+
MLXSW_SP_RIF_COUNTER_EGRESS,
8289+
&egress);
8290+
if (err)
8291+
return err;
8292+
8293+
#define MLXSW_SP_ROUTER_ALL_GOOD(SET, SFX) \
8294+
((SET.good_unicast_ ## SFX) + \
8295+
(SET.good_multicast_ ## SFX) + \
8296+
(SET.good_broadcast_ ## SFX))
8297+
8298+
p_stats->rx_packets = MLXSW_SP_ROUTER_ALL_GOOD(ingress, packets);
8299+
p_stats->tx_packets = MLXSW_SP_ROUTER_ALL_GOOD(egress, packets);
8300+
p_stats->rx_bytes = MLXSW_SP_ROUTER_ALL_GOOD(ingress, bytes);
8301+
p_stats->tx_bytes = MLXSW_SP_ROUTER_ALL_GOOD(egress, bytes);
8302+
p_stats->rx_errors = ingress.error_packets;
8303+
p_stats->tx_errors = egress.error_packets;
8304+
p_stats->rx_dropped = ingress.discard_packets;
8305+
p_stats->tx_dropped = egress.discard_packets;
8306+
p_stats->multicast = ingress.good_multicast_packets +
8307+
ingress.good_broadcast_packets;
8308+
8309+
#undef MLXSW_SP_ROUTER_ALL_GOOD
8310+
8311+
return 0;
8312+
}
8313+
8314+
static int
8315+
mlxsw_sp_router_port_l3_stats_report_delta(struct mlxsw_sp_rif *rif,
8316+
struct netdev_notifier_offload_xstats_info *info)
8317+
{
8318+
struct rtnl_hw_stats64 stats = {};
8319+
int err;
8320+
8321+
if (!mlxsw_sp_router_port_l3_stats_enabled(rif))
8322+
return 0;
8323+
8324+
err = mlxsw_sp_router_port_l3_stats_fetch(rif, &stats);
8325+
if (err)
8326+
return err;
8327+
8328+
netdev_offload_xstats_report_delta(info->report_delta, &stats);
8329+
return 0;
8330+
}
8331+
8332+
struct mlxsw_sp_router_hwstats_notify_work {
8333+
struct work_struct work;
8334+
struct net_device *dev;
8335+
};
8336+
8337+
static void mlxsw_sp_router_hwstats_notify_work(struct work_struct *work)
8338+
{
8339+
struct mlxsw_sp_router_hwstats_notify_work *hws_work =
8340+
container_of(work, struct mlxsw_sp_router_hwstats_notify_work,
8341+
work);
8342+
8343+
rtnl_lock();
8344+
rtnl_offload_xstats_notify(hws_work->dev);
8345+
rtnl_unlock();
8346+
dev_put(hws_work->dev);
8347+
kfree(hws_work);
8348+
}
8349+
8350+
static void
8351+
mlxsw_sp_router_hwstats_notify_schedule(struct net_device *dev)
8352+
{
8353+
struct mlxsw_sp_router_hwstats_notify_work *hws_work;
8354+
8355+
/* To collect notification payload, the core ends up sending another
8356+
* notifier block message, which would deadlock on the attempt to
8357+
* acquire the router lock again. Just postpone the notification until
8358+
* later.
8359+
*/
8360+
8361+
hws_work = kzalloc(sizeof(*hws_work), GFP_KERNEL);
8362+
if (!hws_work)
8363+
return;
8364+
8365+
INIT_WORK(&hws_work->work, mlxsw_sp_router_hwstats_notify_work);
8366+
dev_hold(dev);
8367+
hws_work->dev = dev;
8368+
mlxsw_core_schedule_work(&hws_work->work);
8369+
}
8370+
81498371
int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
81508372
{
81518373
return rif->dev->ifindex;
@@ -8156,6 +8378,16 @@ const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif)
81568378
return rif->dev;
81578379
}
81588380

8381+
static void mlxsw_sp_rif_push_l3_stats(struct mlxsw_sp_rif *rif)
8382+
{
8383+
struct rtnl_hw_stats64 stats = {};
8384+
8385+
if (!mlxsw_sp_router_port_l3_stats_fetch(rif, &stats))
8386+
netdev_offload_xstats_push_delta(rif->dev,
8387+
NETDEV_OFFLOAD_XSTATS_TYPE_L3,
8388+
&stats);
8389+
}
8390+
81598391
static struct mlxsw_sp_rif *
81608392
mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
81618393
const struct mlxsw_sp_rif_params *params,
@@ -8216,10 +8448,19 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
82168448
goto err_mr_rif_add;
82178449
}
82188450

8219-
mlxsw_sp_rif_counters_alloc(rif);
8451+
if (netdev_offload_xstats_enabled(rif->dev,
8452+
NETDEV_OFFLOAD_XSTATS_TYPE_L3)) {
8453+
err = mlxsw_sp_router_port_l3_stats_enable(rif);
8454+
if (err)
8455+
goto err_stats_enable;
8456+
mlxsw_sp_router_hwstats_notify_schedule(rif->dev);
8457+
} else {
8458+
mlxsw_sp_rif_counters_alloc(rif);
8459+
}
82208460

82218461
return rif;
82228462

8463+
err_stats_enable:
82238464
err_mr_rif_add:
82248465
for (i--; i >= 0; i--)
82258466
mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);
@@ -8249,7 +8490,15 @@ static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
82498490
mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
82508491
vr = &mlxsw_sp->router->vrs[rif->vr_id];
82518492

8252-
mlxsw_sp_rif_counters_free(rif);
8493+
if (netdev_offload_xstats_enabled(rif->dev,
8494+
NETDEV_OFFLOAD_XSTATS_TYPE_L3)) {
8495+
mlxsw_sp_rif_push_l3_stats(rif);
8496+
mlxsw_sp_router_port_l3_stats_disable(rif);
8497+
mlxsw_sp_router_hwstats_notify_schedule(rif->dev);
8498+
} else {
8499+
mlxsw_sp_rif_counters_free(rif);
8500+
}
8501+
82538502
for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++)
82548503
mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);
82558504
ops->deconfigure(rif);
@@ -9126,6 +9375,35 @@ static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif,
91269375
return -ENOBUFS;
91279376
}
91289377

9378+
static int
9379+
mlxsw_sp_router_port_offload_xstats_cmd(struct mlxsw_sp_rif *rif,
9380+
unsigned long event,
9381+
struct netdev_notifier_offload_xstats_info *info)
9382+
{
9383+
switch (info->type) {
9384+
case NETDEV_OFFLOAD_XSTATS_TYPE_L3:
9385+
break;
9386+
default:
9387+
return 0;
9388+
}
9389+
9390+
switch (event) {
9391+
case NETDEV_OFFLOAD_XSTATS_ENABLE:
9392+
return mlxsw_sp_router_port_l3_stats_enable(rif);
9393+
case NETDEV_OFFLOAD_XSTATS_DISABLE:
9394+
mlxsw_sp_router_port_l3_stats_disable(rif);
9395+
return 0;
9396+
case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
9397+
mlxsw_sp_router_port_l3_stats_report_used(rif, info);
9398+
return 0;
9399+
case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
9400+
return mlxsw_sp_router_port_l3_stats_report_delta(rif, info);
9401+
}
9402+
9403+
WARN_ON_ONCE(1);
9404+
return 0;
9405+
}
9406+
91299407
int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
91309408
unsigned long event, void *ptr)
91319409
{
@@ -9151,6 +9429,15 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
91519429
case NETDEV_PRE_CHANGEADDR:
91529430
err = mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr);
91539431
break;
9432+
case NETDEV_OFFLOAD_XSTATS_ENABLE:
9433+
case NETDEV_OFFLOAD_XSTATS_DISABLE:
9434+
case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
9435+
case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
9436+
err = mlxsw_sp_router_port_offload_xstats_cmd(rif, event, ptr);
9437+
break;
9438+
default:
9439+
WARN_ON_ONCE(1);
9440+
break;
91549441
}
91559442

91569443
out:

0 commit comments

Comments
 (0)