]>
Commit | Line | Data |
---|---|---|
635c5db9 | 1 | /* |
69555fc1 | 2 | * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. |
635c5db9 AW |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | #include "ofproto-dpif-monitor.h" | |
19 | ||
20 | #include <string.h> | |
21 | ||
22 | #include "bfd.h" | |
23 | #include "cfm.h" | |
cf62fa4c | 24 | #include "dp-packet.h" |
6d308b28 | 25 | #include "guarded-list.h" |
635c5db9 | 26 | #include "hash.h" |
307464a1 | 27 | #include "heap.h" |
ee89ea7b | 28 | #include "openvswitch/hmap.h" |
e441a806 | 29 | #include "latch.h" |
64c96779 | 30 | #include "openvswitch/ofpbuf.h" |
635c5db9 | 31 | #include "ofproto-dpif.h" |
0477baa9 | 32 | #include "ovs-lldp.h" |
e441a806 | 33 | #include "ovs-thread.h" |
fd016ae3 | 34 | #include "openvswitch/poll-loop.h" |
e441a806 | 35 | #include "seq.h" |
307464a1 | 36 | #include "timeval.h" |
635c5db9 | 37 | #include "util.h" |
e6211adc | 38 | #include "openvswitch/vlog.h" |
635c5db9 | 39 | |
e441a806 AW |
40 | VLOG_DEFINE_THIS_MODULE(ofproto_dpif_monitor); |
41 | ||
307464a1 AW |
42 | /* Converts the time in millisecond to heap priority. */ |
43 | #define MSEC_TO_PRIO(TIME) (LLONG_MAX - (TIME)) | |
44 | /* Converts the heap priority to time in millisecond. */ | |
45 | #define PRIO_TO_MSEC(PRIO) (LLONG_MAX - (PRIO)) | |
46 | ||
0477baa9 | 47 | /* Monitored port. It owns references to ofport, bfd, cfm, and lldp structs. */ |
635c5db9 AW |
48 | struct mport { |
49 | struct hmap_node hmap_node; /* In monitor_hmap. */ | |
307464a1 | 50 | struct heap_node heap_node; /* In monitor_heap. */ |
635c5db9 AW |
51 | const struct ofport_dpif *ofport; /* The corresponding ofport. */ |
52 | ||
53 | struct cfm *cfm; /* Reference to cfm. */ | |
54 | struct bfd *bfd; /* Reference to bfd. */ | |
0477baa9 | 55 | struct lldp *lldp; /* Reference to lldp. */ |
74ff3298 | 56 | struct eth_addr hw_addr; /* Hardware address. */ |
635c5db9 AW |
57 | }; |
58 | ||
6d308b28 AW |
59 | /* Entry of the 'send_soon' list. Contains the pointer to the |
60 | * 'ofport_dpif'. Note, the pointed object is not protected, so | |
61 | * users should always use the mport_find() to convert it to 'mport'. */ | |
62 | struct send_soon_entry { | |
ca6ba700 | 63 | struct ovs_list list_node; /* In send_soon. */ |
6d308b28 AW |
64 | const struct ofport_dpif *ofport; |
65 | }; | |
66 | ||
635c5db9 | 67 | /* hmap that contains "struct mport"s. */ |
0cf3b0d0 | 68 | static struct hmap monitor_hmap = HMAP_INITIALIZER(&monitor_hmap); |
307464a1 AW |
69 | |
70 | /* heap for ordering mport based on bfd/cfm wakeup time. */ | |
71 | static struct heap monitor_heap; | |
635c5db9 | 72 | |
6d308b28 AW |
73 | /* guarded-list for storing the mports that need to send bfd/cfm control |
74 | * packet soon. */ | |
55951e15 | 75 | static struct guarded_list send_soon = GUARDED_OVS_LIST_INITIALIZER(&send_soon); |
6d308b28 | 76 | |
e441a806 AW |
77 | /* The monitor thread id. */ |
78 | static pthread_t monitor_tid; | |
79 | /* True if the monitor thread is running. */ | |
80 | static bool monitor_running; | |
81 | ||
e441a806 | 82 | static struct latch monitor_exit_latch; |
69555fc1 | 83 | static struct ovs_mutex monitor_mutex = OVS_MUTEX_INITIALIZER; |
635c5db9 | 84 | |
e441a806 | 85 | static void *monitor_main(void *); |
cf62fa4c | 86 | static void monitor_check_send_soon(struct dp_packet *); |
e441a806 | 87 | static void monitor_run(void); |
cf62fa4c | 88 | static void monitor_mport_run(struct mport *, struct dp_packet *); |
e441a806 | 89 | |
635c5db9 | 90 | static void mport_register(const struct ofport_dpif *, struct bfd *, |
74ff3298 JR |
91 | struct cfm *, struct lldp *, |
92 | const struct eth_addr *) | |
69555fc1 | 93 | OVS_REQUIRES(monitor_mutex); |
635c5db9 | 94 | static void mport_unregister(const struct ofport_dpif *) |
69555fc1 | 95 | OVS_REQUIRES(monitor_mutex); |
635c5db9 | 96 | static void mport_update(struct mport *, struct bfd *, struct cfm *, |
74ff3298 | 97 | struct lldp *, const struct eth_addr *) |
0477baa9 | 98 | OVS_REQUIRES(monitor_mutex); |
635c5db9 | 99 | static struct mport *mport_find(const struct ofport_dpif *) |
69555fc1 | 100 | OVS_REQUIRES(monitor_mutex); |
635c5db9 AW |
101 | |
102 | /* Tries finding and returning the 'mport' from the monitor_hmap. | |
103 | * If there is no such 'mport', returns NULL. */ | |
104 | static struct mport * | |
69555fc1 | 105 | mport_find(const struct ofport_dpif *ofport) OVS_REQUIRES(monitor_mutex) |
635c5db9 AW |
106 | { |
107 | struct mport *node; | |
108 | ||
109 | HMAP_FOR_EACH_WITH_HASH (node, hmap_node, hash_pointer(ofport, 0), | |
110 | &monitor_hmap) { | |
111 | if (node->ofport == ofport) { | |
112 | return node; | |
113 | } | |
114 | } | |
115 | return NULL; | |
116 | } | |
117 | ||
307464a1 AW |
118 | /* Creates a new mport and inserts it into monitor_hmap and monitor_heap, |
119 | * if it doesn't exist. Otherwise, just updates its fields. */ | |
635c5db9 AW |
120 | static void |
121 | mport_register(const struct ofport_dpif *ofport, struct bfd *bfd, | |
74ff3298 JR |
122 | struct cfm *cfm, struct lldp *lldp, |
123 | const struct eth_addr *hw_addr) | |
69555fc1 | 124 | OVS_REQUIRES(monitor_mutex) |
635c5db9 AW |
125 | { |
126 | struct mport *mport = mport_find(ofport); | |
127 | ||
128 | if (!mport) { | |
129 | mport = xzalloc(sizeof *mport); | |
130 | mport->ofport = ofport; | |
131 | hmap_insert(&monitor_hmap, &mport->hmap_node, hash_pointer(ofport, 0)); | |
307464a1 | 132 | heap_insert(&monitor_heap, &mport->heap_node, 0); |
635c5db9 | 133 | } |
0477baa9 | 134 | mport_update(mport, bfd, cfm, lldp, hw_addr); |
635c5db9 AW |
135 | } |
136 | ||
307464a1 | 137 | /* Removes mport from monitor_hmap and monitor_heap and frees it. */ |
635c5db9 AW |
138 | static void |
139 | mport_unregister(const struct ofport_dpif *ofport) | |
69555fc1 | 140 | OVS_REQUIRES(monitor_mutex) |
635c5db9 AW |
141 | { |
142 | struct mport *mport = mport_find(ofport); | |
143 | ||
144 | if (mport) { | |
0477baa9 | 145 | mport_update(mport, NULL, NULL, NULL, NULL); |
635c5db9 | 146 | hmap_remove(&monitor_hmap, &mport->hmap_node); |
307464a1 | 147 | heap_remove(&monitor_heap, &mport->heap_node); |
635c5db9 AW |
148 | free(mport); |
149 | } | |
150 | } | |
151 | ||
152 | /* Updates the fields of an existing mport struct. */ | |
153 | static void | |
154 | mport_update(struct mport *mport, struct bfd *bfd, struct cfm *cfm, | |
74ff3298 | 155 | struct lldp *lldp, const struct eth_addr *hw_addr) |
0477baa9 | 156 | OVS_REQUIRES(monitor_mutex) |
635c5db9 AW |
157 | { |
158 | ovs_assert(mport); | |
159 | ||
160 | if (mport->cfm != cfm) { | |
161 | cfm_unref(mport->cfm); | |
162 | mport->cfm = cfm_ref(cfm); | |
163 | } | |
164 | if (mport->bfd != bfd) { | |
165 | bfd_unref(mport->bfd); | |
166 | mport->bfd = bfd_ref(bfd); | |
167 | } | |
0477baa9 DF |
168 | if (mport->lldp != lldp) { |
169 | lldp_unref(mport->lldp); | |
170 | mport->lldp = lldp_ref(lldp); | |
171 | } | |
74ff3298 JR |
172 | if (hw_addr && !eth_addr_equals(mport->hw_addr, *hw_addr)) { |
173 | mport->hw_addr = *hw_addr; | |
635c5db9 | 174 | } |
0477baa9 | 175 | /* If bfd/cfm/lldp is added or reconfigured, move the mport on top of the heap |
881d47a9 | 176 | * so that the monitor thread can run the mport next time it wakes up. */ |
0477baa9 | 177 | if (mport->bfd || mport->cfm || mport->lldp) { |
307464a1 | 178 | heap_change(&monitor_heap, &mport->heap_node, LLONG_MAX); |
e441a806 | 179 | } |
635c5db9 AW |
180 | } |
181 | \f | |
182 | ||
e441a806 AW |
183 | /* The 'main' function for the monitor thread. */ |
184 | static void * | |
185 | monitor_main(void * args OVS_UNUSED) | |
186 | { | |
e441a806 AW |
187 | VLOG_INFO("monitor thread created"); |
188 | while (!latch_is_set(&monitor_exit_latch)) { | |
e441a806 AW |
189 | monitor_run(); |
190 | latch_wait(&monitor_exit_latch); | |
e441a806 AW |
191 | poll_block(); |
192 | } | |
193 | VLOG_INFO("monitor thread terminated"); | |
194 | return NULL; | |
635c5db9 AW |
195 | } |
196 | ||
881d47a9 JS |
197 | /* The monitor thread should wake up this often to ensure that newly added or |
198 | * reconfigured monitoring ports are run in a timely manner. */ | |
199 | #define MONITOR_INTERVAL_MSEC 100 | |
200 | ||
6d308b28 AW |
201 | /* Checks the 'send_soon' list and the heap for mports that have timed |
202 | * out bfd/cfm sessions. */ | |
e441a806 AW |
203 | static void |
204 | monitor_run(void) | |
635c5db9 | 205 | { |
30fe866b | 206 | uint32_t stub[512 / 4]; |
307464a1 | 207 | long long int prio_now; |
cf62fa4c | 208 | struct dp_packet packet; |
635c5db9 | 209 | |
cf62fa4c | 210 | dp_packet_use_stub(&packet, stub, sizeof stub); |
69555fc1 | 211 | ovs_mutex_lock(&monitor_mutex); |
6d308b28 AW |
212 | |
213 | /* The monitor_check_send_soon() needs to be run twice. The first | |
214 | * time is for preventing the same 'mport' from being processed twice | |
215 | * (i.e. once from heap, the other from the 'send_soon' array). | |
216 | * The second run is to cover the case when the control packet is sent | |
217 | * via patch port and the other end needs to send back immediately. */ | |
218 | monitor_check_send_soon(&packet); | |
219 | ||
307464a1 AW |
220 | prio_now = MSEC_TO_PRIO(time_msec()); |
221 | /* Peeks the top of heap and checks if we should run this mport. */ | |
222 | while (!heap_is_empty(&monitor_heap) | |
223 | && heap_max(&monitor_heap)->priority >= prio_now) { | |
307464a1 AW |
224 | struct mport *mport; |
225 | ||
226 | mport = CONTAINER_OF(heap_max(&monitor_heap), struct mport, heap_node); | |
6d308b28 | 227 | monitor_mport_run(mport, &packet); |
307464a1 AW |
228 | } |
229 | ||
6d308b28 AW |
230 | monitor_check_send_soon(&packet); |
231 | ||
307464a1 AW |
232 | /* Waits on the earliest next wakeup time. */ |
233 | if (!heap_is_empty(&monitor_heap)) { | |
881d47a9 JS |
234 | long long int next_timeout, next_mport_wakeup; |
235 | ||
236 | next_timeout = time_msec() + MONITOR_INTERVAL_MSEC; | |
237 | next_mport_wakeup = PRIO_TO_MSEC(heap_max(&monitor_heap)->priority); | |
238 | poll_timer_wait_until(MIN(next_timeout, next_mport_wakeup)); | |
635c5db9 | 239 | } |
69555fc1 | 240 | ovs_mutex_unlock(&monitor_mutex); |
cf62fa4c | 241 | dp_packet_uninit(&packet); |
635c5db9 | 242 | } |
6d308b28 AW |
243 | |
244 | /* Checks the 'send_soon' list for any mport that needs to send cfm/bfd | |
245 | * control packet immediately, and calls monitor_mport_run(). */ | |
246 | static void | |
cf62fa4c | 247 | monitor_check_send_soon(struct dp_packet *packet) |
6d308b28 AW |
248 | OVS_REQUIRES(monitor_mutex) |
249 | { | |
250 | while (!guarded_list_is_empty(&send_soon)) { | |
251 | struct send_soon_entry *entry; | |
252 | struct mport *mport; | |
253 | ||
254 | entry = CONTAINER_OF(guarded_list_pop_front(&send_soon), | |
255 | struct send_soon_entry, list_node); | |
256 | mport = mport_find(entry->ofport); | |
257 | if (mport) { | |
258 | monitor_mport_run(mport, packet); | |
259 | } | |
260 | free(entry); | |
261 | } | |
262 | } | |
263 | ||
264 | /* Checks the sending of control packet on 'mport'. Sends the control | |
265 | * packet if needed. Executes bfd and cfm periodic functions (run, wait) | |
266 | * on 'mport'. And changes the location of 'mport' in heap based on next | |
267 | * timeout. */ | |
268 | static void | |
cf62fa4c | 269 | monitor_mport_run(struct mport *mport, struct dp_packet *packet) |
6d308b28 AW |
270 | OVS_REQUIRES(monitor_mutex) |
271 | { | |
272 | long long int next_wake_time; | |
0477baa9 DF |
273 | long long int bfd_wake_time = LLONG_MAX; |
274 | long long int cfm_wake_time = LLONG_MAX; | |
275 | long long int lldp_wake_time = LLONG_MAX; | |
6d308b28 AW |
276 | |
277 | if (mport->cfm && cfm_should_send_ccm(mport->cfm)) { | |
cf62fa4c | 278 | dp_packet_clear(packet); |
6d308b28 | 279 | cfm_compose_ccm(mport->cfm, packet, mport->hw_addr); |
2eb79142 | 280 | ofproto_dpif_send_packet(mport->ofport, false, packet); |
6d308b28 AW |
281 | } |
282 | if (mport->bfd && bfd_should_send_packet(mport->bfd)) { | |
2eb79142 JG |
283 | bool oam; |
284 | ||
cf62fa4c | 285 | dp_packet_clear(packet); |
2eb79142 JG |
286 | bfd_put_packet(mport->bfd, packet, mport->hw_addr, &oam); |
287 | ofproto_dpif_send_packet(mport->ofport, oam, packet); | |
6d308b28 | 288 | } |
0477baa9 DF |
289 | if (mport->lldp && lldp_should_send_packet(mport->lldp)) { |
290 | dp_packet_clear(packet); | |
291 | lldp_put_packet(mport->lldp, packet, mport->hw_addr); | |
2eb79142 | 292 | ofproto_dpif_send_packet(mport->ofport, false, packet); |
0477baa9 DF |
293 | } |
294 | ||
6d308b28 AW |
295 | if (mport->cfm) { |
296 | cfm_run(mport->cfm); | |
0477baa9 | 297 | cfm_wake_time = cfm_wait(mport->cfm); |
6d308b28 AW |
298 | } |
299 | if (mport->bfd) { | |
300 | bfd_run(mport->bfd); | |
0477baa9 DF |
301 | bfd_wake_time = bfd_wait(mport->bfd); |
302 | } | |
303 | if (mport->lldp) { | |
304 | lldp_wake_time = lldp_wait(mport->lldp); | |
6d308b28 AW |
305 | } |
306 | /* Computes the next wakeup time for this mport. */ | |
0477baa9 DF |
307 | next_wake_time = MIN(bfd_wake_time, |
308 | cfm_wake_time); | |
309 | next_wake_time = MIN(next_wake_time, lldp_wake_time); | |
6d308b28 AW |
310 | heap_change(&monitor_heap, &mport->heap_node, |
311 | MSEC_TO_PRIO(next_wake_time)); | |
312 | } | |
e441a806 | 313 | \f |
635c5db9 | 314 | |
e441a806 AW |
315 | /* Creates the mport in monitor module if either bfd or cfm |
316 | * is configured. Otherwise, deletes the mport. | |
317 | * Also checks whether the monitor thread should be started | |
318 | * or terminated. */ | |
635c5db9 | 319 | void |
e441a806 AW |
320 | ofproto_dpif_monitor_port_update(const struct ofport_dpif *ofport, |
321 | struct bfd *bfd, struct cfm *cfm, | |
0477baa9 | 322 | struct lldp *lldp, |
74ff3298 | 323 | const struct eth_addr *hw_addr) |
635c5db9 | 324 | { |
69555fc1 | 325 | ovs_mutex_lock(&monitor_mutex); |
0477baa9 | 326 | if (!cfm && !bfd && !lldp) { |
e441a806 AW |
327 | mport_unregister(ofport); |
328 | } else { | |
0477baa9 | 329 | mport_register(ofport, bfd, cfm, lldp, hw_addr); |
635c5db9 | 330 | } |
69555fc1 | 331 | ovs_mutex_unlock(&monitor_mutex); |
e441a806 AW |
332 | |
333 | /* If the monitor thread is not running and the hmap | |
334 | * is not empty, starts it. If it is and the hmap is empty, | |
335 | * terminates it. */ | |
336 | if (!monitor_running && !hmap_is_empty(&monitor_hmap)) { | |
337 | latch_init(&monitor_exit_latch); | |
8ba0a522 | 338 | monitor_tid = ovs_thread_create("monitor", monitor_main, NULL); |
e441a806 AW |
339 | monitor_running = true; |
340 | } else if (monitor_running && hmap_is_empty(&monitor_hmap)) { | |
341 | latch_set(&monitor_exit_latch); | |
342 | xpthread_join(monitor_tid, NULL); | |
343 | latch_destroy(&monitor_exit_latch); | |
344 | monitor_running = false; | |
345 | } | |
635c5db9 | 346 | } |
60d02c72 | 347 | |
6d308b28 AW |
348 | /* Registers the 'ofport' in the 'send_soon' list. We cannot directly |
349 | * insert the corresponding mport to the 'send_soon' list, since the | |
350 | * 'send_soon' list is not updated when the mport is removed. | |
351 | * | |
352 | * Reader of the 'send_soon' list is responsible for freeing the entry. */ | |
60d02c72 AW |
353 | void |
354 | ofproto_dpif_monitor_port_send_soon(const struct ofport_dpif *ofport) | |
60d02c72 | 355 | { |
6d308b28 AW |
356 | struct send_soon_entry *entry = xzalloc(sizeof *entry); |
357 | entry->ofport = ofport; | |
60d02c72 | 358 | |
6d308b28 | 359 | guarded_list_push_back(&send_soon, &entry->list_node, SIZE_MAX); |
60d02c72 | 360 | } |