Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions modules/infra/api/gr_infra.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,23 @@ struct gr_infra_iface_set_req {

// struct gr_infra_iface_set_resp { };

#define GR_INFRA_IFACE_STATS_GET REQUEST_TYPE(GR_INFRA_MODULE, 0x0006)

// struct gr_infra_iface_stats_get_req { };

struct gr_iface_stats {
uint16_t iface_id;
uint64_t rx_packets;
uint64_t rx_bytes;
uint64_t tx_packets;
uint64_t tx_bytes;
};

struct gr_infra_iface_stats_get_resp {
uint16_t n_stats;
struct gr_iface_stats stats[/* n_stats */];
};

// port rxqs ///////////////////////////////////////////////////////////////////
#define GR_INFRA_RXQ_LIST REQUEST_TYPE(GR_INFRA_MODULE, 0x0010)

Expand Down Expand Up @@ -309,4 +326,36 @@ struct gr_infra_cpu_affinity_set_req {

// struct gr_infra_cpu_affinity_set_resp { };

// Helper function to convert iface type enum to string
static inline const char *iface_type_to_str(gr_iface_type_t type) {
switch (type) {
case GR_IFACE_TYPE_UNDEF:
return "undef";
case GR_IFACE_TYPE_LOOPBACK:
return "loopback";
case GR_IFACE_TYPE_PORT:
return "port";
case GR_IFACE_TYPE_VLAN:
return "vlan";
case GR_IFACE_TYPE_IPIP:
return "ipip";
case GR_IFACE_TYPE_COUNT:
break;
}
return "?";
}

// Helper function to convert iface mode enum to string
static inline const char *iface_mode_to_str(gr_iface_mode_t mode) {
switch (mode) {
case GR_IFACE_MODE_L3:
return "l3";
case GR_IFACE_MODE_L1_XC:
return "l1-xc";
case GR_IFACE_MODE_COUNT:
break;
}
return "?";
}

#endif
206 changes: 200 additions & 6 deletions modules/infra/api/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,17 +200,74 @@ static struct api_out stats_reset(const void * /*request*/, void ** /*response*/
atomic_store(&worker->stats_reset, true);

iface = NULL;
while ((iface = iface_next(GR_IFACE_TYPE_PORT, iface)) != NULL) {
struct iface_info_port *port = (struct iface_info_port *)iface->info;
if ((ret = rte_eth_stats_reset(port->port_id)) < 0)
return api_out(-ret, 0);
if ((ret = rte_eth_xstats_reset(port->port_id)) < 0)
return api_out(-ret, 0);

while ((iface = iface_next(GR_IFACE_TYPE_UNDEF, iface)) != NULL) {
struct iface_stats *sw_stats = iface_get_stats(iface->id);
// Reset software stats for all interface types.
if (sw_stats != NULL) {
memset(sw_stats, 0, sizeof(*sw_stats));
}

if (iface->type == GR_IFACE_TYPE_PORT) {
struct iface_info_port *port = (struct iface_info_port *)iface->info;
if ((ret = rte_eth_stats_reset(port->port_id)) < 0)
return api_out(-ret, 0);
if ((ret = rte_eth_xstats_reset(port->port_id)) < 0)
return api_out(-ret, 0);
}
Comment on lines -203 to +217
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was squashed into the wrong commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic to clear the stats is part of "infra: add API and CLI command to obtain and clear iface stats" commit - d5f812f, I think we are good?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. The statistics already exist in the first commit infra: implement per interface statistics even if they are not exposed in the API yet.

You should implement the reset of these statistics in this commit. Not the one that exposes the stats in the API.

}

return api_out(0, 0);
}

static struct api_out iface_stats_get(const void * /*request*/, void **response) {
struct gr_infra_iface_stats_get_resp *resp = NULL;
struct gr_iface_stats *stats_vec = NULL;
struct iface *iface = NULL;
int ret = 0;

while ((iface = iface_next(GR_IFACE_TYPE_UNDEF, iface)) != NULL) {
struct iface_stats *sw_stats = iface_get_stats(iface->id);
if (sw_stats == NULL)
continue;

// Create a single stats object per interface
struct gr_iface_stats s;
s.iface_id = iface->id;
s.rx_packets = 0;
s.rx_bytes = 0;
s.tx_packets = 0;
s.tx_bytes = 0;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rjarry I'm already resetting the stats per iface_id before obtaining the stats. Isn't it enough?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean resetting the internal structure.

static struct iface_stats stats[MAX_IFACES];

So after this line:

ifaces[ifid] = iface;

You need to add:

memset(&stats[ifid], 0, sizeof(stats[ifid]));

So that new interfaces are created with all their statistics reset to 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Aggregate per-core stats
for (int i = 0; i < RTE_MAX_LCORE; i++) {
s.rx_packets += sw_stats->rx_packets[i];
s.rx_bytes += sw_stats->rx_bytes[i];
s.tx_packets += sw_stats->tx_packets[i];
s.tx_bytes += sw_stats->tx_bytes[i];
}
gr_vec_add(stats_vec, s);
}

size_t n_stats = gr_vec_len(stats_vec);
size_t len = sizeof(*resp) + n_stats * sizeof(struct gr_iface_stats);
if ((resp = calloc(1, len)) == NULL) {
ret = -ENOMEM;
goto err;
}

resp->n_stats = n_stats;
memcpy(resp->stats, stats_vec, n_stats * sizeof(struct gr_iface_stats));

gr_vec_free(stats_vec);
*response = resp;
return api_out(0, len);
err:
gr_vec_free(stats_vec);
free(resp);
return api_out(-ret, 0);
}

static int
telemetry_sw_stats_get(const char * /*cmd*/, const char * /*params*/, struct rte_tel_data *d) {
struct stat *stats = NULL, *s;
Expand Down Expand Up @@ -278,6 +335,131 @@ telemetry_sw_stats_get(const char * /*cmd*/, const char * /*params*/, struct rte
return -1;
}

static int
telemetry_ifaces_info_get(const char * /*cmd*/, const char * /*params*/, struct rte_tel_data *d) {
struct iface *iface = NULL;

rte_tel_data_start_dict(d);

while ((iface = iface_next(GR_IFACE_TYPE_UNDEF, iface)) != NULL) {
if (iface->type != GR_IFACE_TYPE_LOOPBACK) {
struct rte_tel_data *iface_container = rte_tel_data_alloc();
if (iface_container == NULL) {
goto err;
}
rte_tel_data_start_dict(iface_container);

rte_tel_data_add_dict_string(iface_container, "name", iface->name);
rte_tel_data_add_dict_uint(iface_container, "id", iface->id);
rte_tel_data_add_dict_string(
iface_container, "type", iface_type_to_str(iface->type)
);
rte_tel_data_add_dict_uint(iface_container, "mtu", iface->mtu);

struct rte_tel_data *flags_array = rte_tel_data_alloc();
if (flags_array == NULL) {
rte_tel_data_free(iface_container);
goto err;
}
rte_tel_data_start_array(flags_array, RTE_TEL_STRING_VAL);
if (iface->flags & GR_IFACE_F_UP)
rte_tel_data_add_array_string(flags_array, "up");
if (iface->state & GR_IFACE_S_RUNNING)
rte_tel_data_add_array_string(flags_array, "running");
rte_tel_data_add_dict_container(iface_container, "flags", flags_array, 0);

rte_tel_data_add_dict_string(
iface_container, "mode", iface_mode_to_str(iface->mode)
);
rte_tel_data_add_dict_uint(iface_container, "vrf_id", iface->vrf_id);

struct rte_tel_data *stats_container = rte_tel_data_alloc();
if (stats_container == NULL) {
rte_tel_data_free(iface_container);
goto err;
}
rte_tel_data_start_dict(stats_container);

// Software stats
struct iface_stats *sw_stats = iface_get_stats(iface->id);
if (sw_stats != NULL) {
uint64_t rx_pkts = 0, rx_bytes = 0, tx_pkts = 0, tx_bytes = 0;
for (int i = 0; i < RTE_MAX_LCORE; i++) {
rx_pkts += sw_stats->rx_packets[i];
rx_bytes += sw_stats->rx_bytes[i];
tx_pkts += sw_stats->tx_packets[i];
tx_bytes += sw_stats->tx_bytes[i];
}
rte_tel_data_add_dict_uint(stats_container, "rx_packets", rx_pkts);
rte_tel_data_add_dict_uint(stats_container, "rx_bytes", rx_bytes);
rte_tel_data_add_dict_uint(stats_container, "tx_packets", tx_pkts);
rte_tel_data_add_dict_uint(stats_container, "tx_bytes", tx_bytes);
}

// Get hardware stats for physical ports.
if (iface->type == GR_IFACE_TYPE_PORT) {
struct iface_info_port *port = (struct
iface_info_port *)iface->info;

struct rte_eth_stats eth_stats;
if (rte_eth_stats_get(port->port_id, &eth_stats) == 0) {
rte_tel_data_add_dict_uint(
stats_container, "rx_missed", eth_stats.imissed
);
rte_tel_data_add_dict_uint(
stats_container, "tx_errors", eth_stats.oerrors
);
}

int ret = rte_eth_xstats_get(port->port_id, NULL, 0);
if (ret > 0) {
unsigned num = ret;
struct rte_eth_xstat *xstats = calloc(num, sizeof(*xstats));
struct rte_eth_xstat_name *names = calloc(
num, sizeof(*names)
);
if (xstats != NULL && names != NULL
&& rte_eth_xstats_get_names(port->port_id, names, num)
== (int)num
&& rte_eth_xstats_get(port->port_id, xstats, num)
== (int)num) {
for (unsigned i = 0; i < num; i++) {
if (xstats[i].value > 0) {
rte_tel_data_add_dict_uint(
stats_container,
names[i].name,
xstats[i].value
);
}
}
}
free(xstats);
free(names);
}

if (rte_tel_data_add_dict_container(
iface_container, "statistics", stats_container, 0
)
!= 0) {
rte_tel_data_free(stats_container);
rte_tel_data_free(iface_container);
goto err;
}
}

if (rte_tel_data_add_dict_container(d, iface->name, iface_container, 0)
!= 0) {
rte_tel_data_free(iface_container);
goto err;
}
}
}
return 0;

err:
return -1;
}

static struct gr_api_handler stats_get_handler = {
.name = "stats get",
.request_type = GR_INFRA_STATS_GET,
Expand All @@ -290,12 +472,24 @@ static struct gr_api_handler stats_reset_handler = {
.callback = stats_reset,
};

static struct gr_api_handler iface_stats_get_handler = {
.name = "iface stats get",
.request_type = GR_INFRA_IFACE_STATS_GET,
.callback = iface_stats_get,
};

RTE_INIT(infra_stats_init) {
gr_register_api_handler(&stats_get_handler);
gr_register_api_handler(&stats_reset_handler);
gr_register_api_handler(&iface_stats_get_handler);
rte_telemetry_register_cmd(
"/grout/stats/graph",
telemetry_sw_stats_get,
"Returns statistics of each graph node. No parameters"
);
rte_telemetry_register_cmd(
"/grout/iface",
telemetry_ifaces_info_get,
"Returns information per interface. No parameters"
);
}
70 changes: 67 additions & 3 deletions modules/infra/cli/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,12 +327,75 @@ static cmd_status_t iface_list(const struct gr_api_client *c, const struct ec_pn
return CMD_ERROR;
}

static cmd_status_t iface_stats(const struct gr_api_client *c, const struct ec_pnode * /*p*/) {
struct gr_infra_iface_stats_get_resp *resp = NULL;
struct libscols_table *table = NULL;
cmd_status_t status = CMD_ERROR;
void *resp_ptr = NULL;
int ret;

// Send the new API request and wait for the response
ret = gr_api_client_send_recv(c, GR_INFRA_IFACE_STATS_GET, 0, NULL, &resp_ptr);
if (ret < 0) {
errorf("failed to get interface stats: %s", strerror(-ret));
goto end;
}

resp = resp_ptr;

table = scols_new_table();
if (table == NULL) {
errorf("failed to create table: %s", strerror(errno));
goto end;
}

scols_table_new_column(table, "INTERFACE", 0, 0);
scols_table_new_column(table, "RX_PACKETS", SCOLS_FL_RIGHT, 0);
scols_table_new_column(table, "RX_BYTES", SCOLS_FL_RIGHT, 0);
scols_table_new_column(table, "TX_PACKETS", SCOLS_FL_RIGHT, 0);
scols_table_new_column(table, "TX_BYTES", SCOLS_FL_RIGHT, 0);
scols_table_set_column_separator(table, " ");

for (uint16_t i = 0; i < resp->n_stats; i++) {
struct libscols_line *line = scols_table_new_line(table, NULL);
struct gr_iface iface;
if (line == NULL) {
errorf("failed to create line: %s", strerror(errno));
goto end;
}

if (iface_from_id(c, resp->stats[i].iface_id, &iface) == 0)
scols_line_set_data(line, 0, iface.name);
else
scols_line_set_data(line, 0, "?");

scols_line_sprintf(line, 1, "%lu", resp->stats[i].rx_packets);
scols_line_sprintf(line, 2, "%lu", resp->stats[i].rx_bytes);
scols_line_sprintf(line, 3, "%lu", resp->stats[i].tx_packets);
scols_line_sprintf(line, 4, "%lu", resp->stats[i].tx_bytes);
}

scols_print_table(table);
status = CMD_SUCCESS;

end:
if (table)
scols_unref_table(table);
free(resp_ptr);
return status;
}

static cmd_status_t iface_show(const struct gr_api_client *c, const struct ec_pnode *p) {
const struct cli_iface_type *type;
struct gr_iface iface;

if (arg_str(p, "NAME") == NULL || arg_str(p, "TYPE") != NULL)
if (arg_str(p, "stats") != NULL) {
return iface_stats(c, p);
}

if (arg_str(p, "NAME") == NULL || arg_str(p, "TYPE") != NULL) {
return iface_list(c, p);
}

if (iface_from_name(c, arg_str(p, "NAME"), &iface) < 0)
return CMD_ERROR;
Expand Down Expand Up @@ -385,7 +448,7 @@ static int ctx_init(struct ec_node *root) {
return ret;
ret = CLI_COMMAND(
CLI_CONTEXT(root, CTX_SHOW, CTX_ARG("interface", "Display interface details.")),
"[(name NAME)|(type TYPE)]",
"[(name NAME)|(type TYPE)|stats]",
iface_show,
"Show interface details.",
with_help(
Expand All @@ -395,7 +458,8 @@ static int ctx_init(struct ec_node *root) {
with_help(
"Show only this type of interface.",
ec_node_dyn("TYPE", complete_iface_types, NULL)
)
),
with_help("Show interface statistics.", ec_node_str("stats", "stats"))
);

return ret;
Expand Down
Loading