ofproto: Generalize facet_put__() so that it is more useful.
[openvswitch] / lib / dpif-linux.c
index 8e584ea5de051a012a823994495ba7ff1a67c3a8..8d5c48963673147e68c429e40a96a38409377646 100644 (file)
@@ -38,6 +38,7 @@
 #include "netdev.h"
 #include "netdev-vport.h"
 #include "netlink.h"
+#include "odp-util.h"
 #include "ofpbuf.h"
 #include "openvswitch/tunnel.h"
 #include "packets.h"
@@ -365,6 +366,14 @@ dpif_linux_port_query_by_name(const struct dpif *dpif, const char *devname,
     return dpif_linux_port_query__(dpif, 0, devname, dpif_port);
 }
 
+static int
+dpif_linux_get_max_ports(const struct dpif *dpif OVS_UNUSED)
+{
+    /* If the datapath increases its range of supported ports, then it should
+     * start reporting that. */
+    return 1024;
+}
+
 static int
 dpif_linux_flow_flush(struct dpif *dpif_)
 {
@@ -450,44 +459,159 @@ dpif_linux_port_poll_wait(const struct dpif *dpif_)
     }
 }
 
+static void
+odp_flow_stats_to_dpif_flow_stats(const struct odp_flow_stats *ofs,
+                                  struct dpif_flow_stats *dfs)
+{
+    dfs->n_packets = ofs->n_packets;
+    dfs->n_bytes = ofs->n_bytes;
+    dfs->used = ofs->used_sec * 1000 + ofs->used_nsec / 1000000;
+    dfs->tcp_flags = ofs->tcp_flags;
+}
+
 static int
-dpif_linux_flow_get(const struct dpif *dpif_, struct odp_flow flows[], int n)
+dpif_linux_flow_get(const struct dpif *dpif_, int flags,
+                    const struct nlattr *key, size_t key_len,
+                    struct ofpbuf **actionsp, struct dpif_flow_stats *stats)
 {
-    struct odp_flowvec fv;
-    fv.flows = flows;
-    fv.n_flows = n;
-    return do_ioctl(dpif_, ODP_FLOW_GET, &fv);
+    struct ofpbuf *actions = NULL;
+    struct odp_flow odp_flow;
+    int error;
+
+    memset(&odp_flow, 0, sizeof odp_flow);
+    odp_flow.key = (struct nlattr *) key;
+    odp_flow.key_len = key_len;
+    if (actionsp) {
+        actions = *actionsp = ofpbuf_new(65536);
+        odp_flow.actions = actions->base;
+        odp_flow.actions_len = actions->allocated;
+    }
+    odp_flow.flags = flags;
+
+    error = do_ioctl(dpif_, ODP_FLOW_GET, &odp_flow);
+    if (!error) {
+        if (stats) {
+            odp_flow_stats_to_dpif_flow_stats(&odp_flow.stats, stats);
+        }
+        if (actions) {
+            actions->size = odp_flow.actions_len;
+            ofpbuf_trim(actions);
+        }
+    } else {
+        if (actions) {
+            ofpbuf_delete(actions);
+        }
+    }
+    return error;
 }
 
 static int
-dpif_linux_flow_put(struct dpif *dpif_, struct odp_flow_put *put)
+dpif_linux_flow_put(struct dpif *dpif_, enum dpif_flow_put_flags flags,
+                    const struct nlattr *key, size_t key_len,
+                    const struct nlattr *actions, size_t actions_len,
+                    struct dpif_flow_stats *stats)
 {
-    return do_ioctl(dpif_, ODP_FLOW_PUT, put);
+    struct odp_flow_put put;
+    int error;
+
+    memset(&put, 0, sizeof put);
+    put.flow.key = (struct nlattr *) key;
+    put.flow.key_len = key_len;
+    put.flow.actions = (struct nlattr *) actions;
+    put.flow.actions_len = actions_len;
+    put.flow.flags = 0;
+    put.flags = 0;
+    if (flags & DPIF_FP_CREATE) {
+        put.flags |= ODPPF_CREATE;
+    }
+    if (flags & DPIF_FP_MODIFY) {
+        put.flags |= ODPPF_MODIFY;
+    }
+    if (flags & DPIF_FP_ZERO_STATS) {
+        put.flags |= ODPPF_ZERO_STATS;
+    }
+    error = do_ioctl(dpif_, ODP_FLOW_PUT, &put);
+    if (!error && stats) {
+        odp_flow_stats_to_dpif_flow_stats(&put.flow.stats, stats);
+    }
+    return error;
 }
 
 static int
