ovs-controller: Improve QoS abilities.
authorBen Pfaff <blp@nicira.com>
Fri, 1 Oct 2010 20:41:40 +0000 (13:41 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 1 Oct 2010 20:41:40 +0000 (13:41 -0700)
This makes it a little easier to test Open vSwitch QoS features using
ovs-controller, by making it possible to assign queues on the basis of
input port, instead of just allowing a single queue for a whole switch.

CC: Michael Mao <mmao@nicira.com>
lib/learning-switch.c
lib/learning-switch.h
utilities/ovs-controller.8.in
utilities/ovs-controller.c

index 90749c018c6320b699e2cf2e0a4f5d0c4d9d2dc8..1e5d25bc2c037ee82966d35c597b335239537d97 100644 (file)
@@ -24,6 +24,7 @@
 #include <time.h>
 
 #include "flow.h"
+#include "hmap.h"
 #include "mac-learning.h"
 #include "ofpbuf.h"
 #include "ofp-parse.h"
@@ -33,6 +34,7 @@
 #include "poll-loop.h"
 #include "queue.h"
 #include "rconn.h"
+#include "shash.h"
 #include "timeval.h"
 #include "vconn.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(learning_switch)
 
+struct lswitch_port {
+    struct hmap_node hmap_node; /* Hash node for port number. */
+    uint16_t port_no;           /* OpenFlow port number, in host byte order. */
+    uint32_t queue_id;          /* OpenFlow queue number. */
+};
+
 struct lswitch {
     /* If nonnegative, the switch sets up flows that expire after the given
      * number of seconds (or never expire, if the value is OFP_FLOW_PERMANENT).
@@ -51,7 +59,11 @@ struct lswitch {
     struct mac_learning *ml;    /* NULL to act as hub instead of switch. */
     uint32_t wildcards;         /* Wildcards to apply to flows. */
     bool action_normal;         /* Use OFPP_NORMAL? */
-    uint32_t queue;             /* OpenFlow queue to use, or UINT32_MAX. */
+
+    /* Queue distribution. */
+    uint32_t default_queue;     /* Default OpenFlow queue, or UINT32_MAX. */
+    struct hmap queue_numbers;  /* Map from port number to lswitch_port. */
+    struct shash queue_names;   /* Map from port name to lswitch_port. */
 
     /* Number of outgoing queued packets on the rconn. */
     struct rconn_packet_counter *queued;
@@ -95,7 +107,21 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
         sw->wildcards = (OFPFW_DL_TYPE | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK
                          | OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST);
     }
-    sw->queue = cfg->queue_id;
+
+    sw->default_queue = cfg->default_queue;
+    hmap_init(&sw->queue_numbers);
+    shash_init(&sw->queue_names);
+    if (cfg->port_queues) {
+        struct shash_node *node;
+
+        SHASH_FOR_EACH (node, cfg->port_queues) {
+            struct lswitch_port *port = xmalloc(sizeof *port);
+            hmap_node_nullify(&port->hmap_node);
+            port->queue_id = (uintptr_t) node->data;
+            shash_add(&sw->queue_names, node->name, port);
+        }
+    }
+
     sw->queued = rconn_packet_counter_create();
     send_features_request(sw, rconn);
 
@@ -111,6 +137,13 @@ void
 lswitch_destroy(struct lswitch *sw)
 {
     if (sw) {
+        struct lswitch_port *node, *next;
+
+        HMAP_FOR_EACH_SAFE (node, next, hmap_node, &sw->queue_numbers) {
+            hmap_remove(&sw->queue_numbers, &node->hmap_node);
+            free(node);
+        }
+        shash_destroy(&sw->queue_names);
         mac_learning_destroy(sw->ml);
         rconn_packet_counter_destroy(sw->queued);
         free(sw);
@@ -247,8 +280,28 @@ process_switch_features(struct lswitch *sw, struct rconn *rconn OVS_UNUSED,
                         void *osf_)
 {
     struct ofp_switch_features *osf = osf_;
+    size_t n_ports;
+    size_t i;
+
+    if (check_ofp_message_array(&osf->header, OFPT_FEATURES_REPLY,
+                                sizeof *osf, sizeof *osf->ports, &n_ports)) {
+        return;
+    }
 
     sw->datapath_id = ntohll(osf->datapath_id);
+
+    for (i = 0; i < n_ports; i++) {
+        struct ofp_phy_port *opp = &osf->ports[i];
+        struct lswitch_port *lp;
+
+        opp->name[OFP_MAX_PORT_NAME_LEN - 1] = '\0';
+        lp = shash_find_data(&sw->queue_names, (char *) opp->name);
+        if (lp && hmap_node_is_null(&lp->hmap_node)) {
+            lp->port_no = ntohs(opp->port_no);
+            hmap_insert(&sw->queue_numbers, &lp->hmap_node,
+                        hash_int(lp->port_no, 0));
+        }
+    }
 }
 
 static uint16_t
@@ -291,11 +344,27 @@ lswitch_choose_destination(struct lswitch *sw, const flow_t *flow)
     return out_port;
 }
 
+static uint32_t
+get_queue_id(const struct lswitch *sw, uint16_t in_port)
+{
+    const struct lswitch_port *port;
+
+    HMAP_FOR_EACH_WITH_HASH (port, hmap_node, hash_int(in_port, 0),
+                             &sw->queue_numbers) {
+        if (port->port_no == in_port) {
+            return port->queue_id;
+        }
+    }
+
+    return sw->default_queue;
+}
+
 static void
 process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
 {
     struct ofp_packet_in *opi = opi_;
     uint16_t in_port = ntohs(opi->in_port);
+    uint32_t queue_id;
     uint16_t out_port;
 
     struct ofp_action_header actions[2];
@@ -323,9 +392,10 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
     out_port = lswitch_choose_destination(sw, &flow);
 
     /* Make actions. */
+    queue_id = get_queue_id(sw, in_port);
     if (out_port == OFPP_NONE) {
         actions_len = 0;
-    } else if (sw->queue == UINT32_MAX || out_port >= OFPP_MAX) {
+    } else if (queue_id == UINT32_MAX || out_port >= OFPP_MAX) {
         struct ofp_action_output oao;
 
         memset(&oao, 0, sizeof oao);
@@ -342,7 +412,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
         oae.type = htons(OFPAT_ENQUEUE);
         oae.len = htons(sizeof oae);
         oae.port = htons(out_port);
-        oae.queue_id = htonl(sw->queue);
+        oae.queue_id = htonl(queue_id);
 
         memcpy(actions, &oae, sizeof oae);
         actions_len = sizeof oae;
index 2ce49e6123aef0f155bf173240d0cf7e43d6a2d2..d0892576adc15180b8cd6dad28620b7e8665960d 100644 (file)
@@ -46,9 +46,12 @@ struct lswitch_config {
      * requests to set up the flow table. */
     const struct ofpbuf *default_flows;
 
-    /* The OpenFlow queue used by packets and flows set up by 'sw'.  Use
-     * UINT32_MAX to avoid specifying a particular queue. */
-    uint32_t queue_id;
+    /* The OpenFlow queue to use by default.  Use UINT32_MAX to avoid
+     * specifying a particular queue. */
+    uint32_t default_queue;
+
+    /* Maps from a port name to a queue_id (cast to void *). */
+    const struct shash *port_queues;
 };
 
 struct lswitch *lswitch_create(struct rconn *, const struct lswitch_config *);
index 24f3a5cd792ede4c27ba536845e10336ada5712f..aa5751f9353cebedea5c7414dc22c48ae193bce4 100644 (file)
@@ -98,7 +98,29 @@ sending packets and setting up flows.  Use one of these options,
 supplying \fIid\fR as an OpenFlow queue ID as a decimal number, to
 instead use that specific queue.
 .IP
-This option may be useful for debugging quality of service setups.
+This option is incompatible with \fB\-N\fR or \fB\-\-normal\fR and
+with \fB\-H\fR or \fB\-\-hub\fR.  If more than one is specified then
+this option takes precedence.
+.IP
+This option may be useful for testing or debugging quality of service
+setups.
+.
+.IP "\fB\-Q \fIport-name\fB:\fIqueue-id\fR"
+.IP "\fB\-\-port\-queue \fIport-name\fB:\fIqueue-id\fR"
+Configures packets received on the port named \fIport-name\fR
+(e.g. \fBeth0\fR) to be output on OpenFlow queue ID \fIqueue-id\fR
+(specified as a decimal number).  For the specified port, this option
+overrides the default specified on \fB\-q\fR or \fB\-\-queue\fR.
+.IP
+This option may be specified any number of times with different
+\Iport-name\fR arguments.
+.IP
+This option is incompatible with \fB\-N\fR or \fB\-\-normal\fR and
+with \fB\-H\fR or \fB\-\-hub\fR.  If more than one is specified then
+this option takes precedence.
+.IP
+This option may be useful for testing or debugging quality of service
+setups.
 .
 .IP "\fB\-\-with\-flows \fIfile\fR"
 When a switch connects, push the flow entries as described in
index 9892abe4d1d41c326dafaa9b880f5396036ee43f..26a1fc3f5609fab76449afe22dbcda479f88648e 100644 (file)
@@ -33,6 +33,7 @@
 #include "openflow/openflow.h"
 #include "poll-loop.h"
 #include "rconn.h"
+#include "shash.h"
 #include "stream-ssl.h"
 #include "timeval.h"
 #include "unixctl.h"
@@ -50,10 +51,11 @@ struct switch_ {
     struct rconn *rconn;
 };
 
-/* Learn the ports on which MAC addresses appear? */
+/* -H, --hub: Learn the ports on which MAC addresses appear? */
 static bool learn_macs = true;
 
-/* Set up flows?  (If not, every packet is processed at the controller.) */
+/* -n, --noflow: Set up flows?  (If not, every packet is processed at the
+ * controller.) */
 static bool set_up_flows = true;
 
 /* -N, --normal: Use "NORMAL" action instead of explicit port? */
@@ -69,8 +71,11 @@ static int max_idle = 60;
  * of their messages (for debugging fail-open mode). */
 static bool mute = false;
 
-/* -q, --queue: OpenFlow queue to use, or the default queue if UINT32_MAX. */
-static uint32_t queue_id = UINT32_MAX;
+/* -q, --queue: default OpenFlow queue, none if UINT32_MAX. */
+static uint32_t default_queue = UINT32_MAX;
+
+/* -Q, --port-queue: map from port name to port number (cast to void *). */
+static struct shash port_queues = SHASH_INITIALIZER(&port_queues);
 
 /* --with-flows: File with flows to send to switch, or null to not load
  * any default flows. */
@@ -225,7 +230,8 @@ new_switch(struct switch_ *sw, struct vconn *vconn)
                 : LSW_FLOOD);
     cfg.max_idle = set_up_flows ? max_idle : -1;
     cfg.default_flows = default_flows.head;
-    cfg.queue_id = queue_id;
+    cfg.default_queue = default_queue;
+    cfg.port_queues = &port_queues;
     sw->lswitch = lswitch_create(sw->rconn, &cfg);
 }
 
@@ -269,6 +275,27 @@ read_flow_file(const char *name)
     fclose(stream);
 }
 
+static void
+add_port_queue(char *s)
+{
+    char *save_ptr = NULL;
+    char *port_name;
+    char *queue_id;
+
+    port_name = strtok_r(s, ":", &save_ptr);
+    queue_id = strtok_r(NULL, "", &save_ptr);
+    if (!queue_id) {
+        ovs_fatal(0, "argument to -Q or --port-queue should take the form "
+                  "\"<port-name>:<queue-id>\"");
+    }
+
+    if (!shash_add_once(&port_queues, port_name,
+                        (void *) (uintptr_t) atoi(queue_id))) {
+        ovs_fatal(0, "<port-name> arguments for -Q or --port-queue must "
+                  "be unique");
+    }
+}
+
 static void
 parse_options(int argc, char *argv[])
 {
@@ -288,6 +315,7 @@ parse_options(int argc, char *argv[])
         {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
         {"mute",        no_argument, 0, OPT_MUTE},
         {"queue",       required_argument, 0, 'q'},
+        {"port-queue",  required_argument, 0, 'Q'},
         {"with-flows",  required_argument, 0, OPT_WITH_FLOWS},
         {"unixctl",     required_argument, 0, OPT_UNIXCTL},
         {"help",        no_argument, 0, 'h'},
@@ -345,7 +373,11 @@ parse_options(int argc, char *argv[])
             break;
 
         case 'q':
-            queue_id = atoi(optarg);
+            default_queue = atoi(optarg);
+            break;
+
+        case 'Q':
+            add_port_queue(optarg);
             break;
 
         case OPT_WITH_FLOWS:
@@ -382,6 +414,20 @@ parse_options(int argc, char *argv[])
         }
     }
     free(short_options);
+
+    if (!shash_is_empty(&port_queues) || default_queue != UINT32_MAX) {
+        if (action_normal) {
+            ovs_error(0, "queue IDs are incompatible with -N or --normal; "
+                      "not using OFPP_NORMAL");
+            action_normal = false;
+        }
+
+        if (!learn_macs) {
+            ovs_error(0, "queue IDs are incompatible with -H or --hub; "
+                      "not acting as hub");
+            learn_macs = true;
+        }
+    }
 }
 
 static void
@@ -398,9 +444,10 @@ usage(void)
            "  -H, --hub               act as hub instead of learning switch\n"
            "  -n, --noflow            pass traffic, but don't add flows\n"
            "  --max-idle=SECS         max idle time for new flows\n"
-           "  -N, --normal            use OFPAT_NORMAL action\n"
+           "  -N, --normal            use OFPP_NORMAL action\n"
            "  -w, --wildcard          use wildcards, not exact-match rules\n"
-           "  -q, --queue=QUEUE       OpenFlow queue ID to use for output\n"
+           "  -q, --queue=QUEUE-ID    OpenFlow queue ID to use for output\n"
+           "  -Q PORT-NAME:QUEUE-ID   use QUEUE-ID for frames from PORT-NAME\n"
            "  --with-flows FILE       use the flows from FILE\n"
            "  --unixctl=SOCKET        override default control socket name\n"
            "  -h, --help              display this help message\n"