Branch data Line data Source code
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 : 1288 : 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 : 111721 : mport_find(const struct ofport_dpif *ofport) OVS_REQUIRES(monitor_mutex)
106 : : {
107 : : struct mport *node;
108 : :
109 [ + + ][ - + ]: 111721 : HMAP_FOR_EACH_WITH_HASH (node, hmap_node, hash_pointer(ofport, 0),
110 : : &monitor_hmap) {
111 [ + - ]: 60739 : if (node->ofport == ofport) {
112 : 60739 : return node;
113 : : }
114 : : }
115 : 50982 : 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 : 60290 : 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 : 60290 : struct mport *mport = mport_find(ofport);
127 : :
128 [ + + ]: 60290 : if (!mport) {
129 : 247 : mport = xzalloc(sizeof *mport);
130 : 247 : mport->ofport = ofport;
131 : 247 : hmap_insert(&monitor_hmap, &mport->hmap_node, hash_pointer(ofport, 0));
132 : 247 : heap_insert(&monitor_heap, &mport->heap_node, 0);
133 : : }
134 : 60290 : mport_update(mport, bfd, cfm, lldp, hw_addr);
135 : 60290 : }
136 : :
137 : : /* Removes mport from monitor_hmap and monitor_heap and frees it. */
138 : : static void
139 : 50982 : mport_unregister(const struct ofport_dpif *ofport)
140 : : OVS_REQUIRES(monitor_mutex)
141 : : {
142 : 50982 : struct mport *mport = mport_find(ofport);
143 : :
144 [ + + ]: 50982 : if (mport) {
145 : 247 : mport_update(mport, NULL, NULL, NULL, NULL);
146 : 247 : hmap_remove(&monitor_hmap, &mport->hmap_node);
147 : 247 : heap_remove(&monitor_heap, &mport->heap_node);
148 : 247 : free(mport);
149 : : }
150 : 50982 : }
151 : :
152 : : /* Updates the fields of an existing mport struct. */
153 : : static void
154 : 60537 : 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 [ - + ]: 60537 : ovs_assert(mport);
159 : :
160 [ + + ]: 60537 : if (mport->cfm != cfm) {
161 : 32 : cfm_unref(mport->cfm);
162 : 32 : mport->cfm = cfm_ref(cfm);
163 : : }
164 [ + + ]: 60537 : if (mport->bfd != bfd) {
165 : 468 : bfd_unref(mport->bfd);
166 : 468 : mport->bfd = bfd_ref(bfd);
167 : : }
168 [ - + ]: 60537 : if (mport->lldp != lldp) {
169 : 0 : lldp_unref(mport->lldp);
170 : 0 : mport->lldp = lldp_ref(lldp);
171 : : }
172 [ + + ][ + + ]: 60537 : if (hw_addr && !eth_addr_equals(mport->hw_addr, *hw_addr)) {
173 : 247 : 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 [ + + ][ + + ]: 60537 : if (mport->bfd || mport->cfm || mport->lldp) {
[ - + ]
178 : 60290 : heap_change(&monitor_heap, &mport->heap_node, LLONG_MAX);
179 : : }
180 : 60537 : }
181 : :
182 : :
183 : : /* The 'main' function for the monitor thread. */
184 : : static void *
185 : 22 : monitor_main(void * args OVS_UNUSED)
186 : : {
187 [ + - ]: 22 : VLOG_INFO("monitor thread created");
188 [ + + ]: 9006 : while (!latch_is_set(&monitor_exit_latch)) {
189 : 8984 : monitor_run();
190 : 8984 : latch_wait(&monitor_exit_latch);
191 : 8984 : poll_block();
192 : : }
193 [ + - ]: 22 : VLOG_INFO("monitor thread terminated");
194 : 22 : 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 : 8984 : monitor_run(void)
205 : : {
206 : : uint32_t stub[512 / 4];
207 : : long long int prio_now;
208 : : struct dp_packet packet;
209 : :
210 : 8984 : dp_packet_use_stub(&packet, stub, sizeof stub);
211 : 8984 : 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 : 8984 : monitor_check_send_soon(&packet);
219 : :
220 : 8984 : prio_now = MSEC_TO_PRIO(time_msec());
221 : : /* Peeks the top of heap and checks if we should run this mport. */
222 [ + - ]: 35299 : while (!heap_is_empty(&monitor_heap)
223 [ + + ]: 35299 : && heap_max(&monitor_heap)->priority >= prio_now) {
224 : : struct mport *mport;
225 : :
226 : 26315 : mport = CONTAINER_OF(heap_max(&monitor_heap), struct mport, heap_node);
227 : 26315 : monitor_mport_run(mport, &packet);
228 : : }
229 : :
230 : 8984 : monitor_check_send_soon(&packet);
231 : :
232 : : /* Waits on the earliest next wakeup time. */
233 [ + - ]: 8984 : if (!heap_is_empty(&monitor_heap)) {
234 : : long long int next_timeout, next_mport_wakeup;
235 : :
236 : 8984 : next_timeout = time_msec() + MONITOR_INTERVAL_MSEC;
237 : 8984 : next_mport_wakeup = PRIO_TO_MSEC(heap_max(&monitor_heap)->priority);
238 : 8984 : poll_timer_wait_until(MIN(next_timeout, next_mport_wakeup));
239 : : }
240 : 8984 : ovs_mutex_unlock(&monitor_mutex);
241 : 8984 : dp_packet_uninit(&packet);
242 : 8984 : }
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 : 17968 : monitor_check_send_soon(struct dp_packet *packet)
248 : : OVS_REQUIRES(monitor_mutex)
249 : : {
250 [ + + ]: 18417 : while (!guarded_list_is_empty(&send_soon)) {
251 : : struct send_soon_entry *entry;
252 : : struct mport *mport;
253 : :
254 : 449 : entry = CONTAINER_OF(guarded_list_pop_front(&send_soon),
255 : : struct send_soon_entry, list_node);
256 : 449 : mport = mport_find(entry->ofport);
257 [ + - ]: 449 : if (mport) {
258 : 449 : monitor_mport_run(mport, packet);
259 : : }
260 : 449 : free(entry);
261 : : }
262 : 17968 : }
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 : 26764 : monitor_mport_run(struct mport *mport, struct dp_packet *packet)
270 : : OVS_REQUIRES(monitor_mutex)
271 : : {
272 : : long long int next_wake_time;
273 : 26764 : long long int bfd_wake_time = LLONG_MAX;
274 : 26764 : long long int cfm_wake_time = LLONG_MAX;
275 : 26764 : long long int lldp_wake_time = LLONG_MAX;
276 : :
277 [ + + ][ + + ]: 26764 : if (mport->cfm && cfm_should_send_ccm(mport->cfm)) {
278 : 1234 : dp_packet_clear(packet);
279 : 1234 : cfm_compose_ccm(mport->cfm, packet, mport->hw_addr);
280 : 1234 : ofproto_dpif_send_packet(mport->ofport, false, packet);
281 : : }
282 [ + + ][ + + ]: 26764 : if (mport->bfd && bfd_should_send_packet(mport->bfd)) {
283 : : bool oam;
284 : :
285 : 5198 : dp_packet_clear(packet);
286 : 5198 : bfd_put_packet(mport->bfd, packet, mport->hw_addr, &oam);
287 : 5198 : ofproto_dpif_send_packet(mport->ofport, oam, packet);
288 : : }
289 [ - + ][ # # ]: 26764 : if (mport->lldp && lldp_should_send_packet(mport->lldp)) {
290 : 0 : dp_packet_clear(packet);
291 : 0 : lldp_put_packet(mport->lldp, packet, mport->hw_addr);
292 : 0 : ofproto_dpif_send_packet(mport->ofport, false, packet);
293 : : }
294 : :
295 [ + + ]: 26764 : if (mport->cfm) {
296 : 1388 : cfm_run(mport->cfm);
297 : 1388 : cfm_wake_time = cfm_wait(mport->cfm);
298 : : }
299 [ + + ]: 26764 : if (mport->bfd) {
300 : 25410 : bfd_run(mport->bfd);
301 : 25410 : bfd_wake_time = bfd_wait(mport->bfd);
302 : : }
303 [ - + ]: 26764 : if (mport->lldp) {
304 : 0 : lldp_wake_time = lldp_wait(mport->lldp);
305 : : }
306 : : /* Computes the next wakeup time for this mport. */
307 : 26764 : next_wake_time = MIN(bfd_wake_time,
308 : : cfm_wake_time);
309 : 26764 : next_wake_time = MIN(next_wake_time, lldp_wake_time);
310 : 26764 : heap_change(&monitor_heap, &mport->heap_node,
311 : 26764 : MSEC_TO_PRIO(next_wake_time));
312 : 26764 : }
313 : :
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 : 111272 : 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 : 111272 : ovs_mutex_lock(&monitor_mutex);
326 [ + + ][ + + ]: 111272 : if (!cfm && !bfd && !lldp) {
[ + - ]
327 : 50982 : mport_unregister(ofport);
328 : : } else {
329 : 60290 : mport_register(ofport, bfd, cfm, lldp, hw_addr);
330 : : }
331 : 111272 : 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 [ + + ][ + + ]: 111272 : if (!monitor_running && !hmap_is_empty(&monitor_hmap)) {
337 : 22 : latch_init(&monitor_exit_latch);
338 : 22 : monitor_tid = ovs_thread_create("monitor", monitor_main, NULL);
339 : 22 : monitor_running = true;
340 [ + + ][ + + ]: 111250 : } else if (monitor_running && hmap_is_empty(&monitor_hmap)) {
341 : 22 : latch_set(&monitor_exit_latch);
342 : 22 : xpthread_join(monitor_tid, NULL);
343 : 22 : latch_destroy(&monitor_exit_latch);
344 : 22 : monitor_running = false;
345 : : }
346 : 111272 : }
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 : 449 : ofproto_dpif_monitor_port_send_soon(const struct ofport_dpif *ofport)
355 : : {
356 : 449 : struct send_soon_entry *entry = xzalloc(sizeof *entry);
357 : 449 : entry->ofport = ofport;
358 : :
359 : 449 : guarded_list_push_back(&send_soon, &entry->list_node, SIZE_MAX);
360 : 449 : }
|