Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pmachata committed Oct 29, 2021
1 parent a8a72b0 commit 453f3cd
Show file tree
Hide file tree
Showing 5 changed files with 320 additions and 36 deletions.
37 changes: 31 additions & 6 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
Expand Up @@ -8925,18 +8925,43 @@ static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif,
info->dev_addr, extack);
}

static int mlxsw_sp_router_port_offload_xstats_get(struct mlxsw_sp_rif *rif,
static int
mlxsw_sp_router_port_offload_xstats_report_delta(struct mlxsw_sp_rif *rif,
struct netdev_notifier_offload_xstats_info *info)
{
struct rtnl_link_stats64 stats = {
.rx_packets = 1234,
};

netdev_offload_xstats_report_delta(info->report_delta, &stats,
NETDEV_HW_STATS_TYPE_IMMEDIATE);
return 0;
}

static int mlxsw_sp_router_port_offload_xstats_cmd(struct mlxsw_sp_rif *rif,
struct netdev_notifier_offload_xstats_info *info)
{
int err = 0;

switch (info->cmd) {
case NETDEV_OFFLOAD_XSTATS_CMD_ENABLE:
printk(KERN_WARNING "xstats enable %s\n", rif->dev->name);
break;
case NETDEV_OFFLOAD_XSTATS_CMD_DISABLE:
printk(KERN_WARNING "xstats disable %s\n", rif->dev->name);
break;
case NETDEV_OFFLOAD_XSTATS_CMD_REPORT_DELTA:
printk(KERN_WARNING "xstats report delta %s\n", rif->dev->name);
err = mlxsw_sp_router_port_offload_xstats_report_delta(rif,
info);
break;
}

switch (info->attr_id) {
case IFLA_OFFLOAD_XSTATS_HW_STATS:
printk(KERN_WARNING "mlxsw_sp_router_port_offload_xstats_get %s\n",
rif->dev->name);
info->handled = true;
if (info->stats)
info->stats->rx_packets += 1234;
info->stats->rx_packets += 1234;
break;
}

Expand Down Expand Up @@ -8967,8 +8992,8 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
case NETDEV_PRE_CHANGEADDR:
err = mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr);
break;
case NETDEV_OFFLOAD_XSTATS_GET:
err = mlxsw_sp_router_port_offload_xstats_get(rif, ptr);
case NETDEV_OFFLOAD_XSTATS_CMD:
err = mlxsw_sp_router_port_offload_xstats_cmd(rif, ptr);
break;
}

Expand Down
54 changes: 43 additions & 11 deletions include/linux/netdevice.h
Expand Up @@ -2266,6 +2266,7 @@ struct net_device {

/* protected by rtnl_lock */
struct bpf_xdp_entity xdp_state[__MAX_XDP_MODE];
struct rtnl_link_stats64 *offload_hw_stats;
};
#define to_net_dev(d) container_of(d, struct net_device, dev)

Expand Down Expand Up @@ -2809,7 +2810,7 @@ enum netdev_cmd {
NETDEV_CVLAN_FILTER_DROP_INFO,
NETDEV_SVLAN_FILTER_PUSH_INFO,
NETDEV_SVLAN_FILTER_DROP_INFO,
NETDEV_OFFLOAD_XSTATS_GET,
NETDEV_OFFLOAD_XSTATS_CMD,
};
const char *netdev_cmd_to_name(enum netdev_cmd cmd);

Expand Down Expand Up @@ -2860,22 +2861,53 @@ struct netdev_notifier_pre_changeaddr_info {
const unsigned char *dev_addr;
};

// xxx naming. Are these types for hw_stats, or generally for offloaded stats?
#define NETDEV_HW_STATS_TYPES(X) \
X(IMMEDIATE)

#define NETDEV_HW_STATS_EXPAND_BITN(T) \
NETDEV_HW_STATS_BITN_ ## T,
enum {
NETDEV_HW_STATS_TYPES(NETDEV_HW_STATS_EXPAND_BITN)
};
#undef NETDEV_HW_STATS_EXPAND_BITN

#define NETDEV_HW_STATS_EXPAND_TYPE(T) \
NETDEV_HW_STATS_TYPE_ ## T = BIT(NETDEV_HW_STATS_BITN_ ## T),
enum netdev_hw_stats_type {
NETDEV_HW_STATS_TYPES(NETDEV_HW_STATS_EXPAND_TYPE)
};
#undef NETDEV_HW_STATS_EXPAND_TYPE

enum netdev_offload_xstats_cmd {
NETDEV_OFFLOAD_XSTATS_CMD_ENABLE,
NETDEV_OFFLOAD_XSTATS_CMD_DISABLE,
NETDEV_OFFLOAD_XSTATS_CMD_REPORT_DELTA,
};

struct netdev_notifier_offload_xstats_info {
struct netdev_notifier_info info; /* must be first */
int attr_id;
enum netdev_offload_xstats_cmd cmd;

/* `stats' is NULL for discovery whether there are any offload xstats
* available. For stat collection, it points to a valid object.
*/
struct rtnl_link_stats64 *stats;
union {
/* For ENABLE. A mask of enum netdev_hw_stats_type. */
enum netdev_hw_stats_type hw_stats;

bool handled;
/* For REPORT_DELTA. */
struct netdev_notifier_offload_xstats_rd *report_delta;
};
};

bool netdev_offload_xstats_has(struct net_device *dev, int attr_id);
int netdev_offload_xstats_get(struct net_device *dev, int attr_id,
struct rtnl_link_stats64 *stats,
struct netlink_ext_ack *extack);
int netdev_offload_xstats_hw_stats_enable(struct net_device *dev,
enum netdev_hw_stats_type hw_stats,
struct netlink_ext_ack *extack);
void netdev_offload_xstats_hw_stats_disable(struct net_device *dev);
int netdev_offload_xstats_hw_stats_get(struct net_device *dev,
struct rtnl_link_stats64 *stats,
struct netlink_ext_ack *extack);
void netdev_offload_xstats_report_delta(struct netdev_notifier_offload_xstats_rd *report_delta,
const struct rtnl_link_stats64 *stats,
enum netdev_hw_stats_type hw_stats);

static inline void netdev_notifier_info_init(struct netdev_notifier_info *info,
struct net_device *dev)
Expand Down
19 changes: 19 additions & 0 deletions include/uapi/linux/if_link.h
Expand Up @@ -1145,6 +1145,16 @@ enum {
IFLA_STATS_LINK_64,
IFLA_STATS_LINK_XSTATS,
IFLA_STATS_LINK_XSTATS_SLAVE,
// filter_mask above instructs the kernel to only collect stats that are
// interesting to the callee. However the filtering as made by the
// attribute. For offload_xstats, we have a nest of attributes and need
// a different filtering mechanism.
//
// So if (filter_mask & IFLA_STATS_FILTER_BIT(OFFLOAD_XSTATS)),
// the attribute may be present and be used for filtering.
//
// For rtnl_stats_set, the attribute will be used in the same way to
// toggle the use of offloaded xstats.
IFLA_STATS_LINK_OFFLOAD_XSTATS,
IFLA_STATS_AF_SPEC,
__IFLA_STATS_MAX,
Expand Down Expand Up @@ -1176,6 +1186,15 @@ enum {
};
#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1)

/* HW stats type. This is modeled after TCA_ACT_HW_STATS_*. For details, please
* see pkt_cls.h.
*/
#define IFLA_HW_STATS_IMMEDIATE (1U << 0)
/* When adding new entries to this list, please consult pkt_cls.h.
* Same-named enumerators should have the same semantics and the same
* value.
*/

/* XDP section */

#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0)
Expand Down
142 changes: 125 additions & 17 deletions net/core/dev.c
Expand Up @@ -8360,45 +8360,153 @@ void netdev_bonding_info_change(struct net_device *dev,
}
EXPORT_SYMBOL(netdev_bonding_info_change);

bool netdev_offload_xstats_has(struct net_device *dev, int attr_id)
static int netdev_offload_xstats_toggle(struct net_device *dev,
enum netdev_offload_xstats_cmd cmd,
struct netlink_ext_ack *extack)
{
struct netdev_notifier_offload_xstats_info info = {
.info.dev = dev,
.attr_id = attr_id,
.info.extack = extack,
.cmd = cmd,
};
int rc;

call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_GET, &info.info);
return info.handled;
rc = call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_CMD,
&info.info);
return notifier_to_errno(rc);
}
EXPORT_SYMBOL(netdev_offload_xstats_has);

int netdev_offload_xstats_get(struct net_device *dev, int attr_id,
struct rtnl_link_stats64 *stats,
struct netlink_ext_ack *extack)
int netdev_offload_xstats_hw_stats_enable(struct net_device *dev,
u32 req_hw_stats,
struct netlink_ext_ack *extack)
{
struct netdev_notifier_offload_xstats_info info = {
.info.dev = dev,
.info.extack = extack,
.attr_id = attr_id,
.stats = stats,
.cmd = NETDEV_OFFLOAD_XSTATS_CMD_ENABLE,
.req_stats = req_hw_stats,
};
struct rtnl_link_stats64 *stats;
int err;
int rc;

rc = call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_GET,
if (WARN_ON(dev->offload_hw_stats))
return 0;

stats = kmalloc(sizeof(dev->offload_hw_stats), GFP_KERNEL);
if (!stats)
return -ENOMEM;

rc = call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_CMD,
&info.info);
err = notifier_to_errno(rc);
if (err) {
WARN_ON(!info.handled);
return err;
}
if (err)
// xxx note we need a way to roll back if the notifier chain
// fails midway through
goto free_stats;

