Implement aggregate flow statistics in kernel and userspace switches.
This commit is contained in:
@@ -1172,6 +1172,63 @@ static void flow_stats_done(void *state)
|
||||
kfree(state);
|
||||
}
|
||||
|
||||
static int aggregate_stats_init(struct datapath *dp,
|
||||
const void *body, int body_len,
|
||||
void **state)
|
||||
{
|
||||
*state = (void *)body;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aggregate_stats_dump_callback(struct sw_flow *flow, void *private)
|
||||
{
|
||||
struct ofp_aggregate_stats_reply *rpy = private;
|
||||
rpy->packet_count += flow->packet_count;
|
||||
rpy->byte_count += flow->byte_count;
|
||||
rpy->flow_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aggregate_stats_dump(struct datapath *dp, void *state,
|
||||
void *body, int *body_len)
|
||||
{
|
||||
struct ofp_aggregate_stats_request *rq = state;
|
||||
struct ofp_aggregate_stats_reply *rpy;
|
||||
struct sw_table_position position;
|
||||
struct sw_flow_key match_key;
|
||||
int table_idx;
|
||||
|
||||
if (*body_len < sizeof *rpy)
|
||||
return -ENOBUFS;
|
||||
rpy = body;
|
||||
*body_len = sizeof *rpy;
|
||||
|
||||
memset(rpy, 0, sizeof *rpy);
|
||||
|
||||
flow_extract_match(&match_key, &rq->match);
|
||||
table_idx = rq->table_id == 0xff ? 0 : rq->table_id;
|
||||
memset(&position, 0, sizeof position);
|
||||
while (table_idx < dp->chain->n_tables
|
||||
&& (rq->table_id == 0xff || rq->table_id == table_idx))
|
||||
{
|
||||
struct sw_table *table = dp->chain->tables[table_idx];
|
||||
int error;
|
||||
|
||||
error = table->iterate(table, &match_key, &position,
|
||||
aggregate_stats_dump_callback, rpy);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
table_idx++;
|
||||
memset(&position, 0, sizeof position);
|
||||
}
|
||||
|
||||
rpy->packet_count = cpu_to_be64(rpy->packet_count);
|
||||
rpy->byte_count = cpu_to_be64(rpy->byte_count);
|
||||
rpy->flow_count = htonl(rpy->flow_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int table_stats_dump(struct datapath *dp, void *state,
|
||||
void *body, int *body_len)
|
||||
{
|
||||
@@ -1281,6 +1338,13 @@ static const struct stats_type stats[] = {
|
||||
flow_stats_dump,
|
||||
flow_stats_done
|
||||
},
|
||||
[OFPST_AGGREGATE] = {
|
||||
sizeof(struct ofp_aggregate_stats_request),
|
||||
sizeof(struct ofp_aggregate_stats_request),
|
||||
aggregate_stats_init,
|
||||
aggregate_stats_dump,
|
||||
NULL
|
||||
},
|
||||
[OFPST_TABLE] = {
|
||||
0,
|
||||
0,
|
||||
|
||||
+26
-14
@@ -369,11 +369,16 @@ struct ofp_error_msg {
|
||||
};
|
||||
|
||||
enum ofp_stats_types {
|
||||
/* Flow statistics.
|
||||
/* Individual flow statistics.
|
||||
* The request body is struct ofp_flow_stats_request.
|
||||
* The reply body is an array of struct ofp_flow_stats. */
|
||||
OFPST_FLOW,
|
||||
|
||||
/* Aggregate flow statistics.
|
||||
* The request body is struct ofp_aggregate_stats_request.
|
||||
* The reply body is struct ofp_aggregate_stats_reply. */
|
||||
OFPST_AGGREGATE,
|
||||
|
||||
/* Flow table statistics.
|
||||
* The request body is empty.
|
||||
* The reply body is an array of struct ofp_table_stats. */
|
||||
@@ -403,35 +408,42 @@ struct ofp_stats_reply {
|
||||
uint8_t body[0]; /* Body of the reply. */
|
||||
};
|
||||
|
||||
enum ofp_stats_type {
|
||||
OFPFS_INDIV, /* Send an entry for each matching flow */
|
||||
OFPFS_AGGREGATE /* Aggregate matching flows */
|
||||
};
|
||||
|
||||
/* Body for ofp_stats_request of type OFPST_FLOW. */
|
||||
struct ofp_flow_stats_request {
|
||||
struct ofp_match match; /* Fields to match */
|
||||
uint8_t table_id; /* ID of table to read (from ofp_table_stats)
|
||||
or 0xff for all tables. */
|
||||
uint8_t type; /* One of OFPFS_ */
|
||||
uint16_t pad; /* Align to 32-bits */
|
||||
uint8_t pad[3]; /* Align to 32 bits. */
|
||||
};
|
||||
|
||||
/* Body of reply to OFPST_FLOW request. */
|
||||
struct ofp_flow_stats {
|
||||
uint16_t length; /* Length of this entry */
|
||||
uint8_t table_id; /* ID of table flow came from. 0nly used for
|
||||
non-aggregated results */
|
||||
uint8_t table_id; /* ID of table flow came from. */
|
||||
uint8_t pad;
|
||||
struct ofp_match match; /* Description of fields */
|
||||
uint32_t duration; /* Time flow has been alive in seconds. Only
|
||||
used for non-aggregated results. */
|
||||
uint32_t duration; /* Time flow has been alive in seconds. */
|
||||
uint64_t packet_count; /* Number of packets in flow. */
|
||||
uint64_t byte_count; /* Number of bytes in flow. */
|
||||
uint16_t priority; /* Priority of the entry. Only meaningful
|
||||
when this is not an exact-match entry. */
|
||||
uint16_t max_idle; /* Only used for non-aggregated results. */
|
||||
struct ofp_action actions[0]; /* Only used for non-aggregated results. */
|
||||
uint16_t max_idle; /* Number of seconds idle before expiration. */
|
||||
struct ofp_action actions[0]; /* Actions. */
|
||||
};
|
||||
|
||||
/* Body for ofp_stats_request of type OFPST_AGGREGATE. */
|
||||
struct ofp_aggregate_stats_request {
|
||||
struct ofp_match match; /* Fields to match */
|
||||
uint8_t table_id; /* ID of table to read (from ofp_table_stats)
|
||||
or 0xff for all tables. */
|
||||
uint8_t pad[3]; /* Align to 32 bits. */
|
||||
};
|
||||
|
||||
/* Body of reply to OFPST_AGGREGATE request. */
|
||||
struct ofp_aggregate_stats_reply {
|
||||
uint64_t packet_count; /* Number of packets in flows. */
|
||||
uint64_t byte_count; /* Number of bytes in flows. */
|
||||
uint32_t flow_count; /* Number of flows. */
|
||||
};
|
||||
|
||||
/* Body of reply to OFPST_TABLE request. */
|
||||
|
||||
+35
-7
@@ -457,13 +457,6 @@ ofp_flow_stats_request(struct ds *string, const void *oh, size_t len,
|
||||
ds_put_format(string, " table_id=%"PRIu8", ", fsr->table_id);
|
||||
}
|
||||
|
||||
if (fsr->type == OFPFS_INDIV) {
|
||||
ds_put_cstr(string, " type=indiv, ");
|
||||
} else if (fsr->type == OFPFS_AGGREGATE) {
|
||||
ds_put_cstr(string, " type=aggregate, ");
|
||||
} else {
|
||||
ds_put_format(string, " ***type=%"PRIu8"***, ", fsr->type);
|
||||
}
|
||||
ofp_print_match(string, &fsr->match);
|
||||
}
|
||||
|
||||
@@ -522,6 +515,32 @@ ofp_flow_stats_reply(struct ds *string, const void *body_, size_t len,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ofp_aggregate_stats_request(struct ds *string, const void *oh, size_t len,
|
||||
int verbosity)
|
||||
{
|
||||
const struct ofp_aggregate_stats_request *asr = oh;
|
||||
|
||||
if (asr->table_id == 0xff) {
|
||||
ds_put_format(string, " table_id=any, ");
|
||||
} else {
|
||||
ds_put_format(string, " table_id=%"PRIu8", ", asr->table_id);
|
||||
}
|
||||
|
||||
ofp_print_match(string, &asr->match);
|
||||
}
|
||||
|
||||
static void
|
||||
ofp_aggregate_stats_reply(struct ds *string, const void *body_, size_t len,
|
||||
int verbosity)
|
||||
{
|
||||
const struct ofp_aggregate_stats_reply *asr = body_;
|
||||
|
||||
ds_put_format(string, " packet_count=%"PRIu64, ntohll(asr->packet_count));
|
||||
ds_put_format(string, " byte_count=%"PRIu64, ntohll(asr->byte_count));
|
||||
ds_put_format(string, " flow_count=%"PRIu32, ntohl(asr->flow_count));
|
||||
}
|
||||
|
||||
static void
|
||||
ofp_port_stats_reply(struct ds *string, const void *body, size_t len,
|
||||
int verbosity)
|
||||
@@ -594,6 +613,15 @@ print_stats(struct ds *string, int type, const void *body, size_t body_len,
|
||||
ofp_flow_stats_request },
|
||||
{ 0, SIZE_MAX, ofp_flow_stats_reply },
|
||||
},
|
||||
[OFPST_AGGREGATE] = {
|
||||
"aggregate",
|
||||
{ sizeof(struct ofp_aggregate_stats_request),
|
||||
sizeof(struct ofp_aggregate_stats_request),
|
||||
ofp_aggregate_stats_request },
|
||||
{ sizeof(struct ofp_aggregate_stats_reply),
|
||||
sizeof(struct ofp_aggregate_stats_reply),
|
||||
ofp_aggregate_stats_reply },
|
||||
},
|
||||
[OFPST_TABLE] = {
|
||||
"table",
|
||||
{ 0, 0, NULL },
|
||||
|
||||
@@ -86,6 +86,13 @@ Prints to the console all flow entries in datapath \fIDP_IDX\fR's table
|
||||
and \fITABLE_ID\fR is the integer ID of one of the datapath's tables
|
||||
as displayed in the output produced by \fBdump-tables\fR.
|
||||
|
||||
.TP
|
||||
.BI dump-aggregate " DP_IDX [FLOWS]"
|
||||
Prints to the console aggregate statistics for flows in datapath
|
||||
\fIDP_IDX\fR's that match \fIFLOWS\fR. If \fIFLOWS\fR is omitted, the
|
||||
statistics are aggregated across all flows in the datapath's flow
|
||||
tables.
|
||||
|
||||
.SH EXAMPLES
|
||||
|
||||
A typical dpctl command sequence:
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "buffer.h"
|
||||
@@ -1188,6 +1189,72 @@ static void flow_stats_done(void *state)
|
||||
free(state);
|
||||
}
|
||||
|
||||
struct aggregate_stats_state {
|
||||
struct ofp_aggregate_stats_request rq;
|
||||
};
|
||||
|
||||
static int aggregate_stats_init(struct datapath *dp,
|
||||
const void *body, int body_len,
|
||||
void **state)
|
||||
{
|
||||
const struct ofp_aggregate_stats_request *rq = body;
|
||||
struct aggregate_stats_state *s = xmalloc(sizeof *s);
|
||||
s->rq = *rq;
|
||||
*state = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aggregate_stats_dump_callback(struct sw_flow *flow, void *private)
|
||||
{
|
||||
struct ofp_aggregate_stats_reply *rpy = private;
|
||||
rpy->packet_count += flow->packet_count;
|
||||
rpy->byte_count += flow->byte_count;
|
||||
rpy->flow_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aggregate_stats_dump(struct datapath *dp, void *state,
|
||||
struct buffer *buffer)
|
||||
{
|
||||
struct aggregate_stats_state *s = state;
|
||||
struct ofp_aggregate_stats_request *rq = &s->rq;
|
||||
struct ofp_aggregate_stats_reply *rpy;
|
||||
struct sw_table_position position;
|
||||
struct sw_flow_key match_key;
|
||||
int table_idx;
|
||||
|
||||
rpy = buffer_put_uninit(buffer, sizeof *rpy);
|
||||
memset(rpy, 0, sizeof *rpy);
|
||||
|
||||
flow_extract_match(&match_key, &rq->match);
|
||||
table_idx = rq->table_id == 0xff ? 0 : rq->table_id;
|
||||
memset(&position, 0, sizeof position);
|
||||
while (table_idx < dp->chain->n_tables
|
||||
&& (rq->table_id == 0xff || rq->table_id == table_idx))
|
||||
{
|
||||
struct sw_table *table = dp->chain->tables[table_idx];
|
||||
int error;
|
||||
|
||||
error = table->iterate(table, &match_key, &position,
|
||||
aggregate_stats_dump_callback, rpy);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
table_idx++;
|
||||
memset(&position, 0, sizeof position);
|
||||
}
|
||||
|
||||
rpy->packet_count = htonll(rpy->packet_count);
|
||||
rpy->byte_count = htonll(rpy->byte_count);
|
||||
rpy->flow_count = htonl(rpy->flow_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aggregate_stats_done(void *state)
|
||||
{
|
||||
free(state);
|
||||
}
|
||||
|
||||
static int table_stats_dump(struct datapath *dp, void *state,
|
||||
struct buffer *buffer)
|
||||
{
|
||||
@@ -1280,6 +1347,13 @@ static const struct stats_type stats[] = {
|
||||
flow_stats_dump,
|
||||
flow_stats_done
|
||||
},
|
||||
[OFPST_AGGREGATE] = {
|
||||
sizeof(struct ofp_aggregate_stats_request),
|
||||
sizeof(struct ofp_aggregate_stats_request),
|
||||
aggregate_stats_init,
|
||||
aggregate_stats_dump,
|
||||
aggregate_stats_done
|
||||
},
|
||||
[OFPST_TABLE] = {
|
||||
0,
|
||||
0,
|
||||
|
||||
+17
-2
@@ -171,6 +171,8 @@ usage(void)
|
||||
" dump-ports SWITCH print port statistics\n"
|
||||
" dump-flows SWITCH print all flow entries\n"
|
||||
" dump-flows SWITCH FLOW print matching FLOWs\n"
|
||||
" dump-aggregate SWITCH print aggregate flow statistics\n"
|
||||
" dump-aggregate SWITCH FLOW print aggregate stats for FLOWs\n"
|
||||
" add-flows SWITCH FILE add flows from FILE\n"
|
||||
" del-flows SWITCH FLOW delete matching FLOWs\n"
|
||||
"where each SWITCH is an active OpenFlow connection method.\n",
|
||||
@@ -624,8 +626,20 @@ static void do_dump_flows(int argc, char *argv[])
|
||||
req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
|
||||
str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, &req->table_id,
|
||||
NULL);
|
||||
req->type = OFPFS_INDIV;
|
||||
req->pad = 0;
|
||||
memset(req->pad, 0, sizeof req->pad);
|
||||
|
||||
dump_stats_transaction(argv[1], request);
|
||||
}
|
||||
|
||||
static void do_dump_aggregate(int argc, char *argv[])
|
||||
{
|
||||
struct ofp_aggregate_stats_request *req;
|
||||
struct buffer *request;
|
||||
|
||||
req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
|
||||
str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, &req->table_id,
|
||||
NULL);
|
||||
memset(req->pad, 0, sizeof req->pad);
|
||||
|
||||
dump_stats_transaction(argv[1], request);
|
||||
}
|
||||
@@ -729,6 +743,7 @@ static struct command all_commands[] = {
|
||||
{ "monitor", 1, 1, do_monitor },
|
||||
{ "dump-tables", 1, 1, do_dump_tables },
|
||||
{ "dump-flows", 1, 2, do_dump_flows },
|
||||
{ "dump-aggregate", 1, 2, do_dump_aggregate },
|
||||
{ "add-flows", 2, 2, do_add_flows },
|
||||
{ "del-flows", 1, 2, do_del_flows },
|
||||
{ "dump-ports", 1, 1, do_dump_ports },
|
||||
|
||||
Reference in New Issue
Block a user