From: Ben Pfaff Date: Fri, 2 May 2008 23:57:54 +0000 (-0700) Subject: Implement aggregate flow statistics in kernel and userspace switches. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7bf3916ffec50a8771727f6dfcf45a3cce9f6ae;p=openvswitch Implement aggregate flow statistics in kernel and userspace switches. --- diff --git a/datapath/datapath.c b/datapath/datapath.c index 839dbe63..9b7710a2 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -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, diff --git a/include/openflow.h b/include/openflow.h index 3b5176a5..da297db9 100644 --- a/include/openflow.h +++ b/include/openflow.h @@ -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. */ diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 80d8ea68..19b880ec 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -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 }, diff --git a/man/man8/dpctl.8 b/man/man8/dpctl.8 index e030c6d4..5c11a1f5 100644 --- a/man/man8/dpctl.8 +++ b/man/man8/dpctl.8 @@ -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: diff --git a/switch/datapath.c b/switch/datapath.c index a5e54cd6..e02f418d 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #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, diff --git a/utilities/dpctl.c b/utilities/dpctl.c index a66975a0..24b43db2 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -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 },