2
0
mirror of https://github.com/openvswitch/ovs synced 2025-10-21 14:49:41 +00:00

ovs-controller: Improve QoS abilities.

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>
This commit is contained in:
Ben Pfaff
2010-10-01 13:41:40 -07:00
parent ad67e56888
commit d4cdc6b4c4
4 changed files with 158 additions and 16 deletions

View File

@@ -24,6 +24,7 @@
#include <time.h> #include <time.h>
#include "flow.h" #include "flow.h"
#include "hmap.h"
#include "mac-learning.h" #include "mac-learning.h"
#include "ofpbuf.h" #include "ofpbuf.h"
#include "ofp-parse.h" #include "ofp-parse.h"
@@ -33,6 +34,7 @@
#include "poll-loop.h" #include "poll-loop.h"
#include "queue.h" #include "queue.h"
#include "rconn.h" #include "rconn.h"
#include "shash.h"
#include "timeval.h" #include "timeval.h"
#include "vconn.h" #include "vconn.h"
#include "vlog.h" #include "vlog.h"
@@ -40,6 +42,12 @@
VLOG_DEFINE_THIS_MODULE(learning_switch) 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 { struct lswitch {
/* If nonnegative, the switch sets up flows that expire after the given /* 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). * 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. */ struct mac_learning *ml; /* NULL to act as hub instead of switch. */
uint32_t wildcards; /* Wildcards to apply to flows. */ uint32_t wildcards; /* Wildcards to apply to flows. */
bool action_normal; /* Use OFPP_NORMAL? */ 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. */ /* Number of outgoing queued packets on the rconn. */
struct rconn_packet_counter *queued; 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 sw->wildcards = (OFPFW_DL_TYPE | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK
| OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST); | 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(); sw->queued = rconn_packet_counter_create();
send_features_request(sw, rconn); send_features_request(sw, rconn);
@@ -111,6 +137,13 @@ void
lswitch_destroy(struct lswitch *sw) lswitch_destroy(struct lswitch *sw)
{ {
if (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); mac_learning_destroy(sw->ml);
rconn_packet_counter_destroy(sw->queued); rconn_packet_counter_destroy(sw->queued);
free(sw); free(sw);
@@ -247,8 +280,28 @@ process_switch_features(struct lswitch *sw, struct rconn *rconn OVS_UNUSED,
void *osf_) void *osf_)
{ {
struct ofp_switch_features *osf = 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); 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 static uint16_t
@@ -291,11 +344,27 @@ lswitch_choose_destination(struct lswitch *sw, const flow_t *flow)
return out_port; 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 static void
process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
{ {
struct ofp_packet_in *opi = opi_; struct ofp_packet_in *opi = opi_;
uint16_t in_port = ntohs(opi->in_port); uint16_t in_port = ntohs(opi->in_port);
uint32_t queue_id;
uint16_t out_port; uint16_t out_port;
struct ofp_action_header actions[2]; 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); out_port = lswitch_choose_destination(sw, &flow);
/* Make actions. */ /* Make actions. */
queue_id = get_queue_id(sw, in_port);
if (out_port == OFPP_NONE) { if (out_port == OFPP_NONE) {
actions_len = 0; 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; struct ofp_action_output oao;
memset(&oao, 0, sizeof 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.type = htons(OFPAT_ENQUEUE);
oae.len = htons(sizeof oae); oae.len = htons(sizeof oae);
oae.port = htons(out_port); oae.port = htons(out_port);
oae.queue_id = htonl(sw->queue); oae.queue_id = htonl(queue_id);
memcpy(actions, &oae, sizeof oae); memcpy(actions, &oae, sizeof oae);
actions_len = sizeof oae; actions_len = sizeof oae;

View File

@@ -46,9 +46,12 @@ struct lswitch_config {
* requests to set up the flow table. */ * requests to set up the flow table. */
const struct ofpbuf *default_flows; const struct ofpbuf *default_flows;
/* The OpenFlow queue used by packets and flows set up by 'sw'. Use /* The OpenFlow queue to use by default. Use UINT32_MAX to avoid
* UINT32_MAX to avoid specifying a particular queue. */ * specifying a particular queue. */
uint32_t queue_id; 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 *); struct lswitch *lswitch_create(struct rconn *, const struct lswitch_config *);

View 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 supplying \fIid\fR as an OpenFlow queue ID as a decimal number, to
instead use that specific queue. instead use that specific queue.
.IP .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" .IP "\fB\-\-with\-flows \fIfile\fR"
When a switch connects, push the flow entries as described in When a switch connects, push the flow entries as described in

View File

@@ -33,6 +33,7 @@
#include "openflow/openflow.h" #include "openflow/openflow.h"
#include "poll-loop.h" #include "poll-loop.h"
#include "rconn.h" #include "rconn.h"
#include "shash.h"
#include "stream-ssl.h" #include "stream-ssl.h"
#include "timeval.h" #include "timeval.h"
#include "unixctl.h" #include "unixctl.h"
@@ -50,10 +51,11 @@ struct switch_ {
struct rconn *rconn; 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; 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; static bool set_up_flows = true;
/* -N, --normal: Use "NORMAL" action instead of explicit port? */ /* -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). */ * of their messages (for debugging fail-open mode). */
static bool mute = false; static bool mute = false;
/* -q, --queue: OpenFlow queue to use, or the default queue if UINT32_MAX. */ /* -q, --queue: default OpenFlow queue, none if UINT32_MAX. */
static uint32_t queue_id = 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 /* --with-flows: File with flows to send to switch, or null to not load
* any default flows. */ * any default flows. */
@@ -225,7 +230,8 @@ new_switch(struct switch_ *sw, struct vconn *vconn)
: LSW_FLOOD); : LSW_FLOOD);
cfg.max_idle = set_up_flows ? max_idle : -1; cfg.max_idle = set_up_flows ? max_idle : -1;
cfg.default_flows = default_flows.head; 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); sw->lswitch = lswitch_create(sw->rconn, &cfg);
} }
@@ -269,6 +275,27 @@ read_flow_file(const char *name)
fclose(stream); 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 static void
parse_options(int argc, char *argv[]) parse_options(int argc, char *argv[])
{ {
@@ -288,6 +315,7 @@ parse_options(int argc, char *argv[])
{"max-idle", required_argument, 0, OPT_MAX_IDLE}, {"max-idle", required_argument, 0, OPT_MAX_IDLE},
{"mute", no_argument, 0, OPT_MUTE}, {"mute", no_argument, 0, OPT_MUTE},
{"queue", required_argument, 0, 'q'}, {"queue", required_argument, 0, 'q'},
{"port-queue", required_argument, 0, 'Q'},
{"with-flows", required_argument, 0, OPT_WITH_FLOWS}, {"with-flows", required_argument, 0, OPT_WITH_FLOWS},
{"unixctl", required_argument, 0, OPT_UNIXCTL}, {"unixctl", required_argument, 0, OPT_UNIXCTL},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
@@ -345,7 +373,11 @@ parse_options(int argc, char *argv[])
break; break;
case 'q': case 'q':
queue_id = atoi(optarg); default_queue = atoi(optarg);
break;
case 'Q':
add_port_queue(optarg);
break; break;
case OPT_WITH_FLOWS: case OPT_WITH_FLOWS:
@@ -382,6 +414,20 @@ parse_options(int argc, char *argv[])
} }
} }
free(short_options); 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 static void
@@ -398,9 +444,10 @@ usage(void)
" -H, --hub act as hub instead of learning switch\n" " -H, --hub act as hub instead of learning switch\n"
" -n, --noflow pass traffic, but don't add flows\n" " -n, --noflow pass traffic, but don't add flows\n"
" --max-idle=SECS max idle time for new 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" " -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" " --with-flows FILE use the flows from FILE\n"
" --unixctl=SOCKET override default control socket name\n" " --unixctl=SOCKET override default control socket name\n"
" -h, --help display this help message\n" " -h, --help display this help message\n"