-dpif_linux_flow_del(struct dpif *dpif_, struct odp_flow *flow)
+dpif_linux_flow_del(struct dpif *dpif_,
+                    const struct nlattr *key, size_t key_len,
+                    struct dpif_flow_stats *stats)
 {
-    return do_ioctl(dpif_, ODP_FLOW_DEL, flow);
+    struct odp_flow odp_flow;
+    int error;
+
+    memset(&odp_flow, 0, sizeof odp_flow);
+    odp_flow.key = (struct nlattr *) key;
+    odp_flow.key_len = key_len;
+    error = do_ioctl(dpif_, ODP_FLOW_DEL, &odp_flow);
+    if (!error && stats) {
+        odp_flow_stats_to_dpif_flow_stats(&odp_flow.stats, stats);
+    }
+    return error;
 }
 
+struct dpif_linux_flow_state {
+    struct odp_flow_dump dump;
+    struct odp_flow flow;
+    uint32_t keybuf[ODPUTIL_FLOW_KEY_U32S];
+    uint32_t actionsbuf[65536 / sizeof(uint32_t)];
+    struct dpif_flow_stats stats;
+};
+
 static int
 dpif_linux_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
 {
-    *statep = xzalloc(sizeof(struct odp_flow_dump));
+    struct dpif_linux_flow_state *state;
+
+    *statep = state = xmalloc(sizeof *state);
+    state->dump.state[0] = 0;
+    state->dump.state[1] = 0;
+    state->dump.flow = &state->flow;
     return 0;
 }
 
 static int
-dpif_linux_flow_dump_next(const struct dpif *dpif, void *state,
-                          struct odp_flow *flow)
+dpif_linux_flow_dump_next(const struct dpif *dpif, void *state_,
+                          const struct nlattr **key, size_t *key_len,
+                          const struct nlattr **actions, size_t *actions_len,
+                          const struct dpif_flow_stats **stats)
 {
-    struct odp_flow_dump *dump = state;
+    struct dpif_linux_flow_state *state = state_;
     int error;
 
-    dump->flow = flow;
-    error = do_ioctl(dpif, ODP_FLOW_DUMP, dump);
-    return error ? error : flow->flags & ODPFF_EOF ? EOF : 0;
+    memset(&state->flow, 0, sizeof state->flow);
+    state->flow.key = (struct nlattr *) state->keybuf;
+    state->flow.key_len = sizeof state->keybuf;
+    if (actions) {
+        state->flow.actions = (struct nlattr *) state->actionsbuf;
+        state->flow.actions_len = sizeof state->actionsbuf;
+    }
+
+    error = do_ioctl(dpif, ODP_FLOW_DUMP, &state->dump);
+    if (!error) {
+        if (state->flow.flags & ODPFF_EOF) {
+            return EOF;
+        }
+        if (key) {
+            *key = (const struct nlattr *) state->keybuf;
+            *key_len = state->flow.key_len;
+        }
+        if (actions) {
+            *actions = (const struct nlattr *) state->actionsbuf;
+            *actions_len = state->flow.actions_len;
+        }
+        if (stats) {
+            odp_flow_stats_to_dpif_flow_stats(&state->flow.stats,
+                                              &state->stats);
+            *stats = &state->stats;
+        }
+    }
+    return error;
 }
 
 static int
@@ -645,6 +769,26 @@ dpif_linux_recv_wait(struct dpif *dpif_)
     poll_fd_wait(dpif->fd, POLLIN);
 }
 
+static void
+dpif_linux_recv_purge(struct dpif *dpif_)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    int i;
+
+    /* This is somewhat bogus because it assumes that the following macros have
+     * fixed values, but it's going to go away later.  */
+#define DP_N_QUEUES 3
+#define DP_MAX_QUEUE_LEN 100
+    for (i = 0; i < DP_N_QUEUES * DP_MAX_QUEUE_LEN; i++) {
+        /* Reading even 1 byte discards a whole datagram and saves time. */
+        char buffer;
+
+        if (read(dpif->fd, &buffer, 1) != 1) {
+            break;
+        }
+    }
+}
+
 const struct dpif_class dpif_linux_class = {
     "system",
     NULL,
@@ -661,6 +805,7 @@ const struct dpif_class dpif_linux_class = {
     dpif_linux_port_del,
     dpif_linux_port_query_by_number,
     dpif_linux_port_query_by_name,
+    dpif_linux_get_max_ports,
     dpif_linux_port_dump_start,
     dpif_linux_port_dump_next,
     dpif_linux_port_dump_done,
@@ -681,6 +826,7 @@ const struct dpif_class dpif_linux_class = {
     dpif_linux_queue_to_priority,
     dpif_linux_recv,
     dpif_linux_recv_wait,
+    dpif_linux_recv_purge,
 };
 \f
 static int get_openvswitch_major(void);