vswitch: Implement dscp column of the Queue table.
authorEthan Jackson <ethan@nicira.com>
Thu, 17 Nov 2011 00:31:05 +0000 (16:31 -0800)
committerEthan Jackson <ethan@nicira.com>
Wed, 23 Nov 2011 20:06:54 +0000 (12:06 -0800)
The dscp column of the queue table instructs Open vSwitch to mark
all traffic egressing the queue with the given DSCP bits in its tos
field.

Bug #7046.

ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/ofproto.h
tests/ofproto-dpif.at
vswitchd/bridge.c
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml

index ead708a..650cb1f 100644 (file)
@@ -325,6 +325,18 @@ struct ofport_dpif {
     struct stp_port *stp_port;  /* Spanning Tree Protocol, if any. */
     enum stp_state stp_state;   /* Always STP_DISABLED if STP not in use. */
     long long int stp_state_entered;
+
+    struct hmap priorities;     /* Map of attached 'priority_to_dscp's. */
+};
+
+/* Node in 'ofport_dpif''s 'priorities' map.  Used to maintain a map from
+ * 'priority' (the datapath's term for QoS queue) to the dscp bits which all
+ * traffic egressing the 'ofport' with that priority should be marked with. */
+struct priority_to_dscp {
+    struct hmap_node hmap_node; /* Node in 'ofport_dpif''s 'priorities' map. */
+    uint32_t priority;          /* Priority of this queue (see struct flow). */
+
+    uint8_t dscp;               /* DSCP bits to mark outgoing traffic with. */
 };
 
 static struct ofport_dpif *
@@ -337,6 +349,7 @@ ofport_dpif_cast(const struct ofport *ofport)
 static void port_run(struct ofport_dpif *);
 static void port_wait(struct ofport_dpif *);
 static int set_cfm(struct ofport *, const struct cfm_settings *);