if (!info.handled)
return -EOPNOTSUPP;
dev->offload_hw_stats = stats;
return 0;

free_stats:
kfree(stats);
return err;
}
EXPORT_SYMBOL(netdev_offload_xstats_hw_stats_enable);

void netdev_offload_xstats_hw_stats_disable(struct net_device *dev)
{
struct netdev_notifier_offload_xstats_info info = {
.info.dev = dev,
.cmd = NETDEV_OFFLOAD_XSTATS_CMD_DISABLE,
};
int rc;

if (WARN_ON(!dev->offload_hw_stats))
return;

call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_CMD,
&info.info);
kfree(dev->offload_hw_stats);
dev->offload_hw_stats = NULL;
}
EXPORT_SYMBOL(netdev_offload_xstats_hw_stats_disable);

struct netdev_notifier_offload_xstats_report_delta {
struct rtnl_link_stats64 stats;
enum netdev_hw_stats_type hw_stats;
};

static void netdev_link_stats64_add(struct rtnl_link_stats64 *dest,
const struct rtnl_link_stats64 *src)
{
dest->rx_packets += src->rx_packets;
dest->tx_packets += src->tx_packets;
dest->rx_bytes += src->rx_bytes;
dest->tx_bytes += src->tx_bytes;
dest->rx_errors += src->rx_errors;
dest->tx_errors += src->tx_errors;
dest->rx_dropped += src->rx_dropped;
dest->tx_dropped += src->tx_dropped;
dest->multicast += src->multicast;
dest->collisions += src->collisions;
dest->rx_length_errors += src->rx_length_errors;
dest->rx_over_errors += src->rx_over_errors;
dest->rx_crc_errors += src->rx_crc_errors;
dest->rx_frame_errors += src->rx_frame_errors;
dest->rx_fifo_errors += src->rx_fifo_errors;
dest->rx_missed_errors += src->rx_missed_errors;
dest->tx_aborted_errors += src->tx_aborted_errors;
dest->tx_carrier_errors += src->tx_carrier_errors;
dest->tx_fifo_errors += src->tx_fifo_errors;
dest->tx_heartbeat_errors += src->tx_heartbeat_errors;
dest->tx_window_errors += src->tx_window_errors;
dest->rx_compressed += src->rx_compressed;
dest->tx_compressed += src->tx_compressed;
dest->rx_nohandler += src->rx_nohandler;
}

int netdev_offload_xstats_hw_stats_get(struct net_device *dev,
struct rtnl_link_stats64 *stats,
enum netdev_hw_stats_type *used_hw_stats,
struct netlink_ext_ack *extack)
{
struct netdev_notifier_offload_xstats_rd report_delta = {};
struct netdev_notifier_offload_xstats_info info = {
.info.dev = dev,
.info.extack = extack,
.cmd = NETDEV_OFFLOAD_XSTATS_CMD_REPORT_DELTA,
.report_delta = &report_delta,
};
int err;
int rc;

rc = call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_CMD,
&info.info);

/* Cache whatever we got, even if there was an error, otherwise the
* successful stats retrievals would get lost.
*/
netdev_link_stats64_add(netdev->offload_hw_stats, &report_delta.stats);
*stats = *netdev->offload_hw_stats;
*used_hw_stats = report_delta.hw_stats;

return notifier_to_errno(rc);
}
EXPORT_SYMBOL(netdev_offload_xstats_get);

void
netdev_offload_xstats_report_delta(struct netdev_notifier_offload_xstats_rd *report_delta,
const struct rtnl_link_stats64 *stats,
enum netdev_hw_stats_type hw_stats)
{
WARN_ON(hweight32(hw_stats) == 1);

report_delta->hw_stats |= hw_stats;
netdev_link_stats64_add(&report_delta->stats, stats);
}
EXPORT_SYMBOL(netdev_offload_xstats_report_delta);

/**
* netdev_get_xmit_slave - Get the xmit slave of master device
* @dev: device
Expand Down

0 comments on commit 453f3cd

Please sign in to comment.