]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. | |
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" | |
24 | #include "dp-packet.h" | |
25 | #include "guarded-list.h" | |
26 | #include "hash.h" | |
27 | #include "heap.h" | |
28 | #include "openvswitch/hmap.h" | |
29 | #include "latch.h" | |
30 | #include "openvswitch/ofpbuf.h" | |
31 | #include "ofproto-dpif.h" | |
32 | #include "ovs-lldp.h" | |
33 | #include "ovs-thread.h" | |
34 | #include "poll-loop.h" | |
35 | #include "seq.h" | |
36 | #include "timeval.h" | |
37 | #include "util.h" | |
38 | #include "openvswitch/vlog.h" | |
39 | ||
40 | VLOG_DEFINE_THIS_MODULE(ofproto_dpif_monitor); | |
41 | ||
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 | ||
47 | /* Monitored port. It owns references to ofport, bfd, cfm, and lldp structs. */ | |
48 | struct mport { | |
49 | struct hmap_node hmap_node; /* In monitor_hmap. */ | |
50 | struct heap_node heap_node; /* In monitor_heap. */ | |
51 | const struct ofport_dpif *ofport; /* The corresponding ofport. */ | |
52 | ||
53 | struct cfm *cfm; /* Reference to cfm. */ | |
54 | struct bfd *bfd; /* Reference to bfd. */ | |
55 | struct lldp *lldp; /* Reference to lldp. */ | |
56 | struct eth_addr hw_addr; /* Hardware address. */ | |
57 | }; | |
58 | ||
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 { | |
63 | struct ovs_list list_node; /* In send_soon. */ | |
64 | const struct ofport_dpif *ofport; | |
65 | }; | |
66 | ||
67 | /* hmap that contains "struct mport"s. */ | |
68 | static struct hmap monitor_hmap = HMAP_INITIALIZER(&monitor_hmap); | |
69 | ||
70 | /* heap for ordering mport based on bfd/cfm wakeup time. */ | |
71 | static struct heap monitor_heap; | |
72 | ||
73 | /* guarded-list for storing the mports that need to send bfd/cfm control | |
74 | * packet soon. */ | |
75 | static struct guarded_list send_soon = GUARDED_OVS_LIST_INITIALIZER(&send_soon); | |
76 | ||
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 | ||
82 | static struct latch monitor_exit_latch; | |
83 | static struct ovs_mutex monitor_mutex = OVS_MUTEX_INITIALIZER; | |
84 | ||
85 | static void *monitor_main(void *); | |
86 | static void monitor_check_send_soon(struct dp_packet *); | |
87 | static void monitor_run(void); | |
88 | static void monitor_mport_run(struct mport *, struct dp_packet *); | |
89 | ||
90 | static void mport_register(const struct ofport_dpif *, struct bfd *, | |
91 | struct cfm *, struct lldp *, | |
92 | const struct eth_addr *) | |
93 | OVS_REQUIRES(monitor_mutex); | |
94 | static void mport_unregister(const struct ofport_dpif *) | |
95 | OVS_REQUIRES(monitor_mutex); | |
96 | static void mport_update(struct mport *, struct bfd *, struct cfm *, | |
97 | struct lldp *, const struct eth_addr *) | |
98 | OVS_REQUIRES(monitor_mutex); | |
99 | static struct mport *mport_find(const struct ofport_dpif *) | |
100 | OVS_REQUIRES(monitor_mutex); | |
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 * | |
105 | mport_find(const struct ofport_dpif *ofport) OVS_REQUIRES(monitor_mutex) | |
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 | ||
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. */ | |
120 | static void | |
121 | mport_register(const struct ofport_dpif *ofport, struct bfd *bfd, | |
122 | struct cfm *cfm, struct lldp *lldp, | |
123 | const struct eth_addr *hw_addr) | |
124 | OVS_REQUIRES(monitor_mutex) | |
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)); | |
132 | heap_insert(&monitor_heap, &mport->heap_node, 0); | |
133 | } | |
134 | mport_update(mport, bfd, cfm, lldp, hw_addr); | |
135 | } | |
136 | ||
137 | /* Removes mport from monitor_hmap and monitor_heap and frees it. */ | |
138 | static void | |
139 | mport_unregister(const struct ofport_dpif *ofport) | |
140 | OVS_REQUIRES(monitor_mutex) | |
141 | { | |
142 | struct mport *mport = mport_find(ofport); | |
143 | ||
144 | if (mport) { | |
145 | mport_update(mport, NULL, NULL, NULL, NULL); | |
146 | hmap_remove(&monitor_hmap, &mport->hmap_node); | |
147 | heap_remove(&monitor_heap, &mport->heap_node); | |
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, | |
155 | struct lldp *lldp, const struct eth_addr *hw_addr) | |
156 | OVS_REQUIRES(monitor_mutex) | |
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 | } | |
168 | if (mport->lldp != lldp) { | |
169 | lldp_unref(mport->lldp); | |
170 | mport->lldp = lldp_ref(lldp); | |
171 | } | |
172 | if (hw_addr && !eth_addr_equals(mport->hw_addr, *hw_addr)) { | |
173 | mport->hw_addr = *hw_addr; | |
174 | } | |
175 | /* If bfd/cfm/lldp is added or reconfigured, move the mport on top of the heap | |
176 | * so that the monitor thread can run the mport next time it wakes up. */ | |
177 | if (mport->bfd || mport->cfm || mport->lldp) { | |
178 | heap_change(&monitor_heap, &mport->heap_node, LLONG_MAX); | |
179 | } | |
180 | } | |
181 | \f | |
182 | ||
183 | /* The 'main' function for the monitor thread. */ | |
184 | static void * | |
185 | monitor_main(void * args OVS_UNUSED) | |
186 | { | |
187 | VLOG_INFO("monitor thread created"); | |
188 | while (!latch_is_set(&monitor_exit_latch)) { | |
189 | monitor_run(); | |
190 | latch_wait(&monitor_exit_latch); | |
191 | poll_block(); | |
192 | } | |
193 | VLOG_INFO("monitor thread terminated"); | |
194 | return NULL; | |
195 | } | |
196 | ||
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 | ||
201 | /* Checks the 'send_soon' list and the heap for mports that have timed | |
202 | * out bfd/cfm sessions. */ | |
203 | static void | |
204 | monitor_run(void) | |
205 | { | |
206 | uint32_t stub[512 / 4]; | |
207 | long long int prio_now; | |
208 | struct dp_packet packet; | |
209 | ||
210 | dp_packet_use_stub(&packet, stub, sizeof stub); | |
211 | ovs_mutex_lock(&monitor_mutex); | |
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 | ||
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) { | |
224 | struct mport *mport; | |
225 | ||
226 | mport = CONTAINER_OF(heap_max(&monitor_heap), struct mport, heap_node); | |
227 | monitor_mport_run(mport, &packet); | |
228 | } | |
229 | ||
230 | monitor_check_send_soon(&packet); | |
231 | ||
232 | /* Waits on the earliest next wakeup time. */ | |
233 | if (!heap_is_empty(&monitor_heap)) { | |
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)); | |
239 | } | |
240 | ovs_mutex_unlock(&monitor_mutex); | |
241 | dp_packet_uninit(&packet); | |
242 | } | |
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 | |
247 | monitor_check_send_soon(struct dp_packet *packet) | |
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 | |
269 | monitor_mport_run(struct mport *mport, struct dp_packet *packet) | |
270 | OVS_REQUIRES(monitor_mutex) | |
271 | { | |
272 | long long int next_wake_time; | |
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; | |
276 | ||
277 | if (mport->cfm && cfm_should_send_ccm(mport->cfm)) { | |
278 | dp_packet_clear(packet); | |
279 | cfm_compose_ccm(mport->cfm, packet, mport->hw_addr); | |
280 | ofproto_dpif_send_packet(mport->ofport, false, packet); | |
281 | } | |
282 | if (mport->bfd && bfd_should_send_packet(mport->bfd)) { | |
283 | bool oam; | |
284 | ||
285 | dp_packet_clear(packet); | |
286 | bfd_put_packet(mport->bfd, packet, mport->hw_addr, &oam); | |
287 | ofproto_dpif_send_packet(mport->ofport, oam, packet); | |
288 | } | |
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); | |
292 | ofproto_dpif_send_packet(mport->ofport, false, packet); | |
293 | } | |
294 | ||
295 | if (mport->cfm) { | |
296 | cfm_run(mport->cfm); | |
297 | cfm_wake_time = cfm_wait(mport->cfm); | |
298 | } | |
299 | if (mport->bfd) { | |
300 | bfd_run(mport->bfd); | |
301 | bfd_wake_time = bfd_wait(mport->bfd); | |
302 | } | |
303 | if (mport->lldp) { | |
304 | lldp_wake_time = lldp_wait(mport->lldp); | |
305 | } | |
306 | /* Computes the next wakeup time for this mport. */ | |
307 | next_wake_time = MIN(bfd_wake_time, | |
308 | cfm_wake_time); | |
309 | next_wake_time = MIN(next_wake_time, lldp_wake_time); | |
310 | heap_change(&monitor_heap, &mport->heap_node, | |
311 | MSEC_TO_PRIO(next_wake_time)); | |
312 | } | |
313 | \f | |
314 | ||
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. */ | |
319 | void | |
320 | ofproto_dpif_monitor_port_update(const struct ofport_dpif *ofport, | |
321 | struct bfd *bfd, struct cfm *cfm, | |
322 | struct lldp *lldp, | |
323 | const struct eth_addr *hw_addr) | |
324 | { | |
325 | ovs_mutex_lock(&monitor_mutex); | |
326 | if (!cfm && !bfd && !lldp) { | |
327 | mport_unregister(ofport); | |
328 | } else { | |
329 | mport_register(ofport, bfd, cfm, lldp, hw_addr); | |
330 | } | |
331 | ovs_mutex_unlock(&monitor_mutex); | |
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); | |
338 | monitor_tid = ovs_thread_create("monitor", monitor_main, NULL); | |
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 | } | |
346 | } | |
347 | ||
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. */ | |
353 | void | |
354 | ofproto_dpif_monitor_port_send_soon(const struct ofport_dpif *ofport) | |
355 | { | |
356 | struct send_soon_entry *entry = xzalloc(sizeof *entry); | |
357 | entry->ofport = ofport; | |
358 | ||
359 | guarded_list_push_back(&send_soon, &entry->list_node, SIZE_MAX); | |
360 | } |