Implement new statistics format in kernel and userspace switches.
authorBen Pfaff <blp@nicira.com>
Fri, 2 May 2008 00:02:13 +0000 (17:02 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 2 May 2008 00:04:44 +0000 (17:04 -0700)
This change really cuts the muster!

17 files changed:
datapath/datapath.c
datapath/datapath.h
datapath/forward.c
include/openflow.h
include/rconn.h
lib/dpif.c
lib/ofp-print.c
lib/rconn.c
switch/Makefile.am
switch/chain.c
switch/datapath.c
switch/switch-flow.h
switch/table-hash.c
switch/table-linear.c
switch/table-mac.c [deleted file]
switch/table.h
utilities/dpctl.c

index 83be31124cf0bf464fd9eb9e7397550989858b56..d5cd1dcd3e9a13fba3389b154ca7413eda086174 100644 (file)
@@ -856,78 +856,6 @@ fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow,
        memset(ofs->pad, 0, sizeof ofs->pad);
 }
 
-static int 
-fill_port_stats_reply(struct datapath *dp, struct ofp_port_stats_reply *psr)
-{
-       struct net_bridge_port *p;
-       int port_count = 0;
-
-       list_for_each_entry_rcu (p, &dp->port_list, node) {
-               struct ofp_port_stats *ps = &psr->ports[port_count++];
-               struct net_device_stats *stats = p->dev->get_stats(p->dev);
-               ps->port_no = htons(p->port_no);
-               memset(ps->pad, 0, sizeof ps->pad);
-               ps->rx_count = cpu_to_be64(stats->rx_packets);
-               ps->tx_count = cpu_to_be64(stats->tx_packets);
-               ps->drop_count = cpu_to_be64(stats->rx_dropped
-                                            + stats->tx_dropped);
-       }
-
-       return port_count;
-}
-
-int
-dp_send_port_stats(struct datapath *dp, const struct sender *sender)
-{
-       struct sk_buff *skb;
-       struct ofp_port_stats_reply *psr;
-       size_t psr_len, port_max_len;
-       int port_count;
-
-       /* Overallocate. */
-       port_max_len = sizeof(struct ofp_port_stats) * OFPP_MAX;
-       psr = alloc_openflow_skb(dp, sizeof *psr + port_max_len,
-                                OFPT_PORT_STATS_REPLY, sender, &skb);
-       if (!psr)
-               return -ENOMEM;
-
-       /* Fill. */
-       port_count = fill_port_stats_reply(dp, psr);
-
-       /* Shrink to fit. */
-       psr_len = sizeof *psr + sizeof(struct ofp_port_stats) * port_count;
-       resize_openflow_skb(skb, &psr->header, psr_len);
-       return send_openflow_skb(skb, sender);
-}
-
-int
-dp_send_table_stats(struct datapath *dp, const struct sender *sender)
-{
-       struct sk_buff *skb;
-       struct ofp_table_stats_reply *tsr;
-       int i, n_tables;
-
-       n_tables = dp->chain->n_tables;
-       tsr = alloc_openflow_skb(dp, (offsetof(struct ofp_table_stats_reply,
-                                              tables)
-                                     + sizeof tsr->tables[0] * n_tables),
-                                OFPT_TABLE_STATS_REPLY, sender, &skb);
-       if (!tsr)
-               return -ENOMEM;
-       for (i = 0; i < n_tables; i++) {
-               struct ofp_table_stats *ots = &tsr->tables[i];
-               struct sw_table_stats stats;
-               dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
-               strncpy(ots->name, stats.name, sizeof ots->name);
-               ots->table_id = i;
-               ots->pad[0] = ots->pad[1] = 0;
-               ots->max_entries = htonl(stats.max_flows);
-               ots->active_count = htonl(stats.n_flows);
-               ots->matched_count = cpu_to_be64(0); /* FIXME */
-       }
-       return send_openflow_skb(skb, sender);
-}
-
 /* Generic Netlink interface.
  *
  * See netlink(7) for an introduction to netlink.  See
@@ -1160,47 +1088,48 @@ static struct nla_policy dp_genl_openflow_policy[DP_GENL_A_MAX + 1] = {
        [DP_GENL_A_DP_IDX] = { .type = NLA_U32 },
 };
 
-struct flow_stats_cb_state {
-       int dp_idx;
+struct flow_stats_state {
        int table_idx;
        struct sw_table_position position;
-       struct ofp_flow_stats_request *rq;
-       int sent_terminator;
+       const struct ofp_flow_stats_request *rq;
 
        struct ofp_flow_stats *flows;
        int n_flows, max_flows;
 };
 
-static int muster_callback(struct sw_flow *flow, void *private)
+static int flow_stats_init(struct datapath *dp, const void *body, int body_len,
+                          void **state)
 {
-       struct flow_stats_cb_state *s = private;
+       const struct ofp_flow_stats_request *fsr = body;
+       struct flow_stats_state *s = kmalloc(sizeof *s, GFP_ATOMIC);
+       if (!s)
+               return -ENOMEM;
+       s->table_idx = fsr->table_id == 0xff ? 0 : fsr->table_id;
+       memset(&s->position, 0, sizeof s->position);
+       s->rq = fsr;
+       *state = s;
+       return 0;
+}
+
+static int flow_stats_dump_callback(struct sw_flow *flow, void *private)
+{
+       struct flow_stats_state *s = private;
 
        fill_flow_stats(&s->flows[s->n_flows], flow, s->table_idx);
        return ++s->n_flows >= s->max_flows;
 }
 
-int
-muster_flow_stats(struct datapath *dp, struct flow_stats_cb_state *s,
-                 const struct sender *sender, struct sk_buff *skb)
+static int flow_stats_dump(struct datapath *dp, void *state,
+                          void *body, int *body_len)
 {
-       struct ofp_flow_stats_reply *fsr;
-       size_t header_size, flow_size;
+       struct flow_stats_state *s = state;
+       struct ofp_flow_stats *ofs;
        struct sw_flow_key match_key;
-       int max_openflow_len;
-       size_t size;
 
-       fsr = put_openflow_headers(dp, skb, OFPT_FLOW_STATS_REPLY, sender,
-                                  &max_openflow_len);
-       if (IS_ERR(fsr))
-               return PTR_ERR(fsr);
-       resize_openflow_skb(skb, &fsr->header, max_openflow_len);
-
-       header_size = offsetof(struct ofp_flow_stats_reply, flows);
-       flow_size = sizeof fsr->flows[0];
-       s->max_flows = (max_openflow_len - header_size) / flow_size;
-       if (s->max_flows <= 0)
+       s->max_flows = *body_len / sizeof *ofs;
+       if (!s->max_flows)
                return -ENOMEM;
-       s->flows = fsr->flows;
+       s->flows = body;
 
        flow_extract_match(&match_key, &s->rq->match);
        s->n_flows = 0;
@@ -1210,84 +1139,253 @@ muster_flow_stats(struct datapath *dp, struct flow_stats_cb_state *s,
                struct sw_table *table = dp->chain->tables[s->table_idx];
 
                if (table->iterate(table, &match_key, &s->position,
-                                  muster_callback, s))
+                                  flow_stats_dump_callback, s))
                        break;
 
                s->table_idx++;
                memset(&s->position, 0, sizeof s->position);
        }
-       if (!s->n_flows) {
-               /* Signal dump completion. */
-               if (s->sent_terminator) {
-                       return 0;
-               }
-               s->sent_terminator = 1;
+       *body_len = sizeof *ofs * s->n_flows;
+       return s->n_flows >= s->max_flows;
+}
+
+static void flow_stats_done(void *state)
+{
+       kfree(state);
+}
+
+static int table_stats_dump(struct datapath *dp, void *state,
+                           void *body, int *body_len)
+{
+       struct ofp_table_stats *ots;
+       int nbytes = dp->chain->n_tables * sizeof *ots;
+       int i;
+       if (nbytes > *body_len)
+               return -ENOBUFS;
+       *body_len = nbytes;
+       for (i = 0, ots = body; i < dp->chain->n_tables; i++, ots++) {
+               struct sw_table_stats stats;
+               dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
+               strncpy(ots->name, stats.name, sizeof ots->name);
+               ots->table_id = i;
+               memset(ots->pad, 0, sizeof ots->pad);
+               ots->max_entries = htonl(stats.max_flows);
+               ots->active_count = htonl(stats.n_flows);
+               ots->matched_count = cpu_to_be64(0); /* FIXME */
        }
-       size = header_size + flow_size * s->n_flows;
-       resize_openflow_skb(skb, &fsr->header, size);
-       return skb->len;
+       return 0;
+}
+
+struct port_stats_state {
+       int port;
+};
+
+static int port_stats_init(struct datapath *dp, const void *body, int body_len,
+                          void **state)
+{
+       struct port_stats_state *s = kmalloc(sizeof *s, GFP_ATOMIC);
+       if (!s)
+               return -ENOMEM;
+       s->port = 0;
+       *state = s;
+       return 0;
 }
 
+static int port_stats_dump(struct datapath *dp, void *state,
+                          void *body, int *body_len)
+{
+       struct port_stats_state *s = state;
+       struct ofp_port_stats *ops;
+       int n_ports, max_ports;
+       int i;
+
+       max_ports = *body_len / sizeof *ops;
+       if (!max_ports)
+               return -ENOMEM;
+       ops = body;
+
+       n_ports = 0;
+       for (i = s->port; i < OFPP_MAX && n_ports < max_ports; i++) {
+               struct net_bridge_port *p = dp->ports[i];
+               struct net_device_stats *stats;
+               if (!p)
+                       continue;
+               stats = p->dev->get_stats(p->dev);
+               ops->port_no = htons(p->port_no);
+               memset(ops->pad, 0, sizeof ops->pad);
+               ops->rx_count = cpu_to_be64(stats->rx_packets);
+               ops->tx_count = cpu_to_be64(stats->tx_packets);
+               ops->drop_count = cpu_to_be64(stats->rx_dropped
+                                             + stats->tx_dropped);
+               n_ports++;
+               ops++;
+       }
+       s->port = i;
+       *body_len = n_ports * sizeof *ops;
+       return n_ports >= max_ports;
+}
+
+static void port_stats_done(void *state)
+{
+       kfree(state);
+}
+
+struct stats_type {
+       /* Minimum and maximum acceptable number of bytes in body member of
+        * struct ofp_stats_request. */
+       size_t min_body, max_body;
+
+       /* Prepares to dump some kind of statistics on 'dp'.  'body' and
+        * 'body_len' are the 'body' member of the struct ofp_stats_request.
+        * Returns zero if successful, otherwise a negative error code.
+        * May initialize '*state' to state information.  May be null if no
+        * initialization is required.*/
+       int (*init)(struct datapath *dp, const void *body, int body_len,
+                   void **state);
+
+       /* Dumps statistics for 'dp' into the '*body_len' bytes at 'body', and
+        * modifies '*body_len' to reflect the number of bytes actually used.
+        * ('body' will be transmitted as the 'body' member of struct
+        * ofp_stats_reply.) */
+       int (*dump)(struct datapath *dp, void *state,
+                   void *body, int *body_len);
+
+       /* Cleans any state created by the init or dump functions.  May be null
+        * if no cleanup is required. */
+       void (*done)(void *state);
+};
+
+static const struct stats_type stats[] = {
+       [OFPST_FLOW] = {
+               sizeof(struct ofp_flow_stats_request),
+               sizeof(struct ofp_flow_stats_request),
+               flow_stats_init,
+               flow_stats_dump,
+               flow_stats_done
+       },
+       [OFPST_TABLE] = {
+               0,
+               0,
+               NULL,
+               table_stats_dump,
+               NULL
+       },
+       [OFPST_PORT] = {
+               0,
+               0,
+               port_stats_init,
+               port_stats_dump,
+               port_stats_done
+       },
+};
+
 static int
 dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct datapath *dp;
        struct sender sender;
