@@ -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+
228286static 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+
81498371int 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+
81598391static struct mlxsw_sp_rif *
81608392mlxsw_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 :
82238464err_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+
91299407int 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
91569443out :
0 commit comments