Implement aggregate flow statistics in kernel and userspace switches.
authorBen Pfaff <blp@nicira.com>
Fri, 2 May 2008 23:57:54 +0000 (16:57 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 2 May 2008 23:58:33 +0000 (16:58 -0700)
datapath/datapath.c
include/openflow.h
lib/ofp-print.c
man/man8/dpctl.8
switch/datapath.c
utilities/dpctl.c

index 839dbe637f86d72e5d5bb7eac526505356e83b11..9b7710a2332c36de32945def780345cdda24f1b9 100644 (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,
index 3b5176a582f75f6aed57f4977c841487f241aed6..da297db94d89e9f29107448138916e90ca080412 100644 (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. */
index 80d8ea68731e030983e1f3c0c50c86313337f0d0..19b880ec0e1a66b4bf7f3b8ecf32ff731d6220d2 100644 (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 },
index e030c6d43028f51649e16f3cc119b3501eb26b92..5c11a1f53ad0e2cd1c70403e933764e1d397150c 100644 (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:
index a5e54cd65a2b93afe777f79110acf3bd5fdc6704..e02f418d5c97b720a153fec4ad59be4a17f6f646 100644 (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,
index a66975a0c6525bcc9d8374835cb87ba45fdb667b..24b43db29e5f1a2e2199a579a0442fb9f9dac94f 100644 (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 },