+static void ofport_clear_priorities(struct ofport_dpif *);
 
 struct dpif_completion {
     struct list list_node;
@@ -800,6 +813,7 @@ port_construct(struct ofport *port_)
     port->may_enable = true;
     port->stp_port = NULL;
     port->stp_state = STP_DISABLED;
+    hmap_init(&port->priorities);
 
     if (ofproto->sflow) {
         dpif_sflow_add_port(ofproto->sflow, port->odp_port,
@@ -821,6 +835,9 @@ port_destruct(struct ofport *port_)
     if (ofproto->sflow) {
         dpif_sflow_del_port(ofproto->sflow, port->odp_port);
     }
+
+    ofport_clear_priorities(port);
+    hmap_destroy(&port->priorities);
 }
 
 static void
@@ -1172,6 +1189,82 @@ stp_process_packet(const struct ofport_dpif *ofport,
     }
 }
 \f
+static struct priority_to_dscp *
+get_priority(const struct ofport_dpif *ofport, uint32_t priority)
+{
+    struct priority_to_dscp *pdscp;
+    uint32_t hash;
+
+    hash = hash_int(priority, 0);
+    HMAP_FOR_EACH_IN_BUCKET (pdscp, hmap_node, hash, &ofport->priorities) {
+        if (pdscp->priority == priority) {
+            return pdscp;
+        }
+    }
+    return NULL;
+}
+
+static void
+ofport_clear_priorities(struct ofport_dpif *ofport)
+{
+    struct priority_to_dscp *pdscp, *next;
+
+    HMAP_FOR_EACH_SAFE (pdscp, next, hmap_node, &ofport->priorities) {
+        hmap_remove(&ofport->priorities, &pdscp->hmap_node);
+        free(pdscp);
+    }
+}
+
+static int
+set_queues(struct ofport *ofport_,
+           const struct ofproto_port_queue *qdscp_list,
+           size_t n_qdscp)
+{
+    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+    struct hmap new = HMAP_INITIALIZER(&new);
+    size_t i;
+
+    for (i = 0; i < n_qdscp; i++) {
+        struct priority_to_dscp *pdscp;
+        uint32_t priority;
+        uint8_t dscp;
+
+        dscp = (qdscp_list[i].dscp << 2) & IP_DSCP_MASK;
+        if (dpif_queue_to_priority(ofproto->dpif, qdscp_list[i].queue,
+                                   &priority)) {
+            continue;
+        }
+
+        pdscp = get_priority(ofport, priority);
+        if (pdscp) {
+            hmap_remove(&ofport->priorities, &pdscp->hmap_node);
+        } else {
+            pdscp = xmalloc(sizeof *pdscp);
+            pdscp->priority = priority;
+            pdscp->dscp = dscp;
+            ofproto->need_revalidate = true;
+        }
+
+        if (pdscp->dscp != dscp) {
+            pdscp->dscp = dscp;
+            ofproto->need_revalidate = true;
+        }
+
+        hmap_insert(&new, &pdscp->hmap_node, hash_int(pdscp->priority, 0));
+    }
+
+    if (!hmap_is_empty(&ofport->priorities)) {
+        ofport_clear_priorities(ofport);
+        ofproto->need_revalidate = true;
+    }
+
+    hmap_swap(&new, &ofport->priorities);
+    hmap_destroy(&new);
+
+    return 0;
+}
+\f
 /* Bundles. */
 
 /* Expires all MAC learning entries associated with 'port' and forces ofproto
@@ -3775,12 +3868,21 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
 {
     const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
     uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
+    uint8_t flow_nw_tos = ctx->flow.nw_tos;
 
     if (ofport) {
+        struct priority_to_dscp *pdscp;
+
         if (ofport->up.opp.config & htonl(OFPPC_NO_FWD)
             || (check_stp && !stp_forward_in_state(ofport->stp_state))) {
             return;
         }
+
+        pdscp = get_priority(ofport, ctx->flow.priority);
+        if (pdscp) {
+            ctx->flow.nw_tos &= ~IP_DSCP_MASK;
+            ctx->flow.nw_tos |= pdscp->dscp;
+        }
     } else {
         /* We may not have an ofport record for this port, but it doesn't hurt
          * to allow forwarding to it anyhow.  Maybe such a port will appear
@@ -3792,6 +3894,7 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
     ctx->sflow_odp_port = odp_port;
     ctx->sflow_n_outputs++;
     ctx->nf_output_iface = ofp_port;
+    ctx->flow.nw_tos = flow_nw_tos;
 }
 
 static void
@@ -5378,6 +5481,7 @@ const struct ofproto_class ofproto_dpif_class = {
     get_stp_status,
     set_stp_port,
     get_stp_port_status,
+    set_queues,
     bundle_set,
     bundle_remove,
     mirror_set,
index 8908dc0..d303632 100644 (file)
@@ -965,6 +965,17 @@ struct ofproto_class {
     int (*get_stp_port_status)(struct ofport *ofport,
                                struct ofproto_port_stp_status *s);
 
+    /* Registers meta-data associated with the 'n_qdscp' Qualities of Service
+     * 'queues' attached to 'ofport'.  This data is not intended to be
+     * sufficient to implement QoS.  Instead, providers may use this
+     * information to implement features which require knowledge of what queues
+     * exist on a port, and some basic information about them.
+     *
+     * EOPNOTSUPP as a return value indicates that this ofproto_class does not
+     * support QoS, as does a null pointer. */
+    int (*set_queues)(struct ofport *ofport,
+                      const struct ofproto_port_queue *queues, size_t n_qdscp);
+
     /* If 's' is nonnull, this function registers a "bundle" associated with
      * client data pointer 'aux' in 'ofproto'.  A bundle is the same concept as
      * a Port in OVSDB, that is, it consists of one or more "slave" devices
index 60cf524..af02a0e 100644 (file)
@@ -603,6 +603,33 @@ ofproto_port_get_stp_status(struct ofproto *ofproto, uint16_t ofp_port,
             : EOPNOTSUPP);
 }
 \f
+/* Queue DSCP configuration. */
+
+/* Registers meta-data associated with the 'n_qdscp' Qualities of Service
+ * 'queues' attached to 'ofport'.  This data is not intended to be sufficient
+ * to implement QoS.  Instead, it is used to implement features which require
+ * knowledge of what queues exist on a port, and some basic information about
+ * them.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+ofproto_port_set_queues(struct ofproto *ofproto, uint16_t ofp_port,
+                        const struct ofproto_port_queue *queues,
+                        size_t n_queues)
+{
+    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+
+    if (!ofport) {
+        VLOG_WARN("%s: cannot set queues on nonexistent port %"PRIu16,
+                  ofproto->name, ofp_port);
+        return ENODEV;
+    }
+
+    return (ofproto->ofproto_class->set_queues
+            ? ofproto->ofproto_class->set_queues(ofport, queues, n_queues)
+            : EOPNOTSUPP);
+}
+\f
 /* Connectivity Fault Management configuration. */
 
 /* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */
index 5a99d46..74b3dec 100644 (file)
@@ -99,6 +99,11 @@ struct ofproto_port_stp_status {
     int error_count;            /* Number of bad BPDUs received. */
 };
 
+struct ofproto_port_queue {
+    uint32_t queue;             /* Queue ID. */
+    uint8_t dscp;               /* DSCP bits (e.g. [0, 63]). */
+};
+
 /* How the switch should act if the controller cannot be contacted. */
 enum ofproto_fail_mode {
     OFPROTO_FAIL_SECURE,        /* Preserve flow table. */
@@ -219,6 +224,9 @@ int ofproto_port_set_stp(struct ofproto *, uint16_t ofp_port,
                          const struct ofproto_port_stp_settings *);
 int ofproto_port_get_stp_status(struct ofproto *, uint16_t ofp_port,
                                 struct ofproto_port_stp_status *);
+int ofproto_port_set_queues(struct ofproto *, uint16_t ofp_port,
+                            const struct ofproto_port_queue *,
+                            size_t n_queues);
 
 /* The behaviour of the port regarding VLAN handling */
 enum port_vlan_mode {
index 545b7c0..9080e7e 100644 (file)
@@ -62,6 +62,33 @@ AT_CHECK([tail -1 stdout], [0],
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - DSCP])
+dnl This test assumes port p1 is allocated OpenFlow port number 1.
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
+AT_DATA([flows.txt], [dnl
+actions=output:65534,enqueue:1:1,enqueue:1:2,enqueue:1:2,enqueue:1:1,output:1,mod_nw_tos:0,output:1,output:65534
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-vsctl -- \
+        set Port p1 qos=@newqos --\
+        --id=@newqos create QoS type=linux-htb queues=1=@q1,2=@q2 --\
+        --id=@q1 create Queue dscp=1 --\
+        --id=@q2 create Queue dscp=2], [0], [ignore])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(9),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xff,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: dnl
+0,dnl
+set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x7,ttl=128,frag=no)),set(priority(1)),1,dnl
+set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xb,ttl=128,frag=no)),set(priority(2)),1,dnl
+1,dnl
+set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x7,ttl=128,frag=no)),set(priority(1)),1,dnl
+set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xff,ttl=128,frag=no)),set(priority(0)),1,dnl
+set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x3,ttl=128,frag=no)),1,dnl
+0
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - output/flood flags])
 dnl This test assumes that OpenFlow port numbers are allocated in order
 dnl starting from one.  It does not necessarily require that they are allocated
