Implement aggregate flow statistics in kernel and userspace switches.

This commit is contained in:
Ben Pfaff
2008-05-02 16:57:54 -07:00
parent 53cceda7ad
commit f7bf3916ff
6 changed files with 223 additions and 23 deletions
+64
View File
@@ -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
View File
@@ -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
View File
@@ -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 },
+7
View File
@@ -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:
+74
View File
@@ -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
View File
@@ -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 },