datapath: Change listing ports to use an iterator concept.
[sliver-openvswitch.git] / lib / dpif.c
index 1bf6e41..708b534 100644 (file)
@@ -83,7 +83,8 @@ static void log_flow_operation(const struct dpif *, const char *operation,
 static void log_flow_put(struct dpif *, int error,
                          const struct odp_flow_put *);
 static bool should_log_flow_message(int error);
-static void check_rw_odp_flow(struct odp_flow *);
+static void check_rw_flow_actions(struct odp_flow *);
+static void check_rw_flow_key(struct odp_flow *);
 
 static void
 dp_initialize(void)
@@ -555,60 +556,62 @@ dpif_port_get_name(struct dpif *dpif, uint16_t port_no,
     return error;
 }
 
-/* Obtains a list of all the ports in 'dpif'.
+/* Initializes 'dump' to begin dumping the ports in a dpif.
  *
- * If successful, returns 0 and sets '*portsp' to point to an array of
- * appropriately initialized port structures and '*n_portsp' to the number of
- * ports in the array.  The caller is responsible for freeing '*portp' by
- * calling free().
+ * This function provides no status indication.  An error status for the entire
+ * dump operation is provided when it is completed by calling
+ * dpif_port_dump_done().
+ */
+void
+dpif_port_dump_start(struct dpif_port_dump *dump, const struct dpif *dpif)
+{
+    dump->dpif = dpif;
+    dump->error = dpif->dpif_class->port_dump_start(dpif, &dump->state);
+    log_operation(dpif, "port_dump_start", dump->error);
+}
+
+/* Attempts to retrieve another port from 'dump', which must have been
+ * initialized with dpif_port_dump_start().  On success, stores a new odp_port
+ * into 'port' and returns true.  On failure, returns false.
  *
- * On failure, returns a positive errno value and sets '*portsp' to NULL and
- * '*n_portsp' to 0. */
-int
-dpif_port_list(const struct dpif *dpif,
-               struct odp_port **portsp, size_t *n_portsp)
+ * Failure might indicate an actual error or merely that the last port has been
+ * dumped.  An error status for the entire dump operation is provided when it
+ * is completed by calling dpif_port_dump_done(). */
+bool
+dpif_port_dump_next(struct dpif_port_dump *dump, struct odp_port *port)
 {
-    struct odp_port *ports;
-    size_t n_ports = 0;
-    int error;
+    const struct dpif *dpif = dump->dpif;
 
-    for (;;) {
-        struct odp_stats stats;
-        int retval;
+    if (dump->error) {
+        return false;
+    }
 
-        error = dpif_get_dp_stats(dpif, &stats);
-        if (error) {
-            goto exit;
-        }
+    dump->error = dpif->dpif_class->port_dump_next(dpif, dump->state, port);
+    if (dump->error == EOF) {
+        VLOG_DBG_RL(&dpmsg_rl, "%s: dumped all ports", dpif_name(dpif));
+    } else {
+        log_operation(dpif, "port_dump_next", dump->error);
+    }
 
-        ports = xcalloc(stats.n_ports, sizeof *ports);
-        retval = dpif->dpif_class->port_list(dpif, ports, stats.n_ports);
-        if (retval < 0) {
-            /* Hard error. */
-            error = -retval;
-            free(ports);
-            goto exit;
-        } else if (retval <= stats.n_ports) {
-            /* Success. */
-            error = 0;
-            n_ports = retval;
-            goto exit;
-        } else {
-            /* Soft error: port count increased behind our back.  Try again. */
-            free(ports);
-        }
+    if (dump->error) {
+        dpif->dpif_class->port_dump_done(dpif, dump->state);
+        return false;
     }
+    return true;
+}
 
-exit:
-    if (error) {
-        *portsp = NULL;
-        *n_portsp = 0;
-    } else {
-        *portsp = ports;
-        *n_portsp = n_ports;
+/* Completes port table dump operation 'dump', which must have been initialized
+ * with dpif_port_dump_start().  Returns 0 if the dump operation was
+ * error-free, otherwise a positive errno value describing the problem. */
+int
+dpif_port_dump_done(struct dpif_port_dump *dump)
+{
+    const struct dpif *dpif = dump->dpif;
+    if (!dump->error) {
+        dump->error = dpif->dpif_class->port_dump_done(dpif, dump->state);
+        log_operation(dpif, "port_dump_done", dump->error);
     }
-    log_operation(dpif, "port_list", error);
-    return error;
+    return dump->error == EOF ? 0 : dump->error;
 }
 
 /* Polls for changes in the set of ports in 'dpif'.  If the set of ports in
@@ -679,7 +682,7 @@ dpif_flow_get(const struct dpif *dpif, struct odp_flow *flow)
 
     COVERAGE_INC(dpif_flow_get);
 
-    check_rw_odp_flow(flow);
+    check_rw_flow_actions(flow);
     error = dpif->dpif_class->flow_get(dpif, flow, 1);
     if (!error) {
         error = flow->stats.error;
@@ -732,7 +735,7 @@ dpif_flow_get_multiple(const struct dpif *dpif,
     COVERAGE_ADD(dpif_flow_get, n);
 
     for (i = 0; i < n; i++) {
-        check_rw_odp_flow(&flows[i]);
+        check_rw_flow_actions(&flows[i]);
     }
 
     error = dpif->dpif_class->flow_get(dpif, flows, n);
@@ -782,7 +785,7 @@ dpif_flow_del(struct dpif *dpif, struct odp_flow *flow)
 
     COVERAGE_INC(dpif_flow_del);
 
-    check_rw_odp_flow(flow);
+    check_rw_flow_actions(flow);
     memset(&flow->stats, 0, sizeof flow->stats);
 
     error = dpif->dpif_class->flow_del(dpif, flow);
@@ -824,7 +827,8 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump, struct odp_flow *flow)
 {
     const struct dpif *dpif = dump->dpif;
 
-    check_rw_odp_flow(flow);
+    check_rw_flow_actions(flow);
+    check_rw_flow_key(flow);
 
     if (dump->error) {
         return false;
@@ -834,6 +838,9 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump, struct odp_flow *flow)
     if (dump->error == EOF) {
         VLOG_DBG_RL(&dpmsg_rl, "%s: dumped all flows", dpif_name(dpif));
     } else {
+        if (dump->error) {
+            flow->key_len = 0;
+        }
         if (should_log_flow_message(dump->error)) {
             log_flow_operation(dpif, "flow_dump_next", dump->error, flow);
         }
@@ -954,39 +961,38 @@ dpif_set_sflow_probability(struct dpif *dpif, uint32_t probability)
     return error;
 }
 
-/* Attempts to receive a message from 'dpif'.  If successful, stores the
- * message into '*packetp'.  The message, if one is received, will begin with
- * 'struct odp_msg' as a header, and will have at least DPIF_RECV_MSG_PADDING
- * bytes of headroom.  Only messages of the types selected with
- * dpif_set_listen_mask() will ordinarily be received (but if a message type is
+/* Polls for an upcall from 'dpif'.  If successful, stores the upcall into
+ * '*upcall'.  Only upcalls of the types selected with the set_listen_mask
+ * member function will ordinarily be received (but if a message type is
  * enabled and then later disabled, some stragglers might pop up).
  *
+ * The caller takes ownership of the data that 'upcall' points to.
+ * 'upcall->key' and 'upcall->actions' (if nonnull) point into data owned by
+ * 'upcall->packet', so their memory cannot be freed separately.  (This is
+ * hardly a great way to do things but it works out OK for the dpif providers
+ * and clients that exist so far.)
+ *
  * Returns 0 if successful, otherwise a positive errno value.  Returns EAGAIN
- * if no message is immediately available. */
+ * if no upcall is immediately available. */
 int
-dpif_recv(struct dpif *dpif, struct ofpbuf **packetp)
+dpif_recv(struct dpif *dpif, struct dpif_upcall *upcall)
 {
-    int error = dpif->dpif_class->recv(dpif, packetp);
-    if (!error) {
-        struct ofpbuf *buf = *packetp;
-
-        assert(ofpbuf_headroom(buf) >= DPIF_RECV_MSG_PADDING);
-        if (VLOG_IS_DBG_ENABLED()) {
-            struct odp_msg *msg = buf->data;
-            void *payload = msg + 1;
-            size_t payload_len = buf->size - sizeof *msg;
-            char *s = ofp_packet_to_string(payload, payload_len, payload_len);
-            VLOG_DBG_RL(&dpmsg_rl, "%s: received %s message of length "
-                        "%zu on port %"PRIu16": %s", dpif_name(dpif),
-                        (msg->type == _ODPL_MISS_NR ? "miss"
-                         : msg->type == _ODPL_ACTION_NR ? "action"
-                         : msg->type == _ODPL_SFLOW_NR ? "sFlow"
-                         : "<unknown>"),
-                        payload_len, msg->port, s);
-            free(s);
-        }
-    } else {
-        *packetp = NULL;
+    int error = dpif->dpif_class->recv(dpif, upcall);
+    if (!error && !VLOG_DROP_DBG(&dpmsg_rl)) {
+        struct flow flow;
+        char *s;
+
+        s = ofp_packet_to_string(upcall->packet->data,
+                                 upcall->packet->size, upcall->packet->size);
+        odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
+
+        VLOG_DBG("%s: %s upcall on port %"PRIu16": %s", dpif_name(dpif),
+                 (upcall->type == _ODPL_MISS_NR ? "miss"
+                  : upcall->type == _ODPL_ACTION_NR ? "action"
+                  : upcall->type == _ODPL_SFLOW_NR ? "sFlow"
+                  : "<unknown>"),
+                 flow.in_port, s);
+        free(s);
     }
     return error;
 }
@@ -1008,12 +1014,12 @@ dpif_recv_purge(struct dpif *dpif)
     }
 
     for (i = 0; i < stats.max_miss_queue + stats.max_action_queue + stats.max_sflow_queue; i++) {
-        struct ofpbuf *buf;
-        error = dpif_recv(dpif, &buf);
+        struct dpif_upcall upcall;
+        error = dpif_recv(dpif, &upcall);
         if (error) {
             return error == EAGAIN ? 0 : error;
         }
-        ofpbuf_delete(buf);
+        ofpbuf_delete(upcall.packet);
     }
     return 0;
 }
@@ -1117,7 +1123,7 @@ should_log_flow_message(int error)
 
 static void
 log_flow_message(const struct dpif *dpif, int error, const char *operation,
-                 const struct odp_flow_key *flow,
+                 const struct nlattr *key, size_t key_len,
                  const struct odp_flow_stats *stats,
                  const struct nlattr *actions, size_t actions_len)
 {
@@ -1130,7 +1136,7 @@ log_flow_message(const struct dpif *dpif, int error, const char *operation,
     if (error) {
         ds_put_format(&ds, "(%s) ", strerror(error));
     }
-    format_odp_flow_key(&ds, flow);
+    odp_flow_key_format(key, key_len, &ds);
     if (stats) {
         ds_put_cstr(&ds, ", ");
         format_odp_flow_stats(&ds, stats);
@@ -1148,10 +1154,11 @@ log_flow_operation(const struct dpif *dpif, const char *operation, int error,
                    struct odp_flow *flow)
 {
     if (error) {
+        flow->key_len = 0;
         flow->actions_len = 0;
     }
-    log_flow_message(dpif, error, operation, &flow->key,
-                     !error ? &flow->stats : NULL,
+    log_flow_message(dpif, error, operation,
+                     flow->key, flow->key_len, !error ? &flow->stats : NULL,
                      flow->actions, flow->actions_len);
 }
 
@@ -1175,7 +1182,8 @@ log_flow_put(struct dpif *dpif, int error, const struct odp_flow_put *put)
     if (put->flags & ~ODPPF_ALL) {
         ds_put_format(&s, "[%x]", put->flags & ~ODPPF_ALL);
     }
-    log_flow_message(dpif, error, ds_cstr(&s), &put->flow.key,
+    log_flow_message(dpif, error, ds_cstr(&s),
+                     put->flow.key, put->flow.key_len,
                      !error ? &put->flow.stats : NULL,
                      put->flow.actions, put->flow.actions_len);
     ds_destroy(&s);
@@ -1198,9 +1206,18 @@ log_flow_put(struct dpif *dpif, int error, const struct odp_flow_put *put)
  *        "actions" or "actions_len" was not initialized.
  */
 static void
-check_rw_odp_flow(struct odp_flow *flow)
+check_rw_flow_actions(struct odp_flow *flow)
 {
     if (flow->actions_len) {
         memset(&flow->actions[0], 0xcc, sizeof flow->actions[0]);
     }
 }
+
+/* Same as check_rw_flow_actions() but for flow->key. */
+static void
+check_rw_flow_key(struct odp_flow *flow)
+{
+    if (flow->key_len) {
+        memset(&flow->key[0], 0xcc, sizeof flow->key[0]);
+    }
+}