From 8b36f51e0fdeb468058e4552858af03851b4fad3 Mon Sep 17 00:00:00 2001 From: Ethan Jackson Date: Wed, 16 Nov 2011 16:31:05 -0800 Subject: [PATCH] vswitch: Implement dscp column of the Queue table. 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 | 104 +++++++++++++++++++++++++++++++++++++ ofproto/ofproto-provider.h | 11 ++++ ofproto/ofproto.c | 27 ++++++++++ ofproto/ofproto.h | 8 +++ tests/ofproto-dpif.at | 27 ++++++++++ vswitchd/bridge.c | 23 ++++++++ vswitchd/vswitch.ovsschema | 9 +++- vswitchd/vswitch.xml | 9 ++++ 8 files changed, 216 insertions(+), 2 deletions(-) diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index ead708a8..650cb1fc 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -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, } } +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; +} + /* 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, diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 8908dc06..d303632a 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -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 diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 60cf5245..af02a0e3 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -603,6 +603,33 @@ ofproto_port_get_stp_status(struct ofproto *ofproto, uint16_t ofp_port, : EOPNOTSUPP); } +/* 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); +} + /* Connectivity Fault Management configuration. */ /* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */ diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h index 5a99d469..74b3dece 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 545b7c0a..9080e7ec 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 378a08cb..83e125cd 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 19c59227..e2f231c8 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 746a11aa..8eba5cbf 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -1842,6 +1842,15 @@ Service (QoS) features. May be referenced by column in table.

+ + If set, Open vSwitch will mark all traffic egressing this + with the given DSCP bits. Traffic egressing the + default is only marked if it was explicitly selected + as the at the time the packet was output. If unset, + the DSCP bits of traffic egressing this will remain + unchanged. + +

These key-value pairs are defined for