]>
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" |
635c5db9 | 28 | #include "hmap.h" |
e441a806 | 29 | #include "latch.h" |
635c5db9 AW |
30 | #include "ofpbuf.h" |
31 | #include "ofproto-dpif.h" | |
0477baa9 | 32 | #include "ovs-lldp.h" |
e441a806 AW |
33 | #include "ovs-thread.h" |
34 | #include "poll-loop.h" | |
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. */ |
635c5db9 AW |
56 | uint8_t hw_addr[OFP_ETH_ALEN]; /* Hardware address. */ |
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 *, |
0477baa9 | 91 | struct cfm *, struct lldp *, uint8_t[ETH_ADDR_LEN]) |
69555fc1 | 92 | OVS_REQUIRES(monitor_mutex); |
635c5db9 | 93 | static void mport_unregister(const struct ofport_dpif *) |
69555fc1 | 94 | OVS_REQUIRES(monitor_mutex); |
635c5db9 | 95 | static void mport_update(struct mport *, struct bfd *, struct cfm *, |
0477baa9 DF |
96 | struct lldp *, uint8_t[ETH_ADDR_LEN]) |
97 | OVS_REQUIRES(monitor_mutex); | |
635c5db9 | 98 | static struct mport *mport_find(const struct ofport_dpif *) |
69555fc1 | 99 | OVS_REQUIRES(monitor_mutex); |
635c5db9 AW |
100 | |
101 | /* Tries finding and returning the 'mport' from the monitor_hmap. | |
102 | * If there is no such 'mport', returns NULL. */ | |
103 | static struct mport * | |
69555fc1 | 104 | mport_find(const struct ofport_dpif *ofport) OVS_REQUIRES(monitor_mutex) |
635c5db9 AW |
105 | { |
106 | struct mport *node; | |
107 | ||
108 | HMAP_FOR_EACH_WITH_HASH (node, hmap_node, hash_pointer(ofport, 0), | |
109 | &monitor_hmap) { | |
110 | if (node->ofport == ofport) { | |
111 | return node; | |
112 | } | |
113 | } | |
114 | return NULL; | |
115 | } | |
116 | ||
307464a1 AW |
117 | /* Creates a new mport and inserts it into monitor_hmap and monitor_heap, |
118 | * if it doesn't exist. Otherwise, just updates its fields. */ | |
635c5db9 AW |
119 | static void |
120 | mport_register(const struct ofport_dpif *ofport, struct bfd *bfd, | |
0477baa9 | 121 | struct cfm *cfm, struct lldp *lldp, uint8_t *hw_addr) |
69555fc1 | 122 | OVS_REQUIRES(monitor_mutex) |
635c5db9 AW |
123 | { |
124 | struct mport *mport = mport_find(ofport); | |
125 | ||
126 | if (!mport) { | |
127 | mport = xzalloc(sizeof *mport); | |
128 | mport->ofport = ofport; | |
129 | hmap_insert(&monitor_hmap, &mport->hmap_node, hash_pointer(ofport, 0)); | |
307464a1 | 130 | heap_insert(&monitor_heap, &mport->heap_node, 0); |
635c5db9 | 131 | } |
0477baa9 | 132 | mport_update(mport, bfd, cfm, lldp, hw_addr); |
635c5db9 AW |
133 | } |
134 | ||
307464a1 | 135 | /* Removes mport from monitor_hmap and monitor_heap and frees it. */ |
635c5db9 AW |
136 | static void |
137 | mport_unregister(const struct ofport_dpif *ofport) | |
69555fc1 | 138 | OVS_REQUIRES(monitor_mutex) |
635c5db9 AW |
139 | { |
140 | struct mport *mport = mport_find(ofport); | |
141 | ||
142 | if (mport) { | |
0477baa9 | 143 | mport_update(mport, NULL, NULL, NULL, NULL); |
635c5db9 | 144 | hmap_remove(&monitor_hmap, &mport->hmap_node); |
307464a1 | 145 | heap_remove(&monitor_heap, &mport->heap_node); |
635c5db9 AW |
146 | free(mport); |
147 | } | |
148 | } | |
149 | ||
150 | /* Updates the fields of an existing mport struct. */ | |
151 | static void | |
152 | mport_update(struct mport *mport, struct bfd *bfd, struct cfm *cfm, | |
0477baa9 DF |
153 | struct lldp *lldp, uint8_t hw_addr[ETH_ADDR_LEN]) |
154 | OVS_REQUIRES(monitor_mutex) | |
635c5db9 AW |
155 | { |
156 | ovs_assert(mport); | |
157 | ||
158 | if (mport->cfm != cfm) { | |
159 | cfm_unref(mport->cfm); | |
160 | mport->cfm = cfm_ref(cfm); | |
161 | } | |
162 | if (mport->bfd != bfd) { | |
163 | bfd_unref(mport->bfd); | |
164 | mport->bfd = bfd_ref(bfd); | |
165 | } | |
0477baa9 DF |
166 | if (mport->lldp != lldp) { |
167 | lldp_unref(mport->lldp); | |
168 | mport->lldp = lldp_ref(lldp); | |
169 | } | |
635c5db9 AW |
170 | if (hw_addr && memcmp(mport->hw_addr, hw_addr, ETH_ADDR_LEN)) { |
171 | memcpy(mport->hw_addr, hw_addr, ETH_ADDR_LEN); | |
172 | } | |
0477baa9 | 173 | /* If bfd/cfm/lldp is added or reconfigured, move the mport on top of the heap |
881d47a9 | 174 | * so that the monitor thread can run the mport next time it wakes up. */ |
0477baa9 | 175 | if (mport->bfd || mport->cfm || mport->lldp) { |
307464a1 | 176 | heap_change(&monitor_heap, &mport->heap_node, LLONG_MAX); |
e441a806 | 177 | } |
635c5db9 AW |
178 | } |
179 | \f | |
180 | ||
e441a806 AW |
181 | /* The 'main' function for the monitor thread. */ |
182 | static void * | |
183 | monitor_main(void * args OVS_UNUSED) | |
184 | { | |
e441a806 AW |
185 | VLOG_INFO("monitor thread created"); |
186 | while (!latch_is_set(&monitor_exit_latch)) { | |
e441a806 AW |
187 | monitor_run(); |
188 | latch_wait(&monitor_exit_latch); | |
e441a806 AW |
189 | poll_block(); |
190 | } | |
191 | VLOG_INFO("monitor thread terminated"); | |
192 | return NULL; | |
635c5db9 AW |
193 | } |
194 | ||
881d47a9 JS |
195 | /* The monitor thread should wake up this often to ensure that newly added or |
196 | * reconfigured monitoring ports are run in a timely manner. */ | |
197 | #define MONITOR_INTERVAL_MSEC 100 | |
198 | ||
6d308b28 AW |
199 | /* Checks the 'send_soon' list and the heap for mports that have timed |
200 | * out bfd/cfm sessions. */ | |
e441a806 AW |
201 | static void |
202 | monitor_run(void) | |
635c5db9 | 203 | { |
30fe866b | 204 | uint32_t stub[512 / 4]; |
307464a1 | 205 | long long int prio_now; |
cf62fa4c | 206 | struct dp_packet packet; |
635c5db9 | 207 | |
cf62fa4c | 208 | dp_packet_use_stub(&packet, stub, sizeof stub); |
69555fc1 | 209 | ovs_mutex_lock(&monitor_mutex); |
6d308b28 AW |
210 | |
211 | /* The monitor_check_send_soon() needs to be run twice. The first | |
212 | * time is for preventing the same 'mport' from being processed twice | |
213 | * (i.e. once from heap, the other from the 'send_soon' array). | |
214 | * The second run is to cover the case when the control packet is sent | |
215 | * via patch port and the other end needs to send back immediately. */ | |
216 | monitor_check_send_soon(&packet); | |
217 | ||
307464a1 AW |
218 | prio_now = MSEC_TO_PRIO(time_msec()); |
219 | /* Peeks the top of heap and checks if we should run this mport. */ | |
220 | while (!heap_is_empty(&monitor_heap) | |
221 | && heap_max(&monitor_heap)->priority >= prio_now) { | |
307464a1 AW |
222 | struct mport *mport; |
223 | ||
224 | mport = CONTAINER_OF(heap_max(&monitor_heap), struct mport, heap_node); | |
6d308b28 | 225 | monitor_mport_run(mport, &packet); |
307464a1 AW |
226 | } |
227 | ||
6d308b28 AW |
228 | monitor_check_send_soon(&packet); |
229 | ||
307464a1 AW |
230 | /* Waits on the earliest next wakeup time. */ |
231 | if (!heap_is_empty(&monitor_heap)) { | |
881d47a9 JS |
232 | long long int next_timeout, next_mport_wakeup; |
233 | ||
234 | next_timeout = time_msec() + MONITOR_INTERVAL_MSEC; | |
235 | next_mport_wakeup = PRIO_TO_MSEC(heap_max(&monitor_heap)->priority); | |
236 | poll_timer_wait_until(MIN(next_timeout, next_mport_wakeup)); | |
635c5db9 | 237 | } |
69555fc1 | 238 | ovs_mutex_unlock(&monitor_mutex); |
cf62fa4c | 239 | dp_packet_uninit(&packet); |
635c5db9 | 240 | } |
6d308b28 AW |
241 | |
242 | /* Checks the 'send_soon' list for any mport that needs to send cfm/bfd | |
243 | * control packet immediately, and calls monitor_mport_run(). */ | |
244 | static void | |
cf62fa4c | 245 | monitor_check_send_soon(struct dp_packet *packet) |
6d308b28 AW |
246 | OVS_REQUIRES(monitor_mutex) |
247 | { | |
248 | while (!guarded_list_is_empty(&send_soon)) { | |
249 | struct send_soon_entry *entry; | |
250 | struct mport *mport; | |
251 | ||
252 | entry = CONTAINER_OF(guarded_list_pop_front(&send_soon), | |
253 | struct send_soon_entry, list_node); | |
254 | mport = mport_find(entry->ofport); | |
255 | if (mport) { | |
256 | monitor_mport_run(mport, packet); | |
257 | } | |
258 | free(entry); | |
259 | } | |
260 | } | |
261 | ||
262 | /* Checks the sending of control packet on 'mport'. Sends the control | |
263 | * packet if needed. Executes bfd and cfm periodic functions (run, wait) | |
264 | * on 'mport'. And changes the location of 'mport' in heap based on next | |
265 | * timeout. */ | |
266 | static void | |
cf62fa4c | 267 | monitor_mport_run(struct mport *mport, struct dp_packet *packet) |
6d308b28 AW |
268 | OVS_REQUIRES(monitor_mutex) |
269 | { | |
270 | long long int next_wake_time; | |
0477baa9 DF |
271 | long long int bfd_wake_time = LLONG_MAX; |
272 | long long int cfm_wake_time = LLONG_MAX; | |
273 | long long int lldp_wake_time = LLONG_MAX; | |
6d308b28 AW |
274 | |
275 | if (mport->cfm && cfm_should_send_ccm(mport->cfm)) { | |
cf62fa4c | 276 | dp_packet_clear(packet); |
6d308b28 AW |
277 | cfm_compose_ccm(mport->cfm, packet, mport->hw_addr); |
278 | ofproto_dpif_send_packet(mport->ofport, packet); | |
279 | } | |
280 | if (mport->bfd && bfd_should_send_packet(mport->bfd)) { | |
cf62fa4c | 281 | dp_packet_clear(packet); |
6d308b28 AW |
282 | bfd_put_packet(mport->bfd, packet, mport->hw_addr); |
283 | ofproto_dpif_send_packet(mport->ofport, packet); | |
284 | } | |
0477baa9 DF |
285 | if (mport->lldp && lldp_should_send_packet(mport->lldp)) { |
286 | dp_packet_clear(packet); | |
287 | lldp_put_packet(mport->lldp, packet, mport->hw_addr); | |
288 | ofproto_dpif_send_packet(mport->ofport, packet); | |
289 | } | |
290 | ||
6d308b28 AW |
291 | if (mport->cfm) { |
292 | cfm_run(mport->cfm); | |
0477baa9 | 293 | cfm_wake_time = cfm_wait(mport->cfm); |
6d308b28 AW |
294 | } |
295 | if (mport->bfd) { | |
296 | bfd_run(mport->bfd); | |
0477baa9 DF |
297 | bfd_wake_time = bfd_wait(mport->bfd); |
298 | } | |
299 | if (mport->lldp) { | |
300 | lldp_wake_time = lldp_wait(mport->lldp); | |
6d308b28 AW |
301 | } |
302 | /* Computes the next wakeup time for this mport. */ | |
0477baa9 DF |
303 | next_wake_time = MIN(bfd_wake_time, |
304 | cfm_wake_time); | |
305 | next_wake_time = MIN(next_wake_time, lldp_wake_time); | |
6d308b28 AW |
306 | heap_change(&monitor_heap, &mport->heap_node, |
307 | MSEC_TO_PRIO(next_wake_time)); | |
308 | } | |
e441a806 | 309 | \f |
635c5db9 | 310 | |
e441a806 AW |
311 | /* Creates the mport in monitor module if either bfd or cfm |
312 | * is configured. Otherwise, deletes the mport. | |
313 | * Also checks whether the monitor thread should be started | |
314 | * or terminated. */ | |
635c5db9 | 315 | void |
e441a806 AW |
316 | ofproto_dpif_monitor_port_update(const struct ofport_dpif *ofport, |
317 | struct bfd *bfd, struct cfm *cfm, | |
0477baa9 | 318 | struct lldp *lldp, |
e441a806 | 319 | uint8_t hw_addr[ETH_ADDR_LEN]) |
635c5db9 | 320 | { |
69555fc1 | 321 | ovs_mutex_lock(&monitor_mutex); |
0477baa9 | 322 | if (!cfm && !bfd && !lldp) { |
e441a806 AW |
323 | mport_unregister(ofport); |
324 | } else { | |
0477baa9 | 325 | mport_register(ofport, bfd, cfm, lldp, hw_addr); |
635c5db9 | 326 | } |
69555fc1 | 327 | ovs_mutex_unlock(&monitor_mutex); |
e441a806 AW |
328 | |
329 | /* If the monitor thread is not running and the hmap | |
330 | * is not empty, starts it. If it is and the hmap is empty, | |
331 | * terminates it. */ | |
332 | if (!monitor_running && !hmap_is_empty(&monitor_hmap)) { | |
333 | latch_init(&monitor_exit_latch); | |
8ba0a522 | 334 | monitor_tid = ovs_thread_create("monitor", monitor_main, NULL); |
e441a806 AW |
335 | monitor_running = true; |
336 | } else if (monitor_running && hmap_is_empty(&monitor_hmap)) { | |
337 | latch_set(&monitor_exit_latch); | |
338 | xpthread_join(monitor_tid, NULL); | |
339 | latch_destroy(&monitor_exit_latch); | |
340 | monitor_running = false; | |
341 | } | |
635c5db9 | 342 | } |
60d02c72 | 343 | |
6d308b28 AW |
344 | /* Registers the 'ofport' in the 'send_soon' list. We cannot directly |
345 | * insert the corresponding mport to the 'send_soon' list, since the | |
346 | * 'send_soon' list is not updated when the mport is removed. | |
347 | * | |
348 | * Reader of the 'send_soon' list is responsible for freeing the entry. */ | |
60d02c72 AW |
349 | void |
350 | ofproto_dpif_monitor_port_send_soon(const struct ofport_dpif *ofport) | |
60d02c72 | 351 | { |
6d308b28 AW |
352 | struct send_soon_entry *entry = xzalloc(sizeof *entry); |
353 | entry->ofport = ofport; | |
60d02c72 | 354 | |
6d308b28 | 355 | guarded_list_push_back(&send_soon, &entry->list_node, SIZE_MAX); |
60d02c72 | 356 | } |