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)
{
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,
};
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. */
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. */
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);
}
}
}
+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)
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 },
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:
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
+#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "buffer.h"
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)
{
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,
" 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",
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);
}
{ "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 },