]> git.proxmox.com Git - ovs.git/blame - ovn/controller/binding.c
Check and allocate free qdisc queue id for ports with qos parameters
[ovs.git] / ovn / controller / binding.c
CommitLineData
aef5f431 1/* Copyright (c) 2015, 2016 Nicira, Inc.
717c7fc5
JP
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <config.h>
e387e3e8 17#include "binding.h"
70c7cfef
RM
18#include "lflow.h"
19#include "lport.h"
717c7fc5 20
78aab811 21#include "lib/bitmap.h"
fdb3c70c 22#include "lib/poll-loop.h"
717c7fc5
JP
23#include "lib/sset.h"
24#include "lib/util.h"
a6095f81 25#include "lib/netdev.h"
717c7fc5 26#include "lib/vswitch-idl.h"
ee89ea7b 27#include "openvswitch/hmap.h"
717c7fc5 28#include "openvswitch/vlog.h"
e3df8838 29#include "ovn/lib/ovn-sb-idl.h"
717c7fc5
JP
30#include "ovn-controller.h"
31
e387e3e8 32VLOG_DEFINE_THIS_MODULE(binding);
717c7fc5 33
a6095f81
BS
34#define OVN_QOS_TYPE "linux-htb"
35
36struct qos_queue {
37 struct hmap_node node;
38 uint32_t queue_id;
39 uint32_t max_rate;
40 uint32_t burst;
41};
42
717c7fc5 43void
4a5a9e06 44binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
717c7fc5 45{
4a5a9e06
BP
46 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
47 ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
717c7fc5 48
4a5a9e06
BP
49 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
50 ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name);
51 ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
717c7fc5 52
4a5a9e06
BP
53 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
54 ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
55 ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
a6095f81 56 ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_qos);
717c7fc5 57
4a5a9e06
BP
58 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
59 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
60 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
a6095f81
BS
61 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_status);
62
63 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
64 ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
717c7fc5
JP
65}
66
926c34fd 67static void
6f783c06 68get_local_iface_ids(const struct ovsrec_bridge *br_int,
c5f346a5 69 struct shash *lport_to_iface,
a6095f81
BS
70 struct sset *all_lports,
71 struct sset *egress_ifaces)
717c7fc5 72{
717c7fc5
JP
73 int i;
74
422a9f73
BP
75 for (i = 0; i < br_int->n_ports; i++) {
76 const struct ovsrec_port *port_rec = br_int->ports[i];
717c7fc5
JP
77 const char *iface_id;
78 int j;
79
422a9f73 80 if (!strcmp(port_rec->name, br_int->name)) {
717c7fc5
JP
81 continue;
82 }
83
84 for (j = 0; j < port_rec->n_interfaces; j++) {
85 const struct ovsrec_interface *iface_rec;
86
87 iface_rec = port_rec->interfaces[j];
88 iface_id = smap_get(&iface_rec->external_ids, "iface-id");
a6095f81
BS
89
90 if (iface_id) {
91 shash_add(lport_to_iface, iface_id, iface_rec);
92 sset_add(all_lports, iface_id);
93 }
94
95 /* Check if this is a tunnel interface. */
96 if (smap_get(&iface_rec->options, "remote_ip")) {
97 const char *tunnel_iface
98 = smap_get(&iface_rec->status, "tunnel_egress_iface");
99 if (tunnel_iface) {
100 sset_add(egress_ifaces, tunnel_iface);
101 }
717c7fc5 102 }
263064ae
RM
103 }
104 }
263064ae
RM
105}
106
bda5a056
RB
107static void
108add_local_datapath(struct hmap *local_datapaths,
3a0d92b3 109 const struct sbrec_port_binding *binding_rec)
bda5a056 110{
e4426e34
BP
111 if (get_local_datapath(local_datapaths,
112 binding_rec->datapath->tunnel_key)) {
6e6c3f91 113 return;
bda5a056 114 }
6e6c3f91
HZ
115
116 struct local_datapath *ld = xzalloc(sizeof *ld);
263064ae 117 ld->logical_port = xstrdup(binding_rec->logical_port);
8e9f1c13 118 memcpy(&ld->uuid, &binding_rec->header_.uuid, sizeof ld->uuid);
6e6c3f91
HZ
119 hmap_insert(local_datapaths, &ld->hmap_node,
120 binding_rec->datapath->tunnel_key);
bda5a056
RB
121}
122
aef5f431 123static void
a6095f81 124get_qos_params(const struct sbrec_port_binding *pb, struct hmap *queue_map)
aef5f431 125{
a6095f81
BS
126 uint32_t max_rate = smap_get_int(&pb->options, "qos_max_rate", 0);
127 uint32_t burst = smap_get_int(&pb->options, "qos_burst", 0);
128 uint32_t queue_id = smap_get_int(&pb->options, "qdisc_queue_id", 0);
aef5f431 129
a6095f81
BS
130 if ((!max_rate && !burst) || !queue_id) {
131 /* Qos is not configured for this port. */
132 return;
133 }
134
135 struct qos_queue *node = xzalloc(sizeof *node);
136 hmap_insert(queue_map, &node->node, hash_int(queue_id, 0));
137 node->max_rate = max_rate;
138 node->burst = burst;
139 node->queue_id = queue_id;
140}
141
142static const struct ovsrec_qos *
143get_noop_qos(struct controller_ctx *ctx)
144{
145 const struct ovsrec_qos *qos;
146 OVSREC_QOS_FOR_EACH (qos, ctx->ovs_idl) {
147 if (!strcmp(qos->type, "linux-noop")) {
148 return qos;
149 }
150 }
151
152 if (!ctx->ovs_idl_txn) {
153 return NULL;
154 }
155 qos = ovsrec_qos_insert(ctx->ovs_idl_txn);
156 ovsrec_qos_set_type(qos, "linux-noop");
157 return qos;
158}
159
160static bool
161set_noop_qos(struct controller_ctx *ctx, struct sset *egress_ifaces)
162{
163 if (!ctx->ovs_idl_txn) {
164 return false;
165 }
166
167 const struct ovsrec_qos *noop_qos = get_noop_qos(ctx);
168 if (!noop_qos) {
169 return false;
170 }
171
172 const struct ovsrec_port *port;
173 size_t count = 0;
174
175 OVSREC_PORT_FOR_EACH (port, ctx->ovs_idl) {
176 if (sset_contains(egress_ifaces, port->name)) {
177 ovsrec_port_set_qos(port, noop_qos);
178 count++;
179 }
180 if (sset_count(egress_ifaces) == count) {
181 break;
182 }
183 }
184 return true;
185}
186
187static void
188setup_qos(const char *egress_iface, struct hmap *queue_map)
189{
190 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
191 struct netdev *netdev_phy;
192
193 if (!egress_iface) {
194 /* Queues cannot be configured. */
195 return;
196 }
197
198 int error = netdev_open(egress_iface, NULL, &netdev_phy);
199 if (error) {
200 VLOG_WARN_RL(&rl, "%s: could not open netdev (%s)",
201 egress_iface, ovs_strerror(error));
202 return;
203 }
204
205 /* Check and configure qdisc. */
206 const char *qdisc_type;
207 struct smap qdisc_details;
208
209 smap_init(&qdisc_details);
210 if (netdev_get_qos(netdev_phy, &qdisc_type, &qdisc_details) != 0 ||
211 qdisc_type[0] == '\0') {
212 /* Qos is not supported. */
213 return;
214 }
215 if (strcmp(qdisc_type, OVN_QOS_TYPE)) {
216 error = netdev_set_qos(netdev_phy, OVN_QOS_TYPE, &qdisc_details);
217 if (error) {
218 VLOG_WARN_RL(&rl, "%s: could not configure QoS (%s)",
219 egress_iface, ovs_strerror(error));
220 }
221 }
222
223 /* Check and delete if needed. */
224 struct netdev_queue_dump dump;
225 unsigned int queue_id;
226 struct smap queue_details;
227 struct qos_queue *sb_info;
228 struct hmap consistent_queues;
229
230 smap_init(&queue_details);
231 hmap_init(&consistent_queues);
232 NETDEV_QUEUE_FOR_EACH (&queue_id, &queue_details, &dump, netdev_phy) {
233 bool is_queue_needed = false;
234
235 HMAP_FOR_EACH_WITH_HASH (sb_info, node, hash_int(queue_id, 0),
236 queue_map) {
237 is_queue_needed = true;
238 if (sb_info->max_rate ==
239 smap_get_int(&queue_details, "max-rate", 0)
240 && sb_info->burst == smap_get_int(&queue_details, "burst", 0)) {
241 /* This queue is consistent. */
242 hmap_insert(&consistent_queues, &sb_info->node,
243 hash_int(queue_id, 0));
244 break;
245 }
246 }
247
248 if (!is_queue_needed) {
249 error = netdev_delete_queue(netdev_phy, queue_id);
250 if (error) {
251 VLOG_WARN_RL(&rl, "%s: could not delete queue %u (%s)",
252 egress_iface, queue_id, ovs_strerror(error));
253 }
254 }
255 }
256
257 /* Create/Update queues. */
258 HMAP_FOR_EACH (sb_info, node, queue_map) {
259 if (hmap_contains(&consistent_queues, &sb_info->node)) {
260 hmap_remove(&consistent_queues, &sb_info->node);
261 continue;
262 }
263
264 smap_clear(&queue_details);
265 smap_add_format(&queue_details, "max-rate", "%d", sb_info->max_rate);
266 smap_add_format(&queue_details, "burst", "%d", sb_info->burst);
267 error = netdev_set_queue(netdev_phy, sb_info->queue_id,
268 &queue_details);
269 if (error) {
270 VLOG_WARN_RL(&rl, "%s: could not configure queue %u (%s)",
271 egress_iface, sb_info->queue_id, ovs_strerror(error));
272 }
273 }
274 smap_destroy(&queue_details);
275 hmap_destroy(&consistent_queues);
276 netdev_close(netdev_phy);
aef5f431
BP
277}
278
263064ae 279static void
6f783c06 280consider_local_datapath(struct controller_ctx *ctx,
263064ae
RM
281 const struct sbrec_chassis *chassis_rec,
282 const struct sbrec_port_binding *binding_rec,
a6095f81 283 struct hmap *qos_map,
6f783c06 284 struct hmap *local_datapaths,
c5f346a5
BS
285 struct shash *lport_to_iface,
286 struct sset *all_lports)
263064ae
RM
287{
288 const struct ovsrec_interface *iface_rec
6f783c06
RB
289 = shash_find_data(lport_to_iface, binding_rec->logical_port);
290
263064ae
RM
291 if (iface_rec
292 || (binding_rec->parent_port && binding_rec->parent_port[0] &&
926c34fd
RM
293 sset_contains(all_lports, binding_rec->parent_port))) {
294 if (binding_rec->parent_port && binding_rec->parent_port[0]) {
295 /* Add child logical port to the set of all local ports. */
296 sset_add(all_lports, binding_rec->logical_port);
297 }
3a0d92b3 298 add_local_datapath(local_datapaths, binding_rec);
a6095f81
BS
299 if (iface_rec && qos_map && ctx->ovs_idl_txn) {
300 get_qos_params(binding_rec, qos_map);
263064ae
RM
301 }
302 if (binding_rec->chassis == chassis_rec) {
303 return;
304 }
305 if (ctx->ovnsb_idl_txn) {
306 if (binding_rec->chassis) {
307 VLOG_INFO("Changing chassis for lport %s from %s to %s.",
308 binding_rec->logical_port,
309 binding_rec->chassis->name,
310 chassis_rec->name);
311 } else {
312 VLOG_INFO("Claiming lport %s for this chassis.",
313 binding_rec->logical_port);
9240e9ab
RR
314 for (int i = 0; i < binding_rec->n_mac; i++) {
315 VLOG_INFO("Claiming %s", binding_rec->mac[i]);
316 }
263064ae
RM
317 }
318 sbrec_port_binding_set_chassis(binding_rec, chassis_rec);
319 }
62b87eab
NS
320 } else if (!strcmp(binding_rec->type, "l2gateway")) {
321 const char *chassis_id = smap_get(&binding_rec->options,
322 "l2gateway-chassis");
323 if (!chassis_id || strcmp(chassis_id, chassis_rec->name)) {
324 if (binding_rec->chassis == chassis_rec && ctx->ovnsb_idl_txn) {
325 VLOG_INFO("Releasing l2gateway port %s from this chassis.",
326 binding_rec->logical_port);
327 sbrec_port_binding_set_chassis(binding_rec, NULL);
328 }
329 return;
330 }
331
926c34fd
RM
332 sset_add(all_lports, binding_rec->logical_port);
333 add_local_datapath(local_datapaths, binding_rec);
62b87eab
NS
334 if (binding_rec->chassis == chassis_rec) {
335 return;
336 }
337
338 if (!strcmp(chassis_id, chassis_rec->name) && ctx->ovnsb_idl_txn) {
339 VLOG_INFO("Claiming l2gateway port %s for this chassis.",
340 binding_rec->logical_port);
341 sbrec_port_binding_set_chassis(binding_rec, chassis_rec);
62b87eab 342 }
8439c2eb
CSV
343 } else if (!strcmp(binding_rec->type, "l3gateway")) {
344 const char *chassis = smap_get(&binding_rec->options,
345 "l3gateway-chassis");
346 if (!strcmp(chassis, chassis_rec->name) && ctx->ovnsb_idl_txn) {
347 add_local_datapath(local_datapaths, binding_rec);
348 }
349 } else if (chassis_rec && binding_rec->chassis == chassis_rec) {
263064ae
RM
350 if (ctx->ovnsb_idl_txn) {
351 VLOG_INFO("Releasing lport %s from this chassis.",
352 binding_rec->logical_port);
9240e9ab
RR
353 for (int i = 0; i < binding_rec->n_mac; i++) {
354 VLOG_INFO("Releasing %s", binding_rec->mac[i]);
355 }
263064ae 356 sbrec_port_binding_set_chassis(binding_rec, NULL);
c5f346a5 357 sset_find_and_delete(all_lports, binding_rec->logical_port);
263064ae 358 }
c5f346a5
BS
359 } else if (!binding_rec->chassis
360 && !strcmp(binding_rec->type, "localnet")) {
361 /* Add all localnet ports to all_lports so that we allocate ct zones
362 * for them. */
363 sset_add(all_lports, binding_rec->logical_port);
263064ae
RM
364 }
365}
366
717c7fc5 367void
4acc496e 368binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
c5f346a5
BS
369 const char *chassis_id, struct hmap *local_datapaths,
370 struct sset *all_lports)
717c7fc5 371{
71332231 372 const struct sbrec_chassis *chassis_rec;
dcda6e0d 373 const struct sbrec_port_binding *binding_rec;
6f783c06 374 struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
a6095f81
BS
375 struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
376 struct hmap qos_map;
f1fd7657 377
2ddf7558 378 chassis_rec = get_chassis(ctx->ovnsb_idl, chassis_id);
263064ae
RM
379 if (!chassis_rec) {
380 return;
381 }
71332231 382
a6095f81 383 hmap_init(&qos_map);
37c7a694 384 if (br_int) {
a6095f81
BS
385 get_local_iface_ids(br_int, &lport_to_iface, all_lports,
386 &egress_ifaces);
37c7a694 387 }
aef5f431 388
7eccc741
JP
389 /* Run through each binding record to see if it is resident on this
390 * chassis and update the binding accordingly. This includes both
391 * directly connected logical ports and children of those ports. */
926c34fd
RM
392 SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
393 consider_local_datapath(ctx, chassis_rec, binding_rec,
a6095f81
BS
394 sset_is_empty(&egress_ifaces) ? NULL :
395 &qos_map, local_datapaths, &lport_to_iface,
926c34fd 396 all_lports);
a6095f81
BS
397
398 }
399
400 if (!sset_is_empty(&egress_ifaces)
401 && set_noop_qos(ctx, &egress_ifaces)) {
402 const char *entry;
403 SSET_FOR_EACH (entry, &egress_ifaces) {
404 setup_qos(entry, &qos_map);
405 }
717c7fc5 406 }
6f783c06
RB
407
408 shash_destroy(&lport_to_iface);
a6095f81
BS
409 sset_destroy(&egress_ifaces);
410 hmap_destroy(&qos_map);
717c7fc5
JP
411}
412
f1fd7657
BP
413/* Returns true if the database is all cleaned up, false if more work is
414 * required. */
415bool
4acc496e 416binding_cleanup(struct controller_ctx *ctx, const char *chassis_id)
717c7fc5 417{
f1fd7657
BP
418 if (!ctx->ovnsb_idl_txn) {
419 return false;
420 }
717c7fc5 421
30a4256f
BP
422 if (!chassis_id) {
423 return true;
424 }
78aab811 425
f1fd7657 426 const struct sbrec_chassis *chassis_rec
2ddf7558 427 = get_chassis(ctx->ovnsb_idl, chassis_id);
71332231 428 if (!chassis_rec) {
f1fd7657 429 return true;
71332231
AW
430 }
431
dcda6e0d
BP
432 ovsdb_idl_txn_add_comment(
433 ctx->ovnsb_idl_txn,
434 "ovn-controller: removing all port bindings for '%s'", chassis_id);
717c7fc5 435
dcda6e0d 436 const struct sbrec_port_binding *binding_rec;
f1fd7657 437 bool any_changes = false;
dcda6e0d 438 SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
f1fd7657 439 if (binding_rec->chassis == chassis_rec) {
dcda6e0d 440 sbrec_port_binding_set_chassis(binding_rec, NULL);
f1fd7657 441 any_changes = true;
717c7fc5 442 }
717c7fc5 443 }
f1fd7657 444 return !any_changes;
717c7fc5 445}