-       struct flow_stats_cb_state *state;
+       const struct stats_type *s;
+       struct ofp_stats_reply *osr;
+       int dp_idx;
+       int max_openflow_len, body_len;
+       void *body;
        int err;
 
+       rcu_read_lock();
        if (!cb->args[0]) {
                struct nlattr *attrs[DP_GENL_A_MAX + 1];
-               struct ofp_flow_stats_request *rq;
+               struct ofp_stats_request *rq;
                struct nlattr *va;
+               size_t len, body_len;
+               int type;
 
                err = nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs, DP_GENL_A_MAX,
                                  dp_genl_openflow_policy);
                if (err < 0)
                        return err;
 
+               err = -EINVAL;
+
                if (!attrs[DP_GENL_A_DP_IDX])
-                       return -EINVAL;
+                       goto out;
+               dp_idx = nla_get_u16(attrs[DP_GENL_A_DP_IDX]);
+               dp = dp_get(dp_idx);
+               if (!dp) {
+                       err = -ENOENT;
+                       goto out;
+               }
 
                va = attrs[DP_GENL_A_OPENFLOW];
-               if (!va || nla_len(va) != sizeof *state->rq)
-                       return -EINVAL;
+               len = nla_len(va);
+               if (!va || len < sizeof *rq)
+                       goto out;
 
                rq = nla_data(va);
+               type = ntohs(rq->type);
                if (rq->header.version != OFP_VERSION
-                   || rq->header.type != OFPT_FLOW_STATS_REQUEST
-                   || ntohs(rq->header.length) != sizeof *rq)
-                       return -EINVAL;
-
-               state = kmalloc(sizeof *state, GFP_KERNEL);
-               if (!state)
-                       return -ENOMEM;
-               state->dp_idx = nla_get_u32(attrs[DP_GENL_A_DP_IDX]);
-               state->table_idx = rq->table_id == 0xff ? 0 : rq->table_id;
-               memset(&state->position, 0, sizeof state->position);
-               state->rq = rq;
-               state->sent_terminator = 0;
-
-               cb->args[0] = (long) state;
-       } else {
-               state = (struct flow_stats_cb_state *) cb->args[0];
-       }
-
-       if (state->rq->type != OFPFS_INDIV) {
-               return -ENOTSUPP;
-       }
+                   || rq->header.type != OFPT_STATS_REQUEST
+                   || ntohs(rq->header.length) != len
+                   || type >= ARRAY_SIZE(stats)
+                   || !stats[type].dump)
+                       goto out;
+
+               s = &stats[type];
+               body_len = len - offsetof(struct ofp_stats_request, body);
+               if (body_len < s->min_body || body_len > s->max_body)
+                       goto out;
+
+               cb->args[0] = 1;
+               cb->args[1] = dp_idx;
+               cb->args[2] = type;
+               cb->args[3] = rq->header.xid;
+               if (s->init) {
+                       void *state;
+                       err = s->init(dp, rq->body, body_len, &state);
+                       if (err)
+                               goto out;
+                       cb->args[4] = (long) state;
+               }
+       } else if (cb->args[0] == 1) {
+               dp_idx = cb->args[1];
+               s = &stats[cb->args[2]];
 
-       rcu_read_lock();
-       dp = dp_get(state->dp_idx);
-       if (!dp) {
-               err = -ENOENT;
+               dp = dp_get(dp_idx);
+               if (!dp) {
+                       err = -ENOENT;
+                       goto out;
+               }
+       } else {
+               err = 0;
                goto out;
        }
 
-       sender.xid = state->rq->header.xid;
+       sender.xid = cb->args[3];
        sender.pid = NETLINK_CB(cb->skb).pid;
        sender.seq = cb->nlh->nlmsg_seq;
-       err = muster_flow_stats(dp, state, &sender, skb);
+
+       osr = put_openflow_headers(dp, skb, OFPT_STATS_REPLY, &sender,
+                                  &max_openflow_len);
+       if (IS_ERR(osr)) {
+               err = PTR_ERR(osr);
+               goto out;
+       }
+       osr->type = htons(s - stats);
+       osr->flags = 0;
+       resize_openflow_skb(skb, &osr->header, max_openflow_len);
+       body = osr->body;
+       body_len = max_openflow_len - offsetof(struct ofp_stats_reply, body);
+
+       err = s->dump(dp, (void *) cb->args[4], body, &body_len);
+       if (err >= 0) {
+               if (!err)
+                       cb->args[0] = 2;
+               else
+                       osr->flags = ntohs(OFPSF_REPLY_MORE);
+               resize_openflow_skb(skb, &osr->header,
+                                   (offsetof(struct ofp_stats_reply, body)
+                                    + body_len));
+               err = skb->len;
+       }
 
 out:
        rcu_read_unlock();
@@ -1297,9 +1395,11 @@ out:
 static int
 dp_genl_openflow_done(struct netlink_callback *cb)
 {
-       struct flow_stats_cb_state *state;
-       state = (struct flow_stats_cb_state *) cb->args[0];
-       kfree(state);
+       if (cb->args[0]) {
+               const struct stats_type *s = &stats[cb->args[2]];
+               if (s->done)
+                       s->done((void *) cb->args[4]);
+       }
        return 0;
 }
 
index 76798ef8e72dc8c66a05ea1b4ff13aab8c78c9e1..1a884f8340958893c587e0a0ed2240d280d77b98 100644 (file)
@@ -71,10 +71,6 @@ int dp_set_origin(struct datapath *, uint16_t, struct sk_buff *);
 int dp_send_features_reply(struct datapath *, const struct sender *);
 int dp_send_config_reply(struct datapath *, const struct sender *);
 int dp_send_flow_expired(struct datapath *, struct sw_flow *);
-int dp_send_flow_stats(struct datapath *, const struct sender *,
-                       const struct ofp_match *);
-int dp_send_table_stats(struct datapath *, const struct sender *);
-int dp_send_port_stats(struct datapath *, const struct sender *);
 int dp_send_error_msg(struct datapath *, const struct sender *, 
                        uint16_t, uint16_t, const uint8_t *, size_t);
 int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
index 7ab282788abfc7d879bde685217d9b64036f19c1..6553c947b7d348196f6d850ff80d3fa5d49b2543 100644 (file)
@@ -437,20 +437,6 @@ recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg)
        }
 }
 
-static int
-recv_port_stats_request(struct sw_chain *chain, const struct sender *sender,
-                        const void *msg)
-{
-       return dp_send_port_stats(chain->dp, sender);
-}
-
-static int
-recv_table_stats_request(struct sw_chain *chain, const struct sender *sender,
-                         const void *msg)
-{
-       return dp_send_table_stats(chain->dp, sender);
-}
-
 /* 'msg', which is 'length' bytes long, was received across Netlink from
  * 'sender'.  Apply it to 'chain'. */
 int
@@ -489,14 +475,6 @@ fwd_control_input(struct sw_chain *chain, const struct sender *sender,
                        sizeof (struct ofp_port_mod),
                        recv_port_mod,
                },
-               [OFPT_PORT_STATS_REQUEST] = {
-                       sizeof (struct ofp_port_stats_request),
-                       recv_port_stats_request,
-               },
-               [OFPT_TABLE_STATS_REQUEST] = {
-                       sizeof (struct ofp_table_stats_request),
-                       recv_table_stats_request,
-               },
        };
 
        const struct openflow_packet *pkt;
index c8253e304d781fad75be2901b03893074d1e839d..776dafdc2dcd64976a9e7b25a466115c127c024d 100644 (file)
@@ -93,12 +93,8 @@ enum ofp_type {
     OFPT_PORT_MOD,            /* 10 Controller/switch message */
     OFPT_PORT_STATUS,         /* 11 Async message */
     OFPT_ERROR_MSG,           /* 12 Async message */
-    OFPT_FLOW_STATS_REQUEST,  /* 13 Controller/switch message */
-    OFPT_FLOW_STATS_REPLY,    /* 14 Controller/switch message */
-    OFPT_TABLE_STATS_REQUEST, /* 15 Controller/switch message */
-    OFPT_TABLE_STATS_REPLY,   /* 16 Controller/switch message */
-    OFPT_PORT_STATS_REQUEST,  /* 17 Controller/switch message */
-    OFPT_PORT_STATS_REPLY     /* 18 Controller/switch message */
+    OFPT_STATS_REQUEST,       /* 13 Controller/switch message */
+    OFPT_STATS_REPLY          /* 14 Controller/switch message */
 };
 
 /* Header on all OpenFlow packets. */
@@ -369,17 +365,39 @@ struct ofp_error_msg {
                                  on the type and code. */
 };
 
