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 | 101 ++++++++++++++++++++++++++++++++++++++++++++ ofproto/ofproto-provider.h | 9 ++++ ofproto/ofproto.c | 25 +++++++++++ ofproto/ofproto.h | 8 ++++ tests/ofproto-dpif.at | 27 ++++++++++++ vswitchd/bridge.c | 23 ++++++++++ vswitchd/vswitch.ovsschema | 9 +++- vswitchd/vswitch.xml | 9 ++++ 8 files changed, 209 insertions(+), 2 deletions(-) diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index ead708a..b32df2e 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -325,6 +325,15 @@ 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_node's. */ +}; + +struct priority_node { + uint32_t priority; /* Priority of this queue. */ + struct hmap_node hmap_node; /* Node in 'ofport_dpif''s 'priorities' map. */ + + uint8_t dscp; /* DSCP bits to mark outgoing traffic with. */ }; static struct ofport_dpif * @@ -337,6 +346,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 +810,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 +832,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 +1186,82 @@ stp_process_packet(const struct ofport_dpif *ofport, } } +static struct priority_node * +get_priority(const struct ofport_dpif *ofport, uint32_t priority) +{ + struct priority_node *pnode; + uint32_t hash; + + hash = hash_int(priority, 0); + HMAP_FOR_EACH_IN_BUCKET (pnode, hmap_node, hash, &ofport->priorities) { + if (pnode->priority == priority) { + return pnode; + } + } + return NULL; +} + +static void +ofport_clear_priorities(struct ofport_dpif *ofport) +{ + struct priority_node *pnode, *next; + + HMAP_FOR_EACH_SAFE (pnode, next, hmap_node, &ofport->priorities) { + hmap_remove(&ofport->priorities, &pnode->hmap_node); + free(pnode); + } +} + +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_node *pnode; + 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; + } + + pnode = get_priority(ofport, priority); + if (pnode) { + hmap_remove(&ofport->priorities, &pnode->hmap_node); + } else { + pnode = xmalloc(sizeof *pnode); + pnode->priority = priority; + pnode->dscp = dscp; + ofproto->need_revalidate = true; + } + + if (pnode->dscp != dscp) { + pnode->dscp = dscp; + ofproto->need_revalidate = true; + } + + hmap_insert(&new, &pnode->hmap_node, hash_int(pnode->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; +} + /* Bundles. */ /* Expires all MAC learning entries associated with 'port' and forces ofproto @@ -3775,12 +3865,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_node *pnode; + if (ofport->up.opp.config & htonl(OFPPC_NO_FWD) || (check_stp && !stp_forward_in_state(ofport->stp_state))) { return; } + + pnode = get_priority(ofport, ctx->flow.priority); + if (pnode) { + ctx->flow.nw_tos &= ~IP_DSCP_MASK; + ctx->flow.nw_tos |= pnode->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 +3891,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 +5478,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, diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 8908dc0..ed06a35 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -965,6 +965,15 @@ struct ofproto_class { int (*get_stp_port_status)(struct ofport *ofport, struct ofproto_port_stp_status *s); + /* Registers the Quality of Service 'queues' associated with 'ofport'. + * 'queues' is an array of Queue elements in arbitrary order with 'n_qdscp' + * elements. + * + * 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 diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 60cf524..f08d64e 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -603,6 +603,31 @@ ofproto_port_get_stp_status(struct ofproto *ofproto, uint16_t ofp_port, : EOPNOTSUPP); } +/* Queue DSCP configuration. */ + +/* Registers the Quality of Service 'queues' associated with 'ofp_port' in + * 'ofproto'. 'queues' is an array of Queue elements in arbitrary order with + * 'n_qdscp' elements. + * + * 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); +} + /* Connectivity Fault Management configuration. */ /* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */ diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h index 5a99d46..74b3dec 100644 --- a/ofproto/ofproto.h +++ b/ofproto/ofproto.h @@ -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 { diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 545b7c0..9080e7e 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -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 diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 378a08c..83e125c 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -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 diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index 19c5922..e2f231c 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -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": { @@ -256,6 +256,11 @@ "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"}}, diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 746a11a..9bbd532 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -1842,6 +1842,15 @@ 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 explicity 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 -- 1.7.7.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev