]>
Commit | Line | Data |
---|---|---|
6094fc7f | 1 | /* Copyright (c) 2015, 2016, 2017 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" |
1da17a0b | 18 | #include "gchassis.h" |
70c7cfef RM |
19 | #include "lflow.h" |
20 | #include "lport.h" | |
717c7fc5 | 21 | |
78aab811 | 22 | #include "lib/bitmap.h" |
fdb3c70c | 23 | #include "lib/poll-loop.h" |
717c7fc5 JP |
24 | #include "lib/sset.h" |
25 | #include "lib/util.h" | |
a6095f81 | 26 | #include "lib/netdev.h" |
717c7fc5 | 27 | #include "lib/vswitch-idl.h" |
ee89ea7b | 28 | #include "openvswitch/hmap.h" |
717c7fc5 | 29 | #include "openvswitch/vlog.h" |
1da17a0b | 30 | #include "ovn/lib/chassis-index.h" |
e3df8838 | 31 | #include "ovn/lib/ovn-sb-idl.h" |
717c7fc5 JP |
32 | #include "ovn-controller.h" |
33 | ||
e387e3e8 | 34 | VLOG_DEFINE_THIS_MODULE(binding); |
717c7fc5 | 35 | |
a6095f81 BS |
36 | #define OVN_QOS_TYPE "linux-htb" |
37 | ||
38 | struct qos_queue { | |
39 | struct hmap_node node; | |
40 | uint32_t queue_id; | |
41 | uint32_t max_rate; | |
42 | uint32_t burst; | |
43 | }; | |
44 | ||
717c7fc5 | 45 | void |
4a5a9e06 | 46 | binding_register_ovs_idl(struct ovsdb_idl *ovs_idl) |
717c7fc5 | 47 | { |
4a5a9e06 BP |
48 | ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch); |
49 | ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges); | |
717c7fc5 | 50 | |
4a5a9e06 BP |
51 | ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge); |
52 | ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name); | |
53 | ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports); | |
717c7fc5 | 54 | |
4a5a9e06 BP |
55 | ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port); |
56 | ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name); | |
57 | ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces); | |
a6095f81 | 58 | ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_qos); |
717c7fc5 | 59 | |
4a5a9e06 BP |
60 | ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface); |
61 | ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name); | |
62 | ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids); | |
3475695e VA |
63 | ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd); |
64 | ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd_status); | |
a6095f81 BS |
65 | ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_status); |
66 | ||
67 | ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos); | |
68 | ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type); | |
717c7fc5 JP |
69 | } |
70 | ||
926c34fd | 71 | static void |
6f783c06 | 72 | get_local_iface_ids(const struct ovsrec_bridge *br_int, |
c5f346a5 | 73 | struct shash *lport_to_iface, |
3a60b7cd | 74 | struct sset *local_lports, |
a6095f81 | 75 | struct sset *egress_ifaces) |
717c7fc5 | 76 | { |
717c7fc5 JP |
77 | int i; |
78 | ||
422a9f73 BP |
79 | for (i = 0; i < br_int->n_ports; i++) { |
80 | const struct ovsrec_port *port_rec = br_int->ports[i]; | |
717c7fc5 JP |
81 | const char *iface_id; |
82 | int j; | |
83 | ||
422a9f73 | 84 | if (!strcmp(port_rec->name, br_int->name)) { |
717c7fc5 JP |
85 | continue; |
86 | } | |
87 | ||
88 | for (j = 0; j < port_rec->n_interfaces; j++) { | |
89 | const struct ovsrec_interface *iface_rec; | |
90 | ||
91 | iface_rec = port_rec->interfaces[j]; | |
92 | iface_id = smap_get(&iface_rec->external_ids, "iface-id"); | |
c74578c1 | 93 | int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0; |
a6095f81 | 94 | |
c74578c1 | 95 | if (iface_id && ofport > 0) { |
a6095f81 | 96 | shash_add(lport_to_iface, iface_id, iface_rec); |
3a60b7cd | 97 | sset_add(local_lports, iface_id); |
a6095f81 BS |
98 | } |
99 | ||
100 | /* Check if this is a tunnel interface. */ | |
101 | if (smap_get(&iface_rec->options, "remote_ip")) { | |
102 | const char *tunnel_iface | |
103 | = smap_get(&iface_rec->status, "tunnel_egress_iface"); | |
104 | if (tunnel_iface) { | |
105 | sset_add(egress_ifaces, tunnel_iface); | |
106 | } | |
717c7fc5 | 107 | } |
263064ae RM |
108 | } |
109 | } | |
263064ae RM |
110 | } |
111 | ||
bda5a056 | 112 | static void |
1ea9b847 BP |
113 | add_local_datapath__(const struct ldatapath_index *ldatapaths, |
114 | const struct lport_index *lports, | |
115 | const struct sbrec_datapath_binding *datapath, | |
116 | bool has_local_l3gateway, int depth, | |
117 | struct hmap *local_datapaths) | |
bda5a056 | 118 | { |
1ea9b847 BP |
119 | uint32_t dp_key = datapath->tunnel_key; |
120 | ||
121 | struct local_datapath *ld = get_local_datapath(local_datapaths, dp_key); | |
122 | if (ld) { | |
123 | if (has_local_l3gateway) { | |
124 | ld->has_local_l3gateway = true; | |
125 | } | |
6e6c3f91 | 126 | return; |
bda5a056 | 127 | } |
6e6c3f91 | 128 | |
1ea9b847 BP |
129 | ld = xzalloc(sizeof *ld); |
130 | hmap_insert(local_datapaths, &ld->hmap_node, dp_key); | |
131 | ld->datapath = datapath; | |
132 | ld->ldatapath = ldatapath_lookup_by_key(ldatapaths, dp_key); | |
133 | ovs_assert(ld->ldatapath); | |
134 | ld->localnet_port = NULL; | |
135 | ld->has_local_l3gateway = has_local_l3gateway; | |
136 | ||
137 | if (depth >= 100) { | |
138 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
139 | VLOG_WARN_RL(&rl, "datapaths nested too deep"); | |
140 | return; | |
141 | } | |
142 | ||
143 | /* Recursively add logical datapaths to which this one patches. */ | |
144 | for (size_t i = 0; i < ld->ldatapath->n_lports; i++) { | |
145 | const struct sbrec_port_binding *pb = ld->ldatapath->lports[i]; | |
146 | if (!strcmp(pb->type, "patch")) { | |
147 | const char *peer_name = smap_get(&pb->options, "peer"); | |
148 | if (peer_name) { | |
149 | const struct sbrec_port_binding *peer = lport_lookup_by_name( | |
150 | lports, peer_name); | |
151 | if (peer && peer->datapath) { | |
152 | add_local_datapath__(ldatapaths, lports, peer->datapath, | |
153 | false, depth + 1, local_datapaths); | |
3475695e VA |
154 | ld->n_peer_dps++; |
155 | ld->peer_dps = xrealloc( | |
156 | ld->peer_dps, | |
157 | ld->n_peer_dps * sizeof *ld->peer_dps); | |
158 | ld->peer_dps[ld->n_peer_dps - 1] = ldatapath_lookup_by_key( | |
159 | ldatapaths, peer->datapath->tunnel_key); | |
1ea9b847 BP |
160 | } |
161 | } | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | static void | |
167 | add_local_datapath(const struct ldatapath_index *ldatapaths, | |
168 | const struct lport_index *lports, | |
169 | const struct sbrec_datapath_binding *datapath, | |
170 | bool has_local_l3gateway, struct hmap *local_datapaths) | |
171 | { | |
172 | add_local_datapath__(ldatapaths, lports, datapath, has_local_l3gateway, 0, | |
173 | local_datapaths); | |
bda5a056 RB |
174 | } |
175 | ||
aef5f431 | 176 | static void |
a6095f81 | 177 | get_qos_params(const struct sbrec_port_binding *pb, struct hmap *queue_map) |
aef5f431 | 178 | { |
a6095f81 BS |
179 | uint32_t max_rate = smap_get_int(&pb->options, "qos_max_rate", 0); |
180 | uint32_t burst = smap_get_int(&pb->options, "qos_burst", 0); | |
181 | uint32_t queue_id = smap_get_int(&pb->options, "qdisc_queue_id", 0); | |
aef5f431 | 182 | |
a6095f81 BS |
183 | if ((!max_rate && !burst) || !queue_id) { |
184 | /* Qos is not configured for this port. */ | |
185 | return; | |
186 | } | |
187 | ||
188 | struct qos_queue *node = xzalloc(sizeof *node); | |
189 | hmap_insert(queue_map, &node->node, hash_int(queue_id, 0)); | |
190 | node->max_rate = max_rate; | |
191 | node->burst = burst; | |
192 | node->queue_id = queue_id; | |
193 | } | |
194 | ||
195 | static const struct ovsrec_qos * | |
196 | get_noop_qos(struct controller_ctx *ctx) | |
197 | { | |
198 | const struct ovsrec_qos *qos; | |
199 | OVSREC_QOS_FOR_EACH (qos, ctx->ovs_idl) { | |
200 | if (!strcmp(qos->type, "linux-noop")) { | |
201 | return qos; | |
202 | } | |
203 | } | |
204 | ||
205 | if (!ctx->ovs_idl_txn) { | |
206 | return NULL; | |
207 | } | |
208 | qos = ovsrec_qos_insert(ctx->ovs_idl_txn); | |
209 | ovsrec_qos_set_type(qos, "linux-noop"); | |
210 | return qos; | |
211 | } | |
212 | ||
213 | static bool | |
214 | set_noop_qos(struct controller_ctx *ctx, struct sset *egress_ifaces) | |
215 | { | |
216 | if (!ctx->ovs_idl_txn) { | |
217 | return false; | |
218 | } | |
219 | ||
220 | const struct ovsrec_qos *noop_qos = get_noop_qos(ctx); | |
221 | if (!noop_qos) { | |
222 | return false; | |
223 | } | |
224 | ||
225 | const struct ovsrec_port *port; | |
226 | size_t count = 0; | |
227 | ||
228 | OVSREC_PORT_FOR_EACH (port, ctx->ovs_idl) { | |
229 | if (sset_contains(egress_ifaces, port->name)) { | |
230 | ovsrec_port_set_qos(port, noop_qos); | |
231 | count++; | |
232 | } | |
233 | if (sset_count(egress_ifaces) == count) { | |
234 | break; | |
235 | } | |
236 | } | |
237 | return true; | |
238 | } | |
239 | ||
dc2dab6e BP |
240 | static void |
241 | set_qos_type(struct netdev *netdev, const char *type) | |
242 | { | |
243 | int error = netdev_set_qos(netdev, type, NULL); | |
244 | if (error) { | |
245 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
246 | VLOG_WARN_RL(&rl, "%s: could not set qdisc type \"%s\" (%s)", | |
247 | netdev_get_name(netdev), type, ovs_strerror(error)); | |
248 | } | |
249 | } | |
250 | ||
a6095f81 BS |
251 | static void |
252 | setup_qos(const char *egress_iface, struct hmap *queue_map) | |
253 | { | |
254 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); | |
255 | struct netdev *netdev_phy; | |
256 | ||
257 | if (!egress_iface) { | |
258 | /* Queues cannot be configured. */ | |
259 | return; | |
260 | } | |
261 | ||
262 | int error = netdev_open(egress_iface, NULL, &netdev_phy); | |
263 | if (error) { | |
264 | VLOG_WARN_RL(&rl, "%s: could not open netdev (%s)", | |
265 | egress_iface, ovs_strerror(error)); | |
266 | return; | |
267 | } | |
268 | ||
dc2dab6e | 269 | /* Check current qdisc. */ |
a6095f81 BS |
270 | const char *qdisc_type; |
271 | struct smap qdisc_details; | |
272 | ||
273 | smap_init(&qdisc_details); | |
274 | if (netdev_get_qos(netdev_phy, &qdisc_type, &qdisc_details) != 0 || | |
275 | qdisc_type[0] == '\0') { | |
6094fc7f | 276 | smap_destroy(&qdisc_details); |
7f42ed63 | 277 | netdev_close(netdev_phy); |
a6095f81 BS |
278 | /* Qos is not supported. */ |
279 | return; | |
280 | } | |
6094fc7f BP |
281 | smap_destroy(&qdisc_details); |
282 | ||
dc2dab6e BP |
283 | /* If we're not actually being requested to do any QoS: |
284 | * | |
285 | * - If the current qdisc type is OVN_QOS_TYPE, then we clear the qdisc | |
286 | * type to "". Otherwise, it's possible that our own leftover qdisc | |
287 | * settings could cause strange behavior on egress. Also, QoS is | |
288 | * expensive and may waste CPU time even if it's not really in use. | |
289 | * | |
290 | * OVN isn't the only software that can configure qdiscs, and | |
291 | * physical interfaces are shared resources, so there is some risk in | |
292 | * this strategy: we could disrupt some other program's QoS. | |
293 | * Probably, to entirely avoid this possibility we would need to add | |
294 | * a configuration setting. | |
295 | * | |
296 | * - Otherwise leave the qdisc alone. */ | |
297 | if (hmap_is_empty(queue_map)) { | |
298 | if (!strcmp(qdisc_type, OVN_QOS_TYPE)) { | |
299 | set_qos_type(netdev_phy, ""); | |
a6095f81 | 300 | } |
7f42ed63 | 301 | netdev_close(netdev_phy); |
dc2dab6e BP |
302 | return; |
303 | } | |
304 | ||
305 | /* Configure qdisc. */ | |
306 | if (strcmp(qdisc_type, OVN_QOS_TYPE)) { | |
307 | set_qos_type(netdev_phy, OVN_QOS_TYPE); | |
a6095f81 BS |
308 | } |
309 | ||
310 | /* Check and delete if needed. */ | |
311 | struct netdev_queue_dump dump; | |
312 | unsigned int queue_id; | |
313 | struct smap queue_details; | |
314 | struct qos_queue *sb_info; | |
315 | struct hmap consistent_queues; | |
316 | ||
317 | smap_init(&queue_details); | |
318 | hmap_init(&consistent_queues); | |
319 | NETDEV_QUEUE_FOR_EACH (&queue_id, &queue_details, &dump, netdev_phy) { | |
320 | bool is_queue_needed = false; | |
321 | ||
322 | HMAP_FOR_EACH_WITH_HASH (sb_info, node, hash_int(queue_id, 0), | |
323 | queue_map) { | |
324 | is_queue_needed = true; | |
325 | if (sb_info->max_rate == | |
326 | smap_get_int(&queue_details, "max-rate", 0) | |
327 | && sb_info->burst == smap_get_int(&queue_details, "burst", 0)) { | |
328 | /* This queue is consistent. */ | |
329 | hmap_insert(&consistent_queues, &sb_info->node, | |
330 | hash_int(queue_id, 0)); | |
331 | break; | |
332 | } | |
333 | } | |
334 | ||
335 | if (!is_queue_needed) { | |
336 | error = netdev_delete_queue(netdev_phy, queue_id); | |
337 | if (error) { | |
338 | VLOG_WARN_RL(&rl, "%s: could not delete queue %u (%s)", | |
339 | egress_iface, queue_id, ovs_strerror(error)); | |
340 | } | |
341 | } | |
342 | } | |
343 | ||
344 | /* Create/Update queues. */ | |
345 | HMAP_FOR_EACH (sb_info, node, queue_map) { | |
346 | if (hmap_contains(&consistent_queues, &sb_info->node)) { | |
347 | hmap_remove(&consistent_queues, &sb_info->node); | |
348 | continue; | |
349 | } | |
350 | ||
351 | smap_clear(&queue_details); | |
352 | smap_add_format(&queue_details, "max-rate", "%d", sb_info->max_rate); | |
353 | smap_add_format(&queue_details, "burst", "%d", sb_info->burst); | |
354 | error = netdev_set_queue(netdev_phy, sb_info->queue_id, | |
355 | &queue_details); | |
356 | if (error) { | |
357 | VLOG_WARN_RL(&rl, "%s: could not configure queue %u (%s)", | |
358 | egress_iface, sb_info->queue_id, ovs_strerror(error)); | |
359 | } | |
360 | } | |
361 | smap_destroy(&queue_details); | |
362 | hmap_destroy(&consistent_queues); | |
363 | netdev_close(netdev_phy); | |
aef5f431 BP |
364 | } |
365 | ||
263064ae | 366 | static void |
6f783c06 | 367 | consider_local_datapath(struct controller_ctx *ctx, |
1ea9b847 BP |
368 | const struct ldatapath_index *ldatapaths, |
369 | const struct lport_index *lports, | |
3475695e VA |
370 | const struct chassis_index *chassis_index, |
371 | struct sset *active_tunnels, | |
263064ae RM |
372 | const struct sbrec_chassis *chassis_rec, |
373 | const struct sbrec_port_binding *binding_rec, | |
a6095f81 | 374 | struct hmap *qos_map, |
6f783c06 | 375 | struct hmap *local_datapaths, |
c5f346a5 | 376 | struct shash *lport_to_iface, |
3a60b7cd | 377 | struct sset *local_lports) |
263064ae RM |
378 | { |
379 | const struct ovsrec_interface *iface_rec | |
6f783c06 | 380 | = shash_find_data(lport_to_iface, binding_rec->logical_port); |
3475695e | 381 | struct ovs_list *gateway_chassis = NULL; |
6f783c06 | 382 | |
f1a8bd06 | 383 | bool our_chassis = false; |
263064ae RM |
384 | if (iface_rec |
385 | || (binding_rec->parent_port && binding_rec->parent_port[0] && | |
3a60b7cd | 386 | sset_contains(local_lports, binding_rec->parent_port))) { |
926c34fd RM |
387 | if (binding_rec->parent_port && binding_rec->parent_port[0]) { |
388 | /* Add child logical port to the set of all local ports. */ | |
3a60b7cd | 389 | sset_add(local_lports, binding_rec->logical_port); |
926c34fd | 390 | } |
1ea9b847 BP |
391 | add_local_datapath(ldatapaths, lports, binding_rec->datapath, |
392 | false, local_datapaths); | |
a6095f81 BS |
393 | if (iface_rec && qos_map && ctx->ovs_idl_txn) { |
394 | get_qos_params(binding_rec, qos_map); | |
263064ae | 395 | } |
2a38ef45 DA |
396 | /* This port is in our chassis unless it is a localport. */ |
397 | if (strcmp(binding_rec->type, "localport")) { | |
398 | our_chassis = true; | |
399 | } | |
f1a8bd06 BP |
400 | } else if (!strcmp(binding_rec->type, "l2gateway")) { |
401 | const char *chassis_id = smap_get(&binding_rec->options, | |
402 | "l2gateway-chassis"); | |
403 | our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name); | |
404 | if (our_chassis) { | |
3a60b7cd | 405 | sset_add(local_lports, binding_rec->logical_port); |
f1a8bd06 BP |
406 | add_local_datapath(ldatapaths, lports, binding_rec->datapath, |
407 | false, local_datapaths); | |
408 | } | |
41a15b71 | 409 | } else if (!strcmp(binding_rec->type, "chassisredirect")) { |
3475695e VA |
410 | gateway_chassis = gateway_chassis_get_ordered(binding_rec, |
411 | chassis_index); | |
412 | if (gateway_chassis && | |
413 | gateway_chassis_contains(gateway_chassis, chassis_rec)) { | |
508b7f96 | 414 | |
415 | our_chassis = gateway_chassis_is_active( | |
416 | gateway_chassis, chassis_rec, active_tunnels); | |
417 | ||
41a15b71 MS |
418 | add_local_datapath(ldatapaths, lports, binding_rec->datapath, |
419 | false, local_datapaths); | |
420 | } | |
3475695e | 421 | gateway_chassis_destroy(gateway_chassis); |
f1a8bd06 BP |
422 | } else if (!strcmp(binding_rec->type, "l3gateway")) { |
423 | const char *chassis_id = smap_get(&binding_rec->options, | |
424 | "l3gateway-chassis"); | |
425 | our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name); | |
426 | if (our_chassis) { | |
427 | add_local_datapath(ldatapaths, lports, binding_rec->datapath, | |
428 | true, local_datapaths); | |
263064ae | 429 | } |
f1a8bd06 | 430 | } else if (!strcmp(binding_rec->type, "localnet")) { |
3a60b7cd | 431 | /* Add all localnet ports to local_lports so that we allocate ct zones |
f1a8bd06 | 432 | * for them. */ |
3a60b7cd | 433 | sset_add(local_lports, binding_rec->logical_port); |
f1a8bd06 BP |
434 | our_chassis = false; |
435 | } | |
436 | ||
437 | if (ctx->ovnsb_idl_txn) { | |
438 | if (our_chassis) { | |
f90bb090 MS |
439 | if (binding_rec->chassis != chassis_rec) { |
440 | if (binding_rec->chassis) { | |
441 | VLOG_INFO("Changing chassis for lport %s from %s to %s.", | |
442 | binding_rec->logical_port, | |
443 | binding_rec->chassis->name, | |
444 | chassis_rec->name); | |
445 | } else { | |
446 | VLOG_INFO("Claiming lport %s for this chassis.", | |
447 | binding_rec->logical_port); | |
448 | } | |
449 | for (int i = 0; i < binding_rec->n_mac; i++) { | |
450 | VLOG_INFO("%s: Claiming %s", | |
451 | binding_rec->logical_port, binding_rec->mac[i]); | |
452 | } | |
453 | sbrec_port_binding_set_chassis(binding_rec, chassis_rec); | |
62b87eab | 454 | } |
f1a8bd06 | 455 | } else if (binding_rec->chassis == chassis_rec) { |
263064ae RM |
456 | VLOG_INFO("Releasing lport %s from this chassis.", |
457 | binding_rec->logical_port); | |
458 | sbrec_port_binding_set_chassis(binding_rec, NULL); | |
459 | } | |
263064ae RM |
460 | } |
461 | } | |
462 | ||
41319fc9 HZ |
463 | static void |
464 | consider_localnet_port(const struct sbrec_port_binding *binding_rec, | |
465 | struct hmap *local_datapaths) | |
466 | { | |
467 | struct local_datapath *ld | |
468 | = get_local_datapath(local_datapaths, | |
469 | binding_rec->datapath->tunnel_key); | |
470 | if (!ld) { | |
471 | return; | |
472 | } | |
473 | ||
474 | if (ld->localnet_port && strcmp(ld->localnet_port->logical_port, | |
475 | binding_rec->logical_port)) { | |
476 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); | |
477 | VLOG_WARN_RL(&rl, "localnet port '%s' already set for datapath " | |
478 | "'%"PRId64"', skipping the new port '%s'.", | |
479 | ld->localnet_port->logical_port, | |
480 | binding_rec->datapath->tunnel_key, | |
481 | binding_rec->logical_port); | |
482 | return; | |
483 | } | |
484 | ld->localnet_port = binding_rec; | |
485 | } | |
486 | ||
717c7fc5 | 487 | void |
4acc496e | 488 | binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int, |
9211bbea BP |
489 | const struct sbrec_chassis *chassis_rec, |
490 | const struct ldatapath_index *ldatapaths, | |
3475695e VA |
491 | const struct lport_index *lports, |
492 | const struct chassis_index *chassis_index, | |
493 | struct sset *active_tunnels, | |
494 | struct hmap *local_datapaths, struct sset *local_lports) | |
717c7fc5 | 495 | { |
9211bbea BP |
496 | if (!chassis_rec) { |
497 | return; | |
498 | } | |
499 | ||
dcda6e0d | 500 | const struct sbrec_port_binding *binding_rec; |
6f783c06 | 501 | struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface); |
a6095f81 BS |
502 | struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces); |
503 | struct hmap qos_map; | |
f1fd7657 | 504 | |
a6095f81 | 505 | hmap_init(&qos_map); |
37c7a694 | 506 | if (br_int) { |
3a60b7cd | 507 | get_local_iface_ids(br_int, &lport_to_iface, local_lports, |
a6095f81 | 508 | &egress_ifaces); |
37c7a694 | 509 | } |
aef5f431 | 510 | |
7eccc741 JP |
511 | /* Run through each binding record to see if it is resident on this |
512 | * chassis and update the binding accordingly. This includes both | |
513 | * directly connected logical ports and children of those ports. */ | |
926c34fd | 514 | SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) { |
3475695e VA |
515 | consider_local_datapath(ctx, ldatapaths, lports, chassis_index, |
516 | active_tunnels, chassis_rec, binding_rec, | |
a6095f81 BS |
517 | sset_is_empty(&egress_ifaces) ? NULL : |
518 | &qos_map, local_datapaths, &lport_to_iface, | |
3a60b7cd | 519 | local_lports); |
a6095f81 BS |
520 | |
521 | } | |
522 | ||
41319fc9 HZ |
523 | /* Run through each binding record to see if it is a localnet port |
524 | * on local datapaths discovered from above loop, and update the | |
525 | * corresponding local datapath accordingly. */ | |
526 | SBREC_PORT_BINDING_FOR_EACH (binding_rec, ctx->ovnsb_idl) { | |
527 | if (!strcmp(binding_rec->type, "localnet")) { | |
528 | consider_localnet_port(binding_rec, local_datapaths); | |
529 | } | |
530 | } | |
531 | ||
a6095f81 BS |
532 | if (!sset_is_empty(&egress_ifaces) |
533 | && set_noop_qos(ctx, &egress_ifaces)) { | |
534 | const char *entry; | |
535 | SSET_FOR_EACH (entry, &egress_ifaces) { | |
536 | setup_qos(entry, &qos_map); | |
537 | } | |
717c7fc5 | 538 | } |
6f783c06 RB |
539 | |
540 | shash_destroy(&lport_to_iface); | |
a6095f81 BS |
541 | sset_destroy(&egress_ifaces); |
542 | hmap_destroy(&qos_map); | |
717c7fc5 JP |
543 | } |
544 | ||
f1fd7657 BP |
545 | /* Returns true if the database is all cleaned up, false if more work is |
546 | * required. */ | |
547 | bool | |
9211bbea BP |
548 | binding_cleanup(struct controller_ctx *ctx, |
549 | const struct sbrec_chassis *chassis_rec) | |
717c7fc5 | 550 | { |
f1fd7657 BP |
551 | if (!ctx->ovnsb_idl_txn) { |
552 | return false; | |
553 | } | |
71332231 | 554 | if (!chassis_rec) { |
f1fd7657 | 555 | return true; |
71332231 AW |
556 | } |
557 | ||
dcda6e0d BP |
558 | ovsdb_idl_txn_add_comment( |
559 | ctx->ovnsb_idl_txn, | |
9211bbea BP |
560 | "ovn-controller: removing all port bindings for '%s'", |
561 | chassis_rec->name); | |
717c7fc5 | 562 | |
dcda6e0d | 563 | const struct sbrec_port_binding *binding_rec; |
f1fd7657 | 564 | bool any_changes = false; |
dcda6e0d | 565 | SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) { |
f1fd7657 | 566 | if (binding_rec->chassis == chassis_rec) { |
dcda6e0d | 567 | sbrec_port_binding_set_chassis(binding_rec, NULL); |
f1fd7657 | 568 | any_changes = true; |
717c7fc5 | 569 | } |
717c7fc5 | 570 | } |
f1fd7657 | 571 | return !any_changes; |
717c7fc5 | 572 | } |