-/* Statistics about flows that match the "match" field */
-struct ofp_flow_stats {
-    struct ofp_match match;   /* Description of fields */
-    uint32_t duration;        /* Time flow has been alive in seconds.  Only 
-                                 used for non-aggregated results. */
-    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. */
-    uint8_t table_id;         /* ID of table flow came from. */
-    uint8_t pad[5];           /* Align to 64-bits. */
+enum ofp_stats_types {
+    /* Flow statistics.
+     * The request body is struct ofp_flow_stats_request.
+     * The reply body is an array of struct ofp_flow_stats. */
+    OFPST_FLOW,
+
+    /* Flow table statistics.
+     * The request body is empty.
+     * The reply body is an array of struct ofp_table_stats. */
+    OFPST_TABLE,
+
+    /* Physical port statistics.
+     * The request body is empty.
+     * The reply body is an array of struct ofp_port_stats. */
+    OFPST_PORT
+};
+
+struct ofp_stats_request {
+    struct ofp_header header;
+    uint16_t type;              /* One of the OFPST_* constants. */
+    uint16_t flags;             /* OFPSF_REQ_* flags (none yet defined). */
+    uint8_t body[0];            /* Body of the request. */
+};
+
+enum ofp_stats_reply_flags {
+    OFPSF_REPLY_MORE  = 1 << 0, /* More replies to follow */
+};
+
+struct ofp_stats_reply {
+    struct ofp_header header;
+    uint16_t type;              /* One of the OFPST_* constants. */
+    uint16_t flags;             /* OFPSF_REPLY_* flags. */
+    uint8_t body[0];            /* Body of the reply. */
 };
 
 enum ofp_stats_type {
@@ -387,9 +405,8 @@ enum ofp_stats_type {
     OFPFS_AGGREGATE           /* Aggregate matching flows */
 };
 
-/* Current flow statistics request */
+/* Body for ofp_stats_request of type OFPST_FLOW. */
 struct ofp_flow_stats_request {
-    struct ofp_header header;
     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. */
@@ -397,26 +414,20 @@ struct ofp_flow_stats_request {
     uint16_t pad;               /* Align to 32-bits */
 };
 
-/* Current flow statistics reply */
-struct ofp_flow_stats_reply {
-    struct ofp_header header;
-
-    /* If request was of type OFPFS_INDIV, this will contain an array of
-     * flow statistic entries.  The number of matching flows is likely
-     * much larger than can fit in a single OpenFlow message, so a
-     * a response with no flows included is sent to indicate the end.
-     * If it was a OFPFS_AGGREGATE request, only a single flow stats 
-     * entry will be contained in the response.
-     */
-    struct ofp_flow_stats flows[0];  
-};
-
-/* Current table statistics request */
-struct ofp_table_stats_request {
-    struct ofp_header header;
+/* Body of reply to OFPST_FLOW request. */
+struct ofp_flow_stats {
+    struct ofp_match match;   /* Description of fields */
+    uint32_t duration;        /* Time flow has been alive in seconds.  Only 
+                                 used for non-aggregated results. */
+    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. */
+    uint8_t table_id;         /* ID of table flow came from. */
+    uint8_t pad[5];           /* Align to 64-bits. */
 };
 
-/* Statistics about a particular table */
+/* Body of reply to OFPST_TABLE request. */
 struct ofp_table_stats {
     uint8_t table_id;
     uint8_t pad[3];          /* Align to 32-bits */
@@ -426,13 +437,6 @@ struct ofp_table_stats {
     uint64_t matched_count;  /* Number of packets that hit table */
 };
 
-/* Current table statistics reply */
-struct ofp_table_stats_reply {
-    struct ofp_header header;
-    struct ofp_table_stats tables[0]; /* The number of entries is inferred from
-                                        the length field in the header. */
-};
-
 /* Statistics about a particular port */
 struct ofp_port_stats {
     uint16_t port_no;
@@ -442,16 +446,4 @@ struct ofp_port_stats {
     uint64_t drop_count;     /* Number of packets dropped by interface */
 };
 
-/* Current port statistics request */
-struct ofp_port_stats_request {
-    struct ofp_header header;
-};
-
-/* Current port statistics reply */
-struct ofp_port_stats_reply {
-    struct ofp_header header;
-    struct ofp_port_stats ports[0]; /* The number of entries is inferred from
-                                      the length field in the header. */
-};
-
 #endif /* openflow.h */
index 064be01a8780c43257e0ca754fee10cf3bdd290e..3e72da03971ad1b4b9c61f565421a8b0f71c5fae 100644 (file)
@@ -62,6 +62,7 @@ void rconn_run_wait(struct rconn *);
 struct buffer *rconn_recv(struct rconn *);
 void rconn_recv_wait(struct rconn *);
 int rconn_send(struct rconn *, struct buffer *);
+bool rconn_is_full(const struct rconn *);
 
 const char *rconn_get_name(const struct rconn *);
 bool rconn_is_alive(const struct rconn *);
index 34268993c1d4d3a1bae7305618d07c32ceaafe3d..e591985637d8be2e0b05833184a70648326728a4 100644 (file)
@@ -202,10 +202,10 @@ dpif_send_openflow(struct dpif *dp, struct buffer *buffer, bool wait)
     int n_iov;
     int retval;
 
-    /* The reply to OFPT_FLOW_STATS_REQUEST may be multiple segments long, so
-     * we need to specify NLM_F_DUMP in the request. */
+    /* The reply to OFPT_STATS_REQUEST may be multiple segments long, so we
+     * need to specify NLM_F_DUMP in the request. */
     oh = buffer_at_assert(buffer, 0, sizeof *oh);
-    dump_flag = oh->type == OFPT_FLOW_STATS_REQUEST ? NLM_F_DUMP : 0;
+    dump_flag = oh->type == OFPT_STATS_REQUEST ? NLM_F_DUMP : 0;
 
     buffer_use(&hdr, fixed_buffer, sizeof fixed_buffer);
     nl_msg_put_genlmsghdr(&hdr, dp->sock, 32, openflow_family,
index 8b2da7e0402ce1754212a8f92b47d4c8ed780f65..4c19bb952715406cdc3d8fd6d254df9c7986eec5 100644 (file)
@@ -466,20 +466,17 @@ ofp_flow_stats_request(struct ds *string, const void *oh, size_t len,
 }
 
 static void
-ofp_flow_stats_reply(struct ds *string, const void *oh, size_t len,
+ofp_flow_stats_reply(struct ds *string, const void *body, size_t len,
                      int verbosity)
 {
-    const struct ofp_flow_stats_reply *fsr = oh;
-    const struct ofp_flow_stats *fs;
-    size_t n;
-
-    n = (len - offsetof(struct ofp_flow_stats_reply, flows)) / sizeof *fs;
+    const struct ofp_flow_stats *fs = body;
+    size_t n = len / sizeof *fs;
     ds_put_format(string, " %zu flows\n", n);
     if (verbosity < 1) {
         return;
     }
 
-    for (fs = &fsr->flows[0]; fs < &fsr->flows[n]; fs++) {
+    for (; n--; fs++) {
         ds_put_format(string, "  duration=%"PRIu32" s, ", ntohl(fs->duration));
         ds_put_format(string, "table_id=%"PRIu8", ", fs->table_id);
         ds_put_format(string, "priority=%"PRIu16", ", 
@@ -492,20 +489,17 @@ ofp_flow_stats_reply(struct ds *string, const void *oh, size_t len,
 }
 
 static void
-ofp_port_stats_reply(struct ds *string, const void *oh, size_t len,
-                    int verbosity)
+ofp_port_stats_reply(struct ds *string, const void *body, size_t len,
+                     int verbosity)
 {
-    const struct ofp_port_stats_reply *psr = oh;
-    const struct ofp_port_stats *ps;
-    size_t n;
-
-    n = (len - offsetof(struct ofp_port_stats_reply, ports)) / sizeof *ps;
+    const struct ofp_port_stats *ps = body;
+    size_t n = len / sizeof *ps;
     ds_put_format(string, " %zu ports\n", n);
     if (verbosity < 1) {
         return;
     }
 
-    for (ps = &psr->ports[0]; ps < &psr->ports[n]; ps++) {
+    for (; n--; ps++) {
         ds_put_format(string, "  port %"PRIu16": ", ntohs(ps->port_no));
         ds_put_format(string, "rx %"PRIu64", ", ntohll(ps->rx_count));
         ds_put_format(string, "tx %"PRIu64", ", ntohll(ps->tx_count));
@@ -514,20 +508,17 @@ ofp_port_stats_reply(struct ds *string, const void *oh, size_t len,
 }
 
 static void
-ofp_table_stats_reply(struct ds *string, const void *oh, size_t len,
+ofp_table_stats_reply(struct ds *string, const void *body, size_t len,
                      int verbosity)
 {
-    const struct ofp_table_stats_reply *tsr = oh;
-    const struct ofp_table_stats *ts;
-    size_t n;
-
-    n = (len - offsetof(struct ofp_table_stats_reply, tables)) / sizeof *ts;
+    const struct ofp_table_stats *ts = body;
+    size_t n = len / sizeof *ts;
     ds_put_format(string, " %zu tables\n", n);
     if (verbosity < 1) {
         return;
     }
 
-    for (ts = &tsr->tables[0]; ts < &tsr->tables[n]; ts++) {
+    for (; n--; ts++) {
         char name[OFP_MAX_TABLE_NAME_LEN + 1];
         strncpy(name, ts->name, sizeof name);
         name[OFP_MAX_TABLE_NAME_LEN] = '\0';
@@ -541,6 +532,106 @@ ofp_table_stats_reply(struct ds *string, const void *oh, size_t len,
      }
 }
 
+enum stats_direction {
+    REQUEST,
+    REPLY
+};
+
+static void
+print_stats(struct ds *string, int type, const void *body, size_t body_len,
+            int verbosity, enum stats_direction direction)
+{
+    struct stats_msg {
+        size_t min_body, max_body;
+        void (*printer)(struct ds *, const void *, size_t len, int verbosity);
+    };
+
+    struct stats_type {
+        const char *name;
+        struct stats_msg request;
+        struct stats_msg reply;
+    };
+
+    static const struct stats_type stats_types[] = {
+        [OFPST_FLOW] = {
+            "flow",
+            { sizeof(struct ofp_flow_stats_request),
+              sizeof(struct ofp_flow_stats_request),
+              ofp_flow_stats_request },
+            { 0, SIZE_MAX, ofp_flow_stats_reply },
+        },
+        [OFPST_TABLE] = {
+            "table",
+            { 0, 0, NULL },
+            { 0, SIZE_MAX, ofp_table_stats_reply },
+        },
+        [OFPST_PORT] = {
+            "port",
+            { 0, 0, NULL, },
+            { 0, SIZE_MAX, ofp_port_stats_reply },
+        },
+    };
+
+    const struct stats_type *s;
+    const struct stats_msg *m;
+
+    if (type >= ARRAY_SIZE(stats_types) || !stats_types[type].name) {
+        ds_put_format(string, " ***unknown type %d***", type);
+        return;
+    }
+    s = &stats_types[type];
+    ds_put_format(string, " type=%d(%s)", type, s->name);
+
+    m = direction == REQUEST ? &s->request : &s->reply;
+    if (body_len < m->min_body || body_len > m->max_body) {
+        ds_put_format(string, " ***body_len=%zu not in %zu...%zu***",
+                      body_len, m->min_body, m->max_body);
+        return;
+    }
+    if (m->printer) {
+        m->printer(string, body, body_len, verbosity);
+    }
+}
+
+static void
+ofp_stats_request(struct ds *string, const void *oh, size_t len, int verbosity)
+{
+    const struct ofp_stats_request *srq = oh;
+
+    if (srq->flags) {
+        ds_put_format(string, " ***unknown flags %04"PRIx16"***",
+                      ntohs(srq->flags));
+    }
+
+    print_stats(string, ntohs(srq->type), srq->body,
+                len - offsetof(struct ofp_stats_request, body),
+                verbosity, REQUEST);
+}
+
+static void
+ofp_stats_reply(struct ds *string, const void *oh, size_t len, int verbosity)
+{
+    const struct ofp_stats_reply *srp = oh;
+
+    ds_put_cstr(string, " flags=");
+    if (!srp->flags) {
+        ds_put_cstr(string, "none");
+    } else {
+        uint16_t flags = ntohs(srp->flags);
+        if (flags & OFPSF_REPLY_MORE) {
+            ds_put_cstr(string, "[more]");
+            flags &= ~OFPSF_REPLY_MORE;
+        }
+        if (flags) {
+            ds_put_format(string, "[***unknown%04"PRIx16"***]", flags);
+        }
+    }
+
+    print_stats(string, ntohs(srp->type), srp->body,
+                len - offsetof(struct ofp_stats_reply, body),
+                verbosity, REPLY);
+}
+
 struct openflow_packet {
     const char *name;
     size_t min_size;
@@ -608,35 +699,15 @@ static const struct openflow_packet packets[] = {
         sizeof (struct ofp_error_msg),
         ofp_print_error_msg,
     },
-    [OFPT_FLOW_STATS_REQUEST] = {
-        "flow_stats_request",
-        sizeof (struct ofp_flow_stats_request),
-        ofp_flow_stats_request,
-    },
-    [OFPT_FLOW_STATS_REPLY] = {
-        "flow_stats_reply",
-        sizeof (struct ofp_flow_stats_reply),
-        ofp_flow_stats_reply,
-    },
-    [OFPT_PORT_STATS_REQUEST] = {
-        "port_stats_request",
-        sizeof (struct ofp_port_stats_request),
-        NULL,
-    },
-    [OFPT_PORT_STATS_REPLY] = {
-        "port_stats_reply",
-        sizeof (struct ofp_port_stats_reply),
-        ofp_port_stats_reply,
+    [OFPT_STATS_REQUEST] = {
+        "stats_request",
+        sizeof (struct ofp_stats_request),
+        ofp_stats_request,
     },
-    [OFPT_TABLE_STATS_REQUEST] = {
-        "table_stats_request",
-        sizeof (struct ofp_table_stats_request),
-        NULL,
-    },
-    [OFPT_TABLE_STATS_REPLY] = {
-        "table_stats_reply",
-        sizeof (struct ofp_table_stats_reply),
-        ofp_table_stats_reply,
+    [OFPT_STATS_REPLY] = {
+        "stats_reply",
+        sizeof (struct ofp_stats_reply),
+        ofp_stats_reply,
     },
 };
 
@@ -692,6 +763,9 @@ ofp_to_string(const void *oh_, size_t len, int verbosity)
     if (verbosity >= 3) {
         ds_put_hex_dump(&string, oh, len, 0, true);
     }
+    if (string.string[string.length - 1] != '\n') {
+        ds_put_char(&string, '\n');
+    }
     return ds_cstr(&string);
 }
 \f
index 73d42bb91cc4bee1220c902c9e407c6fea89fc08..7fd440529702b548f19f2d59404c6e975596d4b1 100644 (file)
@@ -207,6 +207,14 @@ rconn_send(struct rconn *rc, struct buffer *b)
     }
 }
 
+/* Returns true if 'rc''s send buffer is full,
+ * false if it has room for at least one more packet. */
+bool
+rconn_is_full(const struct rconn *rc) 
+{
+    return rc->txq.n >= rc->txq_limit;
+}
+
 /* Returns 'rc''s name (the 'name' argument passed to rconn_new()). */
 const char *
 rconn_get_name(const struct rconn *rc) 
index eb9931b003f279561508f1c5137a9303decde095..7488e0729d590793a084befa6c094f5d9d5c2e96 100644 (file)
@@ -16,7 +16,6 @@ switch_SOURCES = \
        switch-flow.h \
        table.h \
        table-hash.c \
-       table-linear.c \
-       table-mac.c 
+       table-linear.c
 
 switch_LDADD = ../lib/libopenflow.la -ldl
index dab4dd338f1a0f0d90c8d51cdedc965aa0d67ac3..a85a650121579a528d8d764dbea807bc11a9a4c8 100644 (file)
@@ -65,9 +65,7 @@ struct sw_chain *chain_create(void)
     if (chain == NULL)
         return NULL;
 
-    if (add_table(chain, table_mac_create(TABLE_MAC_NUM_BUCKETS, 
-                                          TABLE_MAC_MAX_FLOWS))
-        || add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
+    if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
                                                0x741B8CD7, TABLE_HASH_MAX_FLOWS))
         || add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS))) {
         chain_destroy(chain);
index 1332a4c65eaa64d0fcf716b57d1a20ba2b95bebb..521150ba811ac31bc5d60c261d134a1012a4df91 100644 (file)
@@ -74,16 +74,25 @@ struct sw_port {
     unsigned long long int rx_count, tx_count, drop_count;
 };
 
+/* The origin of a received OpenFlow message, to enable sending a reply. */
+struct sender {
+    struct remote *remote;      /* The device that sent the message. */
+    uint32_t xid;               /* The OpenFlow transaction ID. */
+};
+
 /* A connection to a controller or a management device. */
 struct remote {
     struct list node;
     struct rconn *rconn;
-};
 
-/* The origin of a received OpenFlow message, to enable sending a reply. */
-struct sender {
-    struct remote *remote;      /* The device that sent the message. */
-    uint32_t xid;               /* The OpenFlow transaction ID. */
+    /* Support for reliable, multi-message replies to requests.
+     *
+     * If an incoming request needs to have a reliable reply that might
+     * require multiple messages, it can use remote_start_dump() to set up
+     * a callback that will be called as buffer space for replies. */
+    int (*cb_dump)(struct datapath *, void *aux);
+    void (*cb_done)(void *aux);
+    void *cb_aux;
 };
 
 struct datapath {
@@ -244,7 +253,7 @@ dp_add_listen_vconn(struct datapath *dp, struct vconn *listen_vconn)
 }
 
 void
-dp_run(struct datapath *dp) 
+dp_run(struct datapath *dp)
 {
     time_t now = time(0);
     struct sw_port *p, *pn;
@@ -319,28 +328,43 @@ remote_run(struct datapath *dp, struct remote *r)
 
     rconn_run(r->rconn);
 
-    /* Process a number of commands from the remote, but cap them at a
-     * reasonable number so that other processing doesn't starve. */
+    /* Do some remote processing, but cap it at a reasonable amount so that
+     * other processing doesn't starve. */
     for (i = 0; i < 50; i++) {
-        struct buffer *buffer;
-        struct ofp_header *oh;
+        if (!r->cb_dump) {
+            struct buffer *buffer;
+            struct ofp_header *oh;
 
-        buffer = rconn_recv(r->rconn);
-        if (!buffer) {
-            break;
-        }
+            buffer = rconn_recv(r->rconn);
+            if (!buffer) {
+                break;
+            }
 
-        if (buffer->size >= sizeof *oh) {
-            struct sender sender;
+            if (buffer->size >= sizeof *oh) {
+                struct sender sender;
 
-            oh = buffer->data;
-            sender.remote = r;
-            sender.xid = oh->xid;
-            fwd_control_input(dp, &sender, buffer->data, buffer->size);
+                oh = buffer->data;
+                sender.remote = r;
+                sender.xid = oh->xid;
+                fwd_control_input(dp, &sender, buffer->data, buffer->size);
+            } else {
+                VLOG_WARN("received too-short OpenFlow message");
+            }
+            buffer_delete(buffer); 
         } else {
-            VLOG_WARN("received too-short OpenFlow message"); 
+            if (!rconn_is_full(r->rconn)) {
+                int error = r->cb_dump(dp, r->cb_aux);
+                if (error <= 0) {
+                    if (error) {
+                        VLOG_WARN("dump callback error: %s", strerror(-error));
+                    }
+                    r->cb_done(r->cb_aux);
+                    r->cb_dump = NULL;
+                }
+            } else {
+                break;
+            }
         }
-        buffer_delete(buffer);
     }
 
     if (!rconn_is_alive(r->rconn)) {
@@ -359,6 +383,9 @@ static void
 remote_destroy(struct remote *r)
 {
     if (r) {
+        if (r->cb_dump && r->cb_done) {
+            r->cb_done(r->cb_aux);
+        }
         list_remove(&r->node);
         rconn_destroy(r->rconn);
         free(r);
@@ -371,9 +398,36 @@ remote_create(struct datapath *dp, struct rconn *rconn)
     struct remote *remote = xmalloc(sizeof *remote);
     list_push_back(&dp->remotes, &remote->node);
     remote->rconn = rconn;
+    remote->cb_dump = NULL;
     return remote;
 }
 
+/* Starts a callback-based, reliable, possibly multi-message reply to a
+ * request made by 'remote'.
+ *
+ * 'dump' designates a function that will be called when the 'remote' send
+ * queue has an empty slot.  It should compose a message and send it on
+ * 'remote'.  On success, it should return 1 if it should be called again when
+ * another send queue slot opens up, 0 if its transmissions are complete, or a
+ * negative errno value on failure.
+ *
+ * 'done' designates a function to clean up any resources allocated for the
+ * dump.  It must handle being called before the dump is complete (which will
+ * happen if 'remote' is closed unexpectedly).
+ *
+ * 'aux' is passed to 'dump' and 'done'. */
+static void
+remote_start_dump(struct remote *remote,
+                  int (*dump)(struct datapath *, void *),
+                  void (*done)(void *),
+                  void *aux) 
+{
+    assert(!remote->cb_dump);
+    remote->cb_dump = dump;
+    remote->cb_done = done;
+    remote->cb_aux = aux;
+}
+
 void
 dp_wait(struct datapath *dp) 
 {
@@ -677,99 +731,6 @@ fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow,
     memset(ofs->pad, 0, sizeof ofs->pad);
 }
 
-int
-dp_send_flow_stats(struct datapath *dp, const struct sender *sender,
-                   const struct ofp_match *match)
-{
-    struct buffer *buffer;
-    struct ofp_flow_stats_reply *fsr;
-    size_t header_size, fudge, flow_size;
-    struct sw_flow_key match_key;
-    int table_idx, n_flows, max_flows;
-    time_t now;
-
-    header_size = offsetof(struct ofp_flow_stats_reply, flows);
-    fudge = 128;
-    flow_size = sizeof fsr->flows[0];
-    max_flows = (65536 - header_size - fudge) / flow_size;
-    fsr = alloc_openflow_buffer(dp, header_size,
-                                OFPT_FLOW_STATS_REPLY, sender, &buffer);
-
-    n_flows = 0;
-    flow_extract_match(&match_key, match);
-    now = time(0);
-    for (table_idx = 0; table_idx < dp->chain->n_tables; table_idx++) {
-        struct sw_table *table = dp->chain->tables[table_idx];
-        struct swt_iterator iter;
-
-        if (n_flows >= max_flows) {
-            break;
-        }
-
-        if (!table->iterator(table, &iter)) {
-            printf("iterator failed for table %d\n", table_idx);
-            continue;
-        }
-
-        for (; iter.flow; table->iterator_next(&iter)) {
-            if (flow_matches(&match_key, &iter.flow->key)) {
-                struct ofp_flow_stats *ofs = buffer_put_uninit(buffer,
-                                                               sizeof *ofs);
-                fill_flow_stats(ofs, iter.flow, table_idx, now);
-                if (++n_flows >= max_flows) {
-                    break;
-                }
-            }
-        }
-        table->iterator_destroy(&iter);
-    }
-    return send_openflow_buffer(dp, buffer, sender);
-}
-
-int
-dp_send_port_stats(struct datapath *dp, const struct sender *sender)
-{
-       struct buffer *buffer;
-       struct ofp_port_stats_reply *psr;
-    struct sw_port *p;
-
-       psr = alloc_openflow_buffer(dp, offsetof(struct ofp_port_stats_reply,
-                                             ports),
-                                OFPT_PORT_STATS_REPLY, sender, &buffer);
-    LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
-               struct ofp_port_stats *ps = buffer_put_uninit(buffer, sizeof *ps);
-               ps->port_no = htons(port_no(dp, p));
-               memset(ps->pad, 0, sizeof ps->pad);
-               ps->rx_count = htonll(p->rx_count);
-               ps->tx_count = htonll(p->tx_count);
-               ps->drop_count = htonll(p->drop_count);
-       }
-       return send_openflow_buffer(dp, buffer, sender);
-}
-
-int
-dp_send_table_stats(struct datapath *dp, const struct sender *sender)
-{
-       struct buffer *buffer;
-       struct ofp_table_stats_reply *tsr;
-       int i;
-
-       tsr = alloc_openflow_buffer(dp, offsetof(struct ofp_table_stats_reply,
-                                             tables),
-                                OFPT_TABLE_STATS_REPLY, sender, &buffer);
-       for (i = 0; i < dp->chain->n_tables; i++) {
-               struct ofp_table_stats *ots = buffer_put_uninit(buffer, sizeof *ots);
-               struct sw_table_stats stats;
-               dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
-               strncpy(ots->name, stats.name, sizeof ots->name);
-               ots->table_id = i;
-               ots->pad[0] = ots->pad[1] = 0;
-               ots->max_entries = htonl(stats.max_flows);
-               ots->active_count = htonl(stats.n_flows);
-               ots->matched_count = htonll(0); /* FIXME */
-       }
-       return send_openflow_buffer(dp, buffer, sender);
-}
 \f
 /* 'buffer' was received on 'in_port', a physical switch port between 0 and
  * OFPP_MAX.  Process it according to 'chain'. */
@@ -1161,31 +1122,282 @@ recv_flow(struct datapath *dp, const struct sender *sender UNUSED,
     }
 }
 
-static int
-recv_flow_stats_request(struct datapath *dp, const struct sender *sender,
-                         const void *msg)
+struct flow_stats_state {
+       int table_idx;
+       struct sw_table_position position;
+       struct ofp_flow_stats_request rq;
+    time_t now;
+
+    struct buffer *buffer;
+       int n_flows, max_flows;
+};
+
+static int flow_stats_init(struct datapath *dp, const void *body, int body_len,
+                           void **state)
+{
+       const struct ofp_flow_stats_request *fsr = body;
+       struct flow_stats_state *s = xmalloc(sizeof *s);
+       s->table_idx = fsr->table_id == 0xff ? 0 : fsr->table_id;
+       memset(&s->position, 0, sizeof s->position);
+       s->rq = *fsr;
+       *state = s;
+       return 0;
+}
+
+static int flow_stats_dump_callback(struct sw_flow *flow, void *private)
+{
+       struct flow_stats_state *s = private;
+    struct ofp_flow_stats *ofs = buffer_put_uninit(s->buffer, sizeof *ofs);
+       fill_flow_stats(ofs, flow, s->table_idx, s->now);
+       return ++s->n_flows >= s->max_flows;
+}
+
+static int flow_stats_dump(struct datapath *dp, void *state,
+                           struct buffer *buffer)
+{
+       struct flow_stats_state *s = state;
+       struct ofp_flow_stats *ofs;
+       struct sw_flow_key match_key;
+
+       s->max_flows = 4096 / sizeof *ofs;
+       if (!s->max_flows)
+               return -ENOMEM;
+
+       flow_extract_match(&match_key, &s->rq.match);
+    s->buffer = buffer;
+       s->n_flows = 0;
+    s->now = time(0);
+       while (s->table_idx < dp->chain->n_tables
+              && (s->rq.table_id == 0xff || s->rq.table_id == s->table_idx))
+       {
+               struct sw_table *table = dp->chain->tables[s->table_idx];
+
+               if (table->iterate(table, &match_key, &s->position,
+                           flow_stats_dump_callback, s))
+                       break;
+
+               s->table_idx++;
+               memset(&s->position, 0, sizeof s->position);
+       }
+       return s->n_flows >= s->max_flows;
+}
+
+static void flow_stats_done(void *state)
+{
+       free(state);
+}
+
+static int table_stats_dump(struct datapath *dp, void *state,
+                            struct buffer *buffer)
+{
+       int i;
+       for (i = 0; i < dp->chain->n_tables; i++) {
+        struct ofp_table_stats *ots = buffer_put_uninit(buffer, sizeof *ots);
+               struct sw_table_stats stats;
+               dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
+               strncpy(ots->name, stats.name, sizeof ots->name);
+               ots->table_id = i;
+               memset(ots->pad, 0, sizeof ots->pad);
+               ots->max_entries = htonl(stats.max_flows);
+               ots->active_count = htonl(stats.n_flows);
+               ots->matched_count = htonll(0); /* FIXME */
+       }
+       return 0;
+}
+
+struct port_stats_state {
+       int port;
+};
+
+static int port_stats_init(struct datapath *dp, const void *body, int body_len,
+                          void **state)
 {
-       const struct ofp_flow_stats_request *fsr = msg;
-       if (fsr->type == OFPFS_INDIV) {
-               return dp_send_flow_stats(dp, sender, &fsr->match); 
-       } else {
-               /* FIXME */
-               return -ENOSYS;
+       struct port_stats_state *s = xmalloc(sizeof *s);
+       s->port = 0;
+       *state = s;
+       return 0;
+}
+
+static int port_stats_dump(struct datapath *dp, void *state,
+                           struct buffer *buffer)
+{
+       struct port_stats_state *s = state;
+       int i;
+
+       for (i = s->port; i < OFPP_MAX; i++) {
+               struct sw_port *p = &dp->ports[i];
+        struct ofp_port_stats *ops;
+               if (!p->netdev) {
+                       continue;
+        }
+        ops = buffer_put_uninit(buffer, sizeof *ops);
+               ops->port_no = htons(port_no(dp, p));
+               memset(ops->pad, 0, sizeof ops->pad);
+               ops->rx_count = htonll(p->rx_count);
+               ops->tx_count = htonll(p->tx_count);
+               ops->drop_count = htonll(p->drop_count);
+               ops++;
        }
+       s->port = i;
+       return 0;
+}
+
+static void port_stats_done(void *state)
+{
+       free(state);
 }
 
+struct stats_type {
+       /* Minimum and maximum acceptable number of bytes in body member of
+        * struct ofp_stats_request. */
+       size_t min_body, max_body;
+
+       /* Prepares to dump some kind of statistics on 'dp'.  'body' and
+        * 'body_len' are the 'body' member of the struct ofp_stats_request.
+        * Returns zero if successful, otherwise a negative error code.
+        * May initialize '*state' to state information.  May be null if no
+        * initialization is required.*/
+       int (*init)(struct datapath *dp, const void *body, int body_len,
+                   void **state);
+
+       /* Appends statistics for 'dp' to 'buffer', which initially contains a
+     * struct ofp_stats_reply.  On success, it should return 1 if it should be
+     * called again later with another buffer, 0 if it is done, or a negative
+     * errno value on failure. */
+       int (*dump)(struct datapath *dp, void *state, struct buffer *buffer);
+
+       /* Cleans any state created by the init or dump functions.  May be null
+        * if no cleanup is required. */
+       void (*done)(void *state);
+};
+
+static const struct stats_type stats[] = {
+       [OFPST_FLOW] = {
+               sizeof(struct ofp_flow_stats_request),
+               sizeof(struct ofp_flow_stats_request),
+               flow_stats_init,
+               flow_stats_dump,
+               flow_stats_done
+       },
+       [OFPST_TABLE] = {
+               0,
+               0,
+               NULL,
+               table_stats_dump,
+               NULL
+       },
+       [OFPST_PORT] = {
+               0,
+               0,
+               port_stats_init,
+               port_stats_dump,
+               port_stats_done
+       },
+};
+
+struct stats_dump_cb {
+    bool done;
+    struct ofp_stats_request *rq;
+    struct sender sender;
+    const struct stats_type *s;
+    void *state;
+};
+
 static int
-recv_port_stats_request(struct datapath *dp, const struct sender *sender,
-                         const void *msg)
+stats_dump(struct datapath *dp, void *cb_)
+{
+    struct stats_dump_cb *cb = cb_;
+       struct ofp_stats_reply *osr;
+    struct buffer *buffer;
+       int err;
+
+    if (cb->done) {
+        return 0;
+       }
+
+       osr = alloc_openflow_buffer(dp, sizeof *osr, OFPT_STATS_REPLY, &cb->sender,
+                                &buffer);
+       osr->type = htons(cb->s - stats);
+       osr->flags = 0;
+
+       err = cb->s->dump(dp, cb->state, buffer);
+       if (err >= 0) {
+        int err2;
+               if (!err) {
+                       cb->done = true;
+        } else {
+            /* Buffer might have been reallocated, so find our data again. */
+            osr = buffer_at_assert(buffer, 0, sizeof *osr);
+                       osr->flags = ntohs(OFPSF_REPLY_MORE);
+        }
+        err2 = send_openflow_buffer(dp, buffer, &cb->sender);
+        if (err2) {
+            err = err2;
+        }
+       }
+
+       return err;
+}
+
+static void
+stats_done(void *cb_)
 {
-       return dp_send_port_stats(dp, sender);
+    struct stats_dump_cb *cb = cb_;
+    if (cb) {
+               if (cb->s->done) {
+                       cb->s->done(cb->state);
+        }
+        free(cb);
+       }
 }
 
 static int
-recv_table_stats_request(struct datapath *dp, const struct sender *sender,
-                          const void *msg)
+recv_stats_request(struct datapath *dp, const struct sender *sender,
+                   const void *oh)
 {
-       return dp_send_table_stats(dp, sender);
+    const struct ofp_stats_request *rq = oh;
+    size_t rq_len = ntohs(rq->header.length);
+    struct stats_dump_cb *cb;
+    int type, body_len;
+    int err;
+
+    type = ntohs(rq->type);
+    if (type >= ARRAY_SIZE(stats) || !stats[type].dump) {
+        VLOG_WARN("received stats request of unknown type %d", type);
+        return -EINVAL;
+    }
+
+    cb = xmalloc(sizeof *cb);
+    cb->done = false;
+    cb->rq = xmemdup(rq, rq_len);
+    cb->sender = *sender;
+    cb->s = &stats[type];
+    cb->state = NULL;
+    
+    body_len = rq_len - offsetof(struct ofp_stats_request, body);
+    if (body_len < cb->s->min_body || body_len > cb->s->max_body) {
+        VLOG_WARN("stats request type %d with bad body length %d",
+                  type, body_len);
+        err = -EINVAL;
+        goto error;
+    }
+
+    if (cb->s->init) {
+        err = cb->s->init(dp, rq->body, body_len, &cb->state);
+        if (err) {
+            VLOG_WARN("failed initialization of stats request type %d: %s",
+                      type, strerror(-err));
+            goto error;
+        }
+    }
+
+    remote_start_dump(sender->remote, stats_dump, stats_done, cb);
+    return 0;
+
+error:
+    free(cb->rq);
+    free(cb);
+    return err;
 }
 
 /* 'msg', which is 'length' bytes long, was received from the control path.
@@ -1224,17 +1436,9 @@ fwd_control_input(struct datapath *dp, const struct sender *sender,
             sizeof (struct ofp_port_mod),
             recv_port_mod,
         },
-               [OFPT_FLOW_STATS_REQUEST] = {
-                       sizeof (struct ofp_flow_stats_request),
-                       recv_flow_stats_request,
-               },
-               [OFPT_PORT_STATS_REQUEST] = {
-                       sizeof (struct ofp_port_stats_request),
-                       recv_port_stats_request,
-               },
-               [OFPT_TABLE_STATS_REQUEST] = {
-                       sizeof (struct ofp_table_stats_request),
-                       recv_table_stats_request,
+               [OFPT_STATS_REQUEST] = {
+                       sizeof (struct ofp_stats_request),
+                       recv_stats_request,
                },
     };
 
index 40d94dd216c8d0c4b790eddb47f1eba9018efe4b..8de25bef97f8b3dbed7c7eff2e0ebab39f2b917a 100644 (file)
@@ -58,7 +58,11 @@ struct sw_flow {
     time_t timeout;             /* When the flow expires (if idle). */
     uint64_t packet_count;      /* Number of packets seen. */
     uint64_t byte_count;        /* Number of bytes seen. */
+
+    /* Private to table implementations. */
     struct list node;
+    struct list iter_node;
+    unsigned long int serial;
 
     /* Actions (XXX probably most flows have only a single action). */
     unsigned int n_actions;
index 47bbe4a9368f60796e2d69b3095b1d334c67b260..aaec5039daad8a56585e1ec7fca3d8a66437fe34 100644 (file)
@@ -159,56 +159,36 @@ static void table_hash_destroy(struct sw_table *swt)
     free(th);
 }
 
-struct swt_iterator_hash {
-    struct sw_table_hash *th;
-    unsigned int bucket_i;
-};
-
-static struct sw_flow *next_flow(struct swt_iterator_hash *ih)
-{
-    for (;ih->bucket_i <= ih->th->bucket_mask; ih->bucket_i++) {
-        struct sw_flow *f = ih->th->buckets[ih->bucket_i];
-        if (f != NULL)
-            return f;
-    }
-
-    return NULL;
-}
-
-static int table_hash_iterator(struct sw_table *swt,
-                               struct swt_iterator *swt_iter)
+static int table_hash_iterate(struct sw_table *swt,
+                              const struct sw_flow_key *key,
+                              struct sw_table_position *position,
+                              int (*callback)(struct sw_flow *, void *private),
+                              void *private) 
 {
-    struct swt_iterator_hash *ih;
-
-    swt_iter->private = ih = malloc(sizeof *ih);
+    struct sw_table_hash *th = (struct sw_table_hash *) swt;
 
-    if (ih == NULL)
+    if (position->private[0] > th->bucket_mask)
         return 0;
 
-    ih->th = (struct sw_table_hash *) swt;
-
-    ih->bucket_i = 0;
-    swt_iter->flow = next_flow(ih);
-
-    return 1;
-}
-
-static void table_hash_next(struct swt_iterator *swt_iter)
-{
-    struct swt_iterator_hash *ih;
-
-    if (swt_iter->flow == NULL)
-        return;
-
-    ih = (struct swt_iterator_hash *) swt_iter->private;
-
-    ih->bucket_i++;
-    swt_iter->flow = next_flow(ih);
-}
-
-static void table_hash_iterator_destroy(struct swt_iterator *swt_iter)
-{
-    free(swt_iter->private);
+    if (key->wildcards == 0) {
+        struct sw_flow *flow = table_hash_lookup(swt, key);
+        position->private[0] = -1;
+        return flow ? callback(flow, private) : 0;
+    } else {
+        int i;
+
+        for (i = position->private[0]; i <= th->bucket_mask; i++) {
+            struct sw_flow *flow = th->buckets[i];
+            if (flow && flow_matches(key, &flow->key)) {
+                int error = callback(flow, private);
+                if (error) {
+                    position->private[0] = i + 1;
+                    return error;
+                }
+            }
+        }
+        return 0;
+    }
 }
 
 static void table_hash_stats(struct sw_table *swt,
@@ -246,9 +226,7 @@ struct sw_table *table_hash_create(unsigned int polynomial,
     swt->delete = table_hash_delete;
     swt->timeout = table_hash_timeout;
     swt->destroy = table_hash_destroy;
-    swt->iterator = table_hash_iterator;
-    swt->iterator_next = table_hash_next;
-    swt->iterator_destroy = table_hash_iterator_destroy;
+    swt->iterate = table_hash_iterate;
     swt->stats = table_hash_stats;
 
     crc32_init(&th->crc32, polynomial);
@@ -310,79 +288,25 @@ static void table_hash2_destroy(struct sw_table *swt)
     free(t2);
 }
 
-struct swt_iterator_hash2 {
-    struct sw_table_hash2 *th2;
-    struct swt_iterator ih;
-    uint8_t table_i;
-};
-
-static int table_hash2_iterator(struct sw_table *swt,
-                                struct swt_iterator *swt_iter)
+static int table_hash2_iterate(struct sw_table *swt,
+                               const struct sw_flow_key *key,
+                               struct sw_table_position *position,
+                               int (*callback)(struct sw_flow *, void *),
+                               void *private)
 {
-    struct swt_iterator_hash2 *ih2;
-
-    swt_iter->private = ih2 = malloc(sizeof *ih2);
-    if (ih2 == NULL)
-        return 0;
-
-    ih2->th2 = (struct sw_table_hash2 *) swt;
-    if (!table_hash_iterator(ih2->th2->subtable[0], &ih2->ih)) {
-        free(ih2);
-        return 0;
-    }
-
-    if (ih2->ih.flow != NULL) {
-        swt_iter->flow = ih2->ih.flow;
-        ih2->table_i = 0;
-    } else {
-        table_hash_iterator_destroy(&ih2->ih);
-        ih2->table_i = 1;
-        if (!table_hash_iterator(ih2->th2->subtable[1], &ih2->ih)) {
-            free(ih2);
-            return 0;
-        }
-        swt_iter->flow = ih2->ih.flow;
-    }
-
-    return 1;
-}
-
-static void table_hash2_next(struct swt_iterator *swt_iter) 
-{
-    struct swt_iterator_hash2 *ih2;
-
-    if (swt_iter->flow == NULL)
-        return;
-
-    ih2 = (struct swt_iterator_hash2 *) swt_iter->private;
-    table_hash_next(&ih2->ih);
+    struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
+    int i;
 
-    if (ih2->ih.flow != NULL) {
-        swt_iter->flow = ih2->ih.flow;
-    } else {
-        if (ih2->table_i == 0) {
-            table_hash_iterator_destroy(&ih2->ih);
-            ih2->table_i = 1;
-            if (!table_hash_iterator(ih2->th2->subtable[1], &ih2->ih)) {
-                ih2->ih.private = NULL;
-                swt_iter->flow = NULL;
-            } else {
-                swt_iter->flow = ih2->ih.flow;
-            }
-        } else {
-            swt_iter->flow = NULL;
+    for (i = position->private[1]; i < 2; i++) {
+        int error = table_hash_iterate(t2->subtable[i], key, position,
+                                       callback, private);
+        if (error) {
+            return error;
         }
+        position->private[0] = 0;
+        position->private[1]++;
     }
-}
-
-static void table_hash2_iterator_destroy(struct swt_iterator *swt_iter)
-{
-    struct swt_iterator_hash2 *ih2;
-
-    ih2 = (struct swt_iterator_hash2 *) swt_iter->private;
-    if (ih2->ih.private != NULL)
-        table_hash_iterator_destroy(&ih2->ih);
-    free(ih2);
+    return 0;
 }
 
 static void table_hash2_stats(struct sw_table *swt,
@@ -424,12 +348,9 @@ struct sw_table *table_hash2_create(unsigned int poly0, unsigned int buckets0,
     swt->delete = table_hash2_delete;
     swt->timeout = table_hash2_timeout;
     swt->destroy = table_hash2_destroy;
+    swt->iterate = table_hash2_iterate;
     swt->stats = table_hash2_stats;
 
-    swt->iterator = table_hash2_iterator;
-    swt->iterator_next = table_hash2_next;
-    swt->iterator_destroy = table_hash2_iterator_destroy;
-
     return swt;
 
 out_free_subtable0:
index e6c6a2ffbe45154a3a87d91b57bd0cc8af4075e0..e6b8aed6ab52f1ac9e943578674667ef2aee8a74 100644 (file)
@@ -44,6 +44,8 @@ struct sw_table_linear {
     unsigned int max_flows;
     unsigned int n_flows;
     struct list flows;
+    struct list iter_flows;
+    unsigned long int next_serial;
 };
 
 static struct sw_flow *table_linear_lookup(struct sw_table *swt,
@@ -71,7 +73,9 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
         if (f->priority == flow->priority
                 && f->key.wildcards == flow->key.wildcards
                 && flow_matches(&f->key, &flow->key)) {
+            flow->serial = f->serial;
             list_replace(&flow->node, &f->node);
+            list_replace(&flow->iter_node, &f->iter_node);
             flow_free(f);
             return 1;
         }
@@ -87,7 +91,8 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
     tl->n_flows++;
 
     /* Insert the entry immediately in front of where we're pointing. */
-    list_push_back(&f->node, &flow->node);
+    list_insert(&f->node, &flow->node);
+    list_push_front(&tl->iter_flows, &flow->iter_node);
 
     return 1;
 }
@@ -96,6 +101,7 @@ static void
 do_delete(struct sw_flow *flow) 
 {
     list_remove(&flow->node);
+    list_remove(&flow->iter_node);
     flow_free(flow);
 }
 
@@ -126,6 +132,7 @@ static void table_linear_timeout(struct sw_table *swt, struct list *deleted)
     LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &tl->flows) {
         if (flow_timeout(flow)) {
             list_remove(&flow->node);
+            list_remove(&flow->iter_node);
             list_push_back(deleted, &flow->node);
             tl->n_flows--;
         }
@@ -145,43 +152,29 @@ static void table_linear_destroy(struct sw_table *swt)
     free(tl);
 }
 
-/* Linear table's private data is just a pointer to the table */
-
-static int table_linear_iterator(struct sw_table *swt,
-                                 struct swt_iterator *swt_iter) 
+static int table_linear_iterate(struct sw_table *swt,
+                                const struct sw_flow_key *key,
+                                struct sw_table_position *position,
+                                int (*callback)(struct sw_flow *, void *),
+                                void *private)
 {
     struct sw_table_linear *tl = (struct sw_table_linear *) swt;
-
-    swt_iter->private = tl;
-
-    if (!tl->n_flows)
-        swt_iter->flow = NULL;
-    else
-        swt_iter->flow = CONTAINER_OF(list_front(&tl->flows), struct sw_flow, node);
-
-    return 1;
-}
-
-static void table_linear_next(struct swt_iterator *swt_iter)
-{
-    struct sw_table_linear *tl;
-    struct list *next;
-
-    if (swt_iter->flow == NULL)
-        return;
-
-    tl = (struct sw_table_linear *) swt_iter->private;
-
-    next = swt_iter->flow->node.next;
-    if (next == &tl->flows)
-        swt_iter->flow = NULL;
-    else
-        swt_iter->flow = CONTAINER_OF(next, struct sw_flow, node);
+    struct sw_flow *flow;
+    unsigned long start;
+
+    start = ~position->private[0];
+    LIST_FOR_EACH (flow, struct sw_flow, iter_node, &tl->iter_flows) {
+        if (flow->serial <= start && flow_matches(key, &flow->key)) {
+            int error = callback(flow, private);
+            if (error) {
+                position->private[0] = ~(flow->serial - 1);
+                return error;
+            }
+        }
+    }
+    return 0;
 }
 
-static void table_linear_iterator_destroy(struct swt_iterator *swt_iter)
-{}
-
 static void table_linear_stats(struct sw_table *swt,
                                struct sw_table_stats *stats)
 {
@@ -207,15 +200,14 @@ struct sw_table *table_linear_create(unsigned int max_flows)
     swt->delete = table_linear_delete;
     swt->timeout = table_linear_timeout;
     swt->destroy = table_linear_destroy;
+    swt->iterate = table_linear_iterate;
     swt->stats = table_linear_stats;
 
-    swt->iterator = table_linear_iterator;
-    swt->iterator_next = table_linear_next;
-    swt->iterator_destroy = table_linear_iterator_destroy;
-
     tl->max_flows = max_flows;
     tl->n_flows = 0;
     list_init(&tl->flows);
+    list_init(&tl->iter_flows);
+    tl->next_serial = 0;
 
     return swt;
 }
diff --git a/switch/table-mac.c b/switch/table-mac.c
deleted file mode 100644 (file)
index 5d2d0e9..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
- * Junior University
- * 
- * We are making the OpenFlow specification and associated documentation
- * (Software) available for public use and benefit with the expectation
- * that others will use, modify and enhance the Software and contribute
- * those enhancements back to the community. However, since we would
- * like to make the Software available for broadest use, with as few
- * restrictions as possible permission is hereby granted, free of
- * charge, to any person obtaining a copy of this Software to deal in
- * the Software under the copyrights without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- * 
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- * 
- * The name and trademarks of copyright holder(s) may NOT be used in
- * advertising or publicity pertaining to the Software or any
- * derivatives without specific, written prior permission.
- */
-
-#include "table.h"
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include "crc32.h"
-#include "switch-flow.h"
-#include "openflow.h"
-#include "datapath.h"
-
-struct sw_table_mac {
-    struct sw_table swt;
-    struct crc32 crc32;
-    unsigned int n_flows;
-    unsigned int max_flows;
-    unsigned int bucket_mask; /* Number of buckets minus 1. */
-    struct list *buckets;
-};
-
-static struct list *find_bucket(struct sw_table *swt,
-                                const struct sw_flow_key *key)
-{
-    struct sw_table_mac *tm = (struct sw_table_mac *) swt;
-    unsigned int crc = crc32_calculate(&tm->crc32, key, sizeof *key);
-    return &tm->buckets[crc & tm->bucket_mask];
-}
-
-static struct sw_flow *table_mac_lookup(struct sw_table *swt,
-                                        const struct sw_flow_key *key)
-{
-    struct list *bucket = find_bucket(swt, key);
-    struct sw_flow *flow;
-    LIST_FOR_EACH (flow, struct sw_flow, node, bucket) {
-        if (!memcmp(key->flow.dl_src, flow->key.flow.dl_src, 6)) {
-            return flow; 
-        }
-    }
-    return NULL;
-}
-
-static int table_mac_insert(struct sw_table *swt, struct sw_flow *flow)
-{
-    struct sw_table_mac *tm = (struct sw_table_mac *) swt;
-    struct list *bucket;
-    struct sw_flow *f;
-
-    /* MAC table only handles flows that match on Ethernet
-       source address and wildcard everything else. */
-    if (flow->key.wildcards != (OFPFW_ALL & ~OFPFW_DL_SRC))
-        return 0;
-    bucket = find_bucket(swt, &flow->key);
-
-    LIST_FOR_EACH (f, struct sw_flow, node, bucket) {
-        if (!memcmp(f->key.flow.dl_src, flow->key.flow.dl_src, 6)) {
-            list_replace(&flow->node, &f->node);
-            flow_free(f);
-            return 1;
-        }
-    }
-
-    /* Table overflow? */
-    if (tm->n_flows >= tm->max_flows) {
-        return 0; 
-    }
-    tm->n_flows++;
-
-    list_push_front(bucket, &flow->node);
-    return 1;
-}
-
-static void
-do_delete(struct sw_flow *flow)
-{
-    list_remove(&flow->node);
-    flow_free(flow);
-}
-
-/* Returns number of deleted flows. */
-static int table_mac_delete(struct sw_table *swt,
-                            const struct sw_flow_key *key, int strict)
-{
-    struct sw_table_mac *tm = (struct sw_table_mac *) swt;
-
-    if (key->wildcards == (OFPFW_ALL & ~OFPFW_DL_SRC)) {
-        struct sw_flow *flow = table_mac_lookup(swt, key);
-        if (flow) {
-            do_delete(flow);
-            tm->n_flows--;
-            return 1;
-        }
-        return 0;
-    } else {
-        unsigned int i;
-        int count = 0;
-        for (i = 0; i <= tm->bucket_mask; i++) {
-            struct list *bucket = &tm->buckets[i];
-            struct sw_flow *flow, *next;
-            LIST_FOR_EACH_SAFE (flow, next, struct sw_flow, node, bucket) {
-                if (flow_del_matches(&flow->key, key, strict)) {
-                    do_delete(flow);
-                    count++;
-                }
-            }
-        }
-        tm->n_flows -= count;
-        return count;
-    }
-}
-
-static void table_mac_timeout(struct sw_table *swt, struct list *deleted)
-{
-    struct sw_table_mac *tm = (struct sw_table_mac *) swt;
-    unsigned int i;
-
-    for (i = 0; i <= tm->bucket_mask; i++) {
-        struct list *bucket = &tm->buckets[i];
-        struct sw_flow *flow, *next;
-        LIST_FOR_EACH_SAFE (flow, next, struct sw_flow, node, bucket) {
-            if (flow_timeout(flow)) {
-                list_remove(&flow->node);
-                list_push_back(deleted, &flow->node);
-                tm->n_flows--;
-            }
-        }
-    }
-}
-
-static void table_mac_destroy(struct sw_table *swt)
-{
-    struct sw_table_mac *tm = (struct sw_table_mac *) swt;
-    unsigned int i;
-    for (i = 0; i <= tm->bucket_mask; i++) {
-        struct list *list = &tm->buckets[i];
-        while (!list_is_empty(list)) {
-            struct sw_flow *flow = CONTAINER_OF(list_front(list),
-                                                struct sw_flow, node);
-            list_remove(&flow->node);
-            flow_free(flow);
-        }
-    }
-    free(tm->buckets);
-    free(tm);
-}
-
-struct swt_iterator_mac {
-    struct sw_table_mac *tm;
-    unsigned int bucket_i;
-};
-
-static struct sw_flow *next_head_flow(struct swt_iterator_mac *im)
-{
-    for (; im->bucket_i <= im->tm->bucket_mask; im->bucket_i++) {
-        struct list *bucket = &im->tm->buckets[im->bucket_i];
-        if (!list_is_empty(bucket)) {
-            return CONTAINER_OF(bucket, struct sw_flow, node);
-        }
-    }
-    return NULL;
-}
-
-static int table_mac_iterator(struct sw_table *swt,
-                              struct swt_iterator *swt_iter)
-{
-    struct swt_iterator_mac *im;
-
-    swt_iter->private = im = malloc(sizeof *im);
-    if (im == NULL)
-        return 0;
-
-    im->tm = (struct sw_table_mac *) swt;
-
-    if (!im->tm->n_flows)
-        swt_iter->flow = NULL;
-    else {
-        im->bucket_i = 0;
-        swt_iter->flow = next_head_flow(im);
-    }
-
-    return 1;
-}
-
-static void table_mac_next(struct swt_iterator *swt_iter)
-{
-    struct swt_iterator_mac *im;
-    struct list *next;
-
-    if (swt_iter->flow == NULL)
-        return;
-
-    im = (struct swt_iterator_mac *) swt_iter->private;
-
-    next = swt_iter->flow->node.next;
-    if (next != NULL) {
-        swt_iter->flow = CONTAINER_OF(next, struct sw_flow, node);
-    } else {
-        im->bucket_i++;
-        swt_iter->flow = next_head_flow(im);
-    }
-}
-
-static void table_mac_iterator_destroy(struct swt_iterator *swt_iter)
-{
-    free(swt_iter->private);
-}
-
-static void table_mac_stats(struct sw_table *swt, struct sw_table_stats *stats)
-{
-    struct sw_table_mac *tm = (struct sw_table_mac *) swt;
-    stats->name = "mac";
-    stats->n_flows = tm->n_flows;
-    stats->max_flows = tm->max_flows;
-}
-
-struct sw_table *table_mac_create(unsigned int n_buckets,
-                                  unsigned int max_flows)
-{
-    struct sw_table_mac *tm;
-    struct sw_table *swt;
-    unsigned int i;
-
-    tm = calloc(1, sizeof *tm);
-    if (tm == NULL)
-        return NULL;
-
-    assert(!(n_buckets & (n_buckets - 1)));
-
-    tm->buckets = malloc(n_buckets * sizeof *tm->buckets);
-    if (tm->buckets == NULL) {
-        printf("failed to allocate %u buckets\n", n_buckets);
-        free(tm);
-        return NULL;
-    }
-    for (i = 0; i < n_buckets; i++) {
-        list_init(&tm->buckets[i]);
-    }
-    tm->bucket_mask = n_buckets - 1;
-
-    swt = &tm->swt;
-    swt->lookup = table_mac_lookup;
-    swt->insert = table_mac_insert;
-    swt->delete = table_mac_delete;
-    swt->timeout = table_mac_timeout;
-    swt->destroy = table_mac_destroy;
-    swt->stats = table_mac_stats;
-
-    swt->iterator = table_mac_iterator;
-    swt->iterator_next = table_mac_next;
-    swt->iterator_destroy = table_mac_iterator_destroy;
-
-    crc32_init(&tm->crc32, 0x04C11DB7); /* Ethernet CRC. */
-    tm->n_flows = 0;
-    tm->max_flows = max_flows;
-
-    return swt;
-}
index 15743387b56c0a992cf80e360af8de0d47c9d84d..c86380a04d4eac02ba4cdd8d182817c8f519da79 100644 (file)
@@ -43,12 +43,6 @@ struct sw_flow;
 struct sw_flow_key;
 struct list;
 
-/* Iterator through the flows stored in a table. */
-struct swt_iterator {
-    struct sw_flow *flow;   /* Current flow, for use by client. */
-    void *private;
-};
-
 /* Table statistics. */
 struct sw_table_stats {
     const char *name;       /* Human-readable name. */
@@ -56,6 +50,14 @@ struct sw_table_stats {
     unsigned long int max_flows; /* Flow capacity. */
 };
 
+/* Position within an iteration of a sw_table.
+ *
+ * The contents are private to the table implementation, except that a position
+ * initialized to all-zero-bits represents the start of a table. */
+struct sw_table_position {
+       unsigned long private[4];
+};
+
 /* A single table of flows.  */
 struct sw_table {
     /* Searches 'table' for a flow matching 'key', which must not have any
@@ -87,16 +89,28 @@ struct sw_table {
     /* Destroys 'table', which must not have any users. */
     void (*destroy)(struct sw_table *table);
 
-    int (*iterator)(struct sw_table *, struct swt_iterator *);
-    void (*iterator_next)(struct swt_iterator *);
-    void (*iterator_destroy)(struct swt_iterator *);
+       /* Iterates through the flow entries in 'table', passing each one
+        * matches 'key' to 'callback'.  The callback function should return 0
+        * to continue iteration or a nonzero error code to stop.  The iterator
+        * function returns either 0 if the table iteration completed or the
+        * value returned by the callback function otherwise.
+        *
+        * The iteration starts at 'position', which may be initialized to
+        * all-zero-bits to iterate from the beginning of the table.  If the
+        * iteration terminates due to an error from the callback function,
+        * 'position' is updated to a value that can be passed back to the
+        * iterator function to resume iteration later with the following
+        * flow. */
+       int (*iterate)(struct sw_table *table,
+                      const struct sw_flow_key *key,
+                      struct sw_table_position *position,
+                      int (*callback)(struct sw_flow *flow, void *private),
+                      void *private);
 
     /* Dumps statistics for 'table' into 'stats'. */
     void (*stats)(struct sw_table *table, struct sw_table_stats *stats);
 };
 
-struct sw_table *table_mac_create(unsigned int n_buckets,
-                                  unsigned int max_flows);
 struct sw_table *table_hash_create(unsigned int polynomial,
                                    unsigned int n_buckets);
 struct sw_table *table_hash2_create(unsigned int poly0, unsigned int buckets0,
index f6d851d3078c4a3ce26996287cb225bf11023c51..a66975a0c6525bcc9d8374835cb87ba45fdb667b 100644 (file)
@@ -344,6 +344,17 @@ alloc_openflow_buffer(size_t openflow_len, uint8_t type,
        return oh;
 }
 
+static void *
+alloc_stats_request(size_t body_len, uint16_t type, struct buffer **bufferp)
+{
+    struct ofp_stats_request *rq;
+    rq = alloc_openflow_buffer((offsetof(struct ofp_stats_request, body)
+                                + body_len), OFPT_STATS_REQUEST, bufferp);
+    rq->type = htons(type);
+    rq->flags = htons(0);
+    return rq->body;
+}
+
 static void
 send_openflow_buffer(struct vconn *vconn, struct buffer *buffer)
 {
@@ -378,30 +389,76 @@ transact_openflow(struct vconn *vconn, struct buffer *request)
 }
 
 static void
-dump_transaction(const char *vconn_name, uint8_t request_type)
+dump_transaction(const char *vconn_name, struct buffer *request)
 {
     struct vconn *vconn;
-    struct buffer *request, *reply;
+    struct buffer *reply;
 
     run(vconn_open_block(vconn_name, &vconn), "connecting to %s", vconn_name);
-    alloc_openflow_buffer(sizeof(struct ofp_header), request_type, &request);
     reply = transact_openflow(vconn, request);
     ofp_print(stdout, reply->data, reply->size, 1);
     vconn_close(vconn);
 }
 
+static void
+dump_trivial_transaction(const char *vconn_name, uint8_t request_type)
+{
+    struct buffer *request;
+    alloc_openflow_buffer(sizeof(struct ofp_header), request_type, &request);
+    dump_transaction(vconn_name, request);
+}
+
+static void
+dump_stats_transaction(const char *vconn_name, struct buffer *request)
+{
+    uint32_t send_xid = ((struct ofp_header *) request->data)->xid;
+    struct vconn *vconn;
+    bool done = false;
+
+    run(vconn_open_block(vconn_name, &vconn), "connecting to %s", vconn_name);
+    send_openflow_buffer(vconn, request);
+    while (!done) {
+        uint32_t recv_xid;
+        struct buffer *reply;
+
+        run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
+        recv_xid = ((struct ofp_header *) reply->data)->xid;
+        if (send_xid == recv_xid) {
+            struct ofp_stats_reply *osr;
+
+            ofp_print(stdout, reply->data, reply->size, 1);
+
+            osr = buffer_at(reply, 0, sizeof *osr);
+            done = !osr || !(ntohs(osr->flags) & OFPSF_REPLY_MORE);
+        } else {
+            VLOG_DBG("received reply with xid %08"PRIx32" "
+                     "!= expected %08"PRIx32, recv_xid, send_xid);
+        }
+        buffer_delete(reply);
+    }
+    vconn_close(vconn);
+}
+
+static void
+dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
+{
+    struct buffer *request;
+    alloc_stats_request(0, stats_type, &request);
+    dump_stats_transaction(vconn_name, request);
+}
+
 static void
 do_show(int argc UNUSED, char *argv[])
 {
-    dump_transaction(argv[1], OFPT_FEATURES_REQUEST);
-    dump_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
+    dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
+    dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
 }
 
 
 static void
 do_dump_tables(int argc, char *argv[])
 {
-    dump_transaction(argv[1], OFPT_TABLE_STATS_REQUEST);
+    dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
 }
 
 
@@ -561,46 +618,16 @@ str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action,
 
 static void do_dump_flows(int argc, char *argv[])
 {
-    struct vconn *vconn;
-    struct buffer *request;
     struct ofp_flow_stats_request *req;
-    uint32_t send_xid;
-    bool done = false;
+    struct buffer *request;
 
-    run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
-    req = alloc_openflow_buffer(sizeof *req, OFPT_FLOW_STATS_REQUEST,
-                                &request);
+    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;
 
-    send_xid = ((struct ofp_header *) request->data)->xid;
-    send_openflow_buffer(vconn, request);
-    while (!done) {
-        uint32_t recv_xid;
-        struct buffer *reply;
-
-        run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
-        recv_xid = ((struct ofp_header *) reply->data)->xid;
-        if (send_xid == recv_xid) {
-            struct ofp_flow_stats_reply *rpy;
-
-            ofp_print(stdout, reply->data, reply->size, 1);
-
-            rpy = buffer_at(reply, 0, sizeof *rpy);
-            done = (!rpy
-                    || rpy->header.version != OFP_VERSION
-                    || rpy->header.type != OFPT_FLOW_STATS_REPLY
-                    || (ntohs(rpy->header.length)
-                        < sizeof rpy->header + sizeof *rpy->flows));
-        } else {
-            VLOG_DBG("received reply with xid %08"PRIx32" "
-                     "!= expected %08"PRIx32, recv_xid, send_xid);
-        }
-        buffer_delete(reply);
-    }
-    vconn_close(vconn);
+    dump_stats_transaction(argv[1], request);
 }
 
 static void do_add_flows(int argc, char *argv[])
@@ -679,7 +706,7 @@ static void do_del_flows(int argc, char *argv[])
 static void
 do_dump_ports(int argc, char *argv[])
 {
-    dump_transaction(argv[1], OFPT_PORT_STATS_REQUEST);
+    dump_trivial_stats_transaction(argv[1], OFPST_PORT);
 }
 
 static void do_help(int argc UNUSED, char *argv[] UNUSED)