+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 = 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 sw_flow_actions *sf_acts = rcu_dereference(flow->sf_acts);
+ struct flow_stats_state *s = private;
+ struct ofp_flow_stats *ofs;
+ int length;
+ uint64_t duration;
+
+ length = sizeof *ofs + sf_acts->actions_len;
+ if (length + s->bytes_used > s->bytes_allocated)
+ return 1;
+
+ ofs = s->body + s->bytes_used;
+ ofs->length = htons(length);
+ ofs->table_id = s->table_idx;
+ ofs->pad = 0;
+ ofs->match.wildcards = htonl(flow->key.wildcards);
+ ofs->match.in_port = flow->key.in_port;
+ memcpy(ofs->match.dl_src, flow->key.dl_src, ETH_ALEN);
+ memcpy(ofs->match.dl_dst, flow->key.dl_dst, ETH_ALEN);
+ ofs->match.dl_vlan = flow->key.dl_vlan;
+ ofs->match.dl_type = flow->key.dl_type;
+ ofs->match.nw_src = flow->key.nw_src;
+ ofs->match.nw_dst = flow->key.nw_dst;
+ ofs->match.nw_proto = flow->key.nw_proto;
+ ofs->match.pad = 0;
+ ofs->match.tp_src = flow->key.tp_src;
+ ofs->match.tp_dst = flow->key.tp_dst;
+
+ /* The kernel doesn't support 64-bit division, so use the 'do_div'
+ * macro instead. The first argument is replaced with the quotient,
+ * while the remainder is the return value. */
+ duration = get_jiffies_64() - flow->created;
+ do_div(duration, HZ);
+ ofs->duration = htonl(duration);
+
+ ofs->priority = htons(flow->priority);
+ ofs->idle_timeout = htons(flow->idle_timeout);
+ ofs->hard_timeout = htons(flow->hard_timeout);
+ memset(ofs->pad2, 0, sizeof ofs->pad2);
+ ofs->packet_count = cpu_to_be64(flow->packet_count);
+ ofs->byte_count = cpu_to_be64(flow->byte_count);
+ memcpy(ofs->actions, sf_acts->actions, sf_acts->actions_len);
+
+ s->bytes_used += length;
+ return 0;
+}
+
+static int flow_stats_dump(struct datapath *dp, void *state,
+ void *body, int *body_len)
+{
+ struct flow_stats_state *s = state;
+ struct sw_flow_key match_key;
+ int error = 0;
+
+ s->bytes_used = 0;
+ s->bytes_allocated = *body_len;
+ s->body = body;
+
+ flow_extract_match(&match_key, &s->rq->match);
+ 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];
+
+ error = table->iterate(table, &match_key, s->rq->out_port,
+ &s->position, flow_stats_dump_callback, s);
+ if (error)
+ break;
+
+ s->table_idx++;
+ memset(&s->position, 0, sizeof s->position);
+ }
+ *body_len = s->bytes_used;
+
+ /* If error is 0, we're done.
+ * Otherwise, if some bytes were used, there are more flows to come.
+ * Otherwise, we were not able to fit even a single flow in the body,
+ * which indicates that we have a single flow with too many actions to
+ * fit. We won't ever make any progress at that rate, so give up. */
+ return !error ? 0 : s->bytes_used ? 1 : -ENOMEM;
+}
+
+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, rq->out_port, &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)
+{
+ struct ofp_table_stats *ots;
+ int n_bytes = dp->chain->n_tables * sizeof *ots;
+ int i;
+ if (n_bytes > *body_len)
+ return -ENOBUFS;
+ *body_len = n_bytes;
+ 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;
+ ots->wildcards = htonl(stats.wildcards);
+ memset(ots->pad, 0, sizeof ots->pad);
+ ots->max_entries = htonl(stats.max_flows);
+ ots->active_count = htonl(stats.n_flows);
+ ots->lookup_count = cpu_to_be64(stats.n_lookup);
+ ots->matched_count = cpu_to_be64(stats.n_matched);
+ }
+ 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 < DP_MAX_PORTS && 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_packets = cpu_to_be64(stats->rx_packets);
+ ops->tx_packets = cpu_to_be64(stats->tx_packets);
+ ops->rx_bytes = cpu_to_be64(stats->rx_bytes);
+ ops->tx_bytes = cpu_to_be64(stats->tx_bytes);
+ ops->rx_dropped = cpu_to_be64(stats->rx_dropped);
+ ops->tx_dropped = cpu_to_be64(stats->tx_dropped);
+ ops->rx_errors = cpu_to_be64(stats->rx_errors);
+ ops->tx_errors = cpu_to_be64(stats->tx_errors);
+ ops->rx_frame_err = cpu_to_be64(stats->rx_frame_errors);
+ ops->rx_over_err = cpu_to_be64(stats->rx_over_errors);
+ ops->rx_crc_err = cpu_to_be64(stats->rx_crc_errors);
+ ops->collisions = cpu_to_be64(stats->collisions);
+ 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_DESC] = {
+ 0,
+ 0,
+ NULL,
+ desc_stats_dump,
+ NULL
+ },
+ [OFPST_FLOW] = {
+ sizeof(struct ofp_flow_stats_request),
+ sizeof(struct ofp_flow_stats_request),
+ flow_stats_init,
+ 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,
+ NULL,
+ table_stats_dump,
+ NULL
+ },
+ [OFPST_PORT] = {
+ 0,
+ 0,
+ port_stats_init,
+ port_stats_dump,
+ port_stats_done
+ },