index 378a08c..83e125c 100644 (file)
@@ -2959,6 +2959,10 @@ iface_delete_queues(unsigned int queue_id,
 static void
 iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
 {
+    struct ofpbuf queues_buf;
+
+    ofpbuf_init(&queues_buf, 0);
+
     if (!qos || qos->type[0] == '\0' || qos->n_queues < 1) {
         netdev_set_qos(iface->netdev, NULL, NULL);
     } else {
@@ -2989,6 +2993,15 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
                 queue_zero = true;
             }
 
+            if (queue->n_dscp == 1) {
+                struct ofproto_port_queue *port_queue;
+
+                port_queue = ofpbuf_put_uninit(&queues_buf,
+                                               sizeof *port_queue);
+                port_queue->queue = queue_id;
+                port_queue->dscp = queue->dscp[0];
+            }
+
             shash_from_ovs_idl_map(queue->key_other_config,
                                    queue->value_other_config,
                                    queue->n_other_config, &details);
@@ -3004,9 +3017,19 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
         }
     }
 
+    if (iface->ofp_port >= 0) {
+        const struct ofproto_port_queue *port_queues = queues_buf.data;
+        size_t n_queues = queues_buf.size / sizeof *port_queues;
+
+        ofproto_port_set_queues(iface->port->bridge->ofproto, iface->ofp_port,
+                                port_queues, n_queues);
+    }
+
     netdev_set_policing(iface->netdev,
                         iface->cfg->ingress_policing_rate,
                         iface->cfg->ingress_policing_burst);
+
+    ofpbuf_uninit(&queues_buf);
 }
 
 static void
index 19c5922..e2f231c 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "6.3.0",
- "cksum": "1659474737 15341",
+ "version": "6.4.0",
+ "cksum": "3757343995 15531",
  "tables": {
    "Open_vSwitch": {
      "columns": {
      "isRoot": true},
    "Queue": {
      "columns": {
+       "dscp": {
+         "type": {"key": {"type": "integer",
+                          "minInteger": 0,
+                          "maxInteger": 63},
+                  "min": 0, "max": 1}},
        "other_config": {
          "type": {"key": "string", "value": "string",
                   "min": 0, "max": "unlimited"}},
index 746a11a..8eba5cb 100644 (file)
     Service (QoS) features.  May be referenced by <ref column="queues"
     table="QoS"/> column in <ref table="QoS"/> table.</p>
 
+    <column name="dscp">
+      If set, Open vSwitch will mark all traffic egressing this
+      <ref table="Queue"/> with the given DSCP bits.  Traffic egressing the
+      default <ref table="Queue"/> is only marked if it was explicitly selected
+      as the <ref table="Queue"/> at the time the packet was output.  If unset,
+      the DSCP bits of traffic egressing this <ref table="Queue"/> will remain
+      unchanged.
+    </column>
+
     <group title="Configuration for min-rate QoS">
       <p>
         These key-value pairs are defined for <ref table="QoS"/> <ref