]>
Commit | Line | Data |
---|---|---|
2f51a7eb | 1 | /* Copyright (c) 2010, 2012, 2013, 2014 Nicira, Inc. |
ce887677 BP |
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> | |
17 | ||
57c8677b BP |
18 | #include "system-stats.h" |
19 | ||
ce887677 BP |
20 | #include <ctype.h> |
21 | #include <dirent.h> | |
22 | #include <errno.h> | |
23 | #if HAVE_MNTENT_H | |
24 | #include <mntent.h> | |
25 | #endif | |
ce887677 BP |
26 | #include <stdint.h> |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #if HAVE_SYS_STATVFS_H | |
30 | #include <sys/statvfs.h> | |
31 | #endif | |
32 | #include <unistd.h> | |
33 | ||
34 | #include "daemon.h" | |
35 | #include "dirs.h" | |
3e8a2ad1 | 36 | #include "openvswitch/dynamic-string.h" |
35a22d8c | 37 | #include "json.h" |
59987a62 | 38 | #include "latch.h" |
64c96779 | 39 | #include "openvswitch/ofpbuf.h" |
5724fca4 | 40 | #include "ovs-rcu.h" |
59987a62 | 41 | #include "ovs-thread.h" |
35a22d8c | 42 | #include "poll-loop.h" |
ce887677 | 43 | #include "shash.h" |
57c8677b | 44 | #include "smap.h" |
ce887677 | 45 | #include "timeval.h" |
e6211adc | 46 | #include "openvswitch/vlog.h" |
ce887677 | 47 | |
d98e6007 | 48 | VLOG_DEFINE_THIS_MODULE(system_stats); |
ce887677 BP |
49 | |
50 | /* #ifdefs make it a pain to maintain code: you have to try to build both ways. | |
51 | * Thus, this file tries to compile as much of the code as possible regardless | |
2f51a7eb BP |
52 | * of the target, by writing "if (LINUX)" instead of "#ifdef __linux__" where |
53 | * this is possible. */ | |
54 | #ifdef __linux__ | |
55 | #define LINUX 1 | |
ce887677 | 56 | #include <asm/param.h> |
ce887677 | 57 | #else |
2f51a7eb | 58 | #define LINUX 0 |
ce887677 BP |
59 | #endif |
60 | ||
61 | static void | |
57c8677b | 62 | get_cpu_cores(struct smap *stats) |
ce887677 | 63 | { |
0122f6e6 | 64 | long int n_cores = count_cpu_cores(); |
ce887677 | 65 | if (n_cores > 0) { |
57c8677b | 66 | smap_add_format(stats, "cpu", "%ld", n_cores); |
ce887677 BP |
67 | } |
68 | } | |
69 | ||
70 | static void | |
57c8677b | 71 | get_load_average(struct smap *stats OVS_UNUSED) |
ce887677 BP |
72 | { |
73 | #if HAVE_GETLOADAVG | |
74 | double loadavg[3]; | |
75 | ||
76 | if (getloadavg(loadavg, 3) == 3) { | |
57c8677b BP |
77 | smap_add_format(stats, "load_average", "%.2f,%.2f,%.2f", |
78 | loadavg[0], loadavg[1], loadavg[2]); | |
ce887677 BP |
79 | } |
80 | #endif | |
81 | } | |
82 | ||
83 | static unsigned int | |
84 | get_page_size(void) | |
85 | { | |
86 | static unsigned int cached; | |
87 | ||
88 | if (!cached) { | |
6c04f67c | 89 | #ifndef _WIN32 |
ce887677 | 90 | long int value = sysconf(_SC_PAGESIZE); |
6c04f67c GS |
91 | #else |
92 | long int value; | |
93 | SYSTEM_INFO sysinfo; | |
94 | GetSystemInfo(&sysinfo); | |
95 | value = sysinfo.dwPageSize; | |
96 | #endif | |
ce887677 BP |
97 | if (value >= 0) { |
98 | cached = value; | |
99 | } | |
100 | } | |
101 | ||
102 | return cached; | |
103 | } | |
104 | ||
105 | static void | |
57c8677b | 106 | get_memory_stats(struct smap *stats) |
ce887677 | 107 | { |
2f51a7eb | 108 | if (!LINUX) { |
ce887677 | 109 | unsigned int pagesize = get_page_size(); |
a0e21f58 | 110 | #ifdef _SC_PHYS_PAGES |
ce887677 | 111 | long int phys_pages = sysconf(_SC_PHYS_PAGES); |
a0e21f58 YT |
112 | #else |
113 | long int phys_pages = 0; | |
114 | #endif | |
6ca00f6f | 115 | #ifdef _SC_AVPHYS_PAGES |
ce887677 | 116 | long int avphys_pages = sysconf(_SC_AVPHYS_PAGES); |
6ca00f6f ETN |
117 | #else |
118 | long int avphys_pages = 0; | |
119 | #endif | |
ce887677 BP |
120 | int mem_total, mem_used; |
121 | ||
6c04f67c | 122 | #ifndef _WIN32 |
ce887677 BP |
123 | if (pagesize <= 0 || phys_pages <= 0 || avphys_pages <= 0) { |
124 | return; | |
125 | } | |
126 | ||
127 | mem_total = phys_pages * (pagesize / 1024); | |
128 | mem_used = (phys_pages - avphys_pages) * (pagesize / 1024); | |
6c04f67c GS |
129 | #else |
130 | MEMORYSTATUS memory_status; | |
131 | GlobalMemoryStatus(&memory_status); | |
132 | ||
133 | mem_total = memory_status.dwTotalPhys; | |
134 | mem_used = memory_status.dwTotalPhys - memory_status.dwAvailPhys; | |
135 | #endif | |
57c8677b | 136 | smap_add_format(stats, "memory", "%d,%d", mem_total, mem_used); |
ce887677 BP |
137 | } else { |
138 | static const char file_name[] = "/proc/meminfo"; | |
139 | int mem_used, mem_cache, swap_used; | |
140 | int mem_free = 0; | |
141 | int buffers = 0; | |
142 | int cached = 0; | |
143 | int swap_free = 0; | |
144 | int mem_total = 0; | |
145 | int swap_total = 0; | |
146 | struct shash dict; | |
147 | char line[128]; | |
148 | FILE *stream; | |
149 | ||
150 | stream = fopen(file_name, "r"); | |
151 | if (!stream) { | |
10a89ef0 BP |
152 | VLOG_WARN_ONCE("%s: open failed (%s)", |
153 | file_name, ovs_strerror(errno)); | |
ce887677 BP |
154 | return; |
155 | } | |
156 | ||
157 | shash_init(&dict); | |
158 | shash_add(&dict, "MemTotal", &mem_total); | |
159 | shash_add(&dict, "MemFree", &mem_free); | |
160 | shash_add(&dict, "Buffers", &buffers); | |
161 | shash_add(&dict, "Cached", &cached); | |
162 | shash_add(&dict, "SwapTotal", &swap_total); | |
163 | shash_add(&dict, "SwapFree", &swap_free); | |
164 | while (fgets(line, sizeof line, stream)) { | |
165 | char key[16]; | |
166 | int value; | |
167 | ||
c2c28dfd | 168 | if (ovs_scan(line, "%15[^:]: %u", key, &value)) { |
ce887677 BP |
169 | int *valuep = shash_find_data(&dict, key); |
170 | if (valuep) { | |
171 | *valuep = value; | |
172 | } | |
173 | } | |
174 | } | |
175 | fclose(stream); | |
176 | shash_destroy(&dict); | |
177 | ||
178 | mem_used = mem_total - mem_free; | |
179 | mem_cache = buffers + cached; | |
180 | swap_used = swap_total - swap_free; | |
57c8677b BP |
181 | smap_add_format(stats, "memory", "%d,%d,%d,%d,%d", |
182 | mem_total, mem_used, mem_cache, swap_total, swap_used); | |
ce887677 BP |
183 | } |
184 | } | |
185 | ||
186 | /* Returns the time at which the system booted, as the number of milliseconds | |
187 | * since the epoch, or 0 if the time of boot cannot be determined. */ | |
188 | static long long int | |
189 | get_boot_time(void) | |
190 | { | |
191 | static long long int cache_expiration = LLONG_MIN; | |
192 | static long long int boot_time; | |
193 | ||
2f51a7eb | 194 | ovs_assert(LINUX); |
ce887677 BP |
195 | |
196 | if (time_msec() >= cache_expiration) { | |
197 | static const char stat_file[] = "/proc/stat"; | |
198 | char line[128]; | |
199 | FILE *stream; | |
200 | ||
201 | cache_expiration = time_msec() + 5 * 1000; | |
202 | ||
203 | stream = fopen(stat_file, "r"); | |
204 | if (!stream) { | |
10a89ef0 BP |
205 | VLOG_ERR_ONCE("%s: open failed (%s)", |
206 | stat_file, ovs_strerror(errno)); | |
ce887677 BP |
207 | return boot_time; |
208 | } | |
209 | ||
210 | while (fgets(line, sizeof line, stream)) { | |
211 | long long int btime; | |
c2c28dfd | 212 | if (ovs_scan(line, "btime %lld", &btime)) { |
ce887677 BP |
213 | boot_time = btime * 1000; |
214 | goto done; | |
215 | } | |
216 | } | |
217 | VLOG_ERR_ONCE("%s: btime not found", stat_file); | |
218 | done: | |
219 | fclose(stream); | |
220 | } | |
221 | return boot_time; | |
222 | } | |
223 | ||
224 | static unsigned long long int | |
225 | ticks_to_ms(unsigned long long int ticks) | |
226 | { | |
2f51a7eb | 227 | ovs_assert(LINUX); |
ce887677 BP |
228 | |
229 | #ifndef USER_HZ | |
230 | #define USER_HZ 100 | |
231 | #endif | |
232 | ||
233 | #if USER_HZ == 100 /* Common case. */ | |
234 | return ticks * (1000 / USER_HZ); | |
235 | #else /* Alpha and some other architectures. */ | |
236 | double factor = 1000.0 / USER_HZ; | |
237 | return ticks * factor + 0.5; | |
238 | #endif | |
239 | } | |
240 | ||
241 | struct raw_process_info { | |
242 | unsigned long int vsz; /* Virtual size, in kB. */ | |
243 | unsigned long int rss; /* Resident set size, in kB. */ | |
244 | long long int uptime; /* ms since started. */ | |
245 | long long int cputime; /* ms of CPU used during 'uptime'. */ | |
246 | pid_t ppid; /* Parent. */ | |
247 | char name[18]; /* Name (surrounded by parentheses). */ | |
248 | }; | |
249 | ||
250 | static bool | |
251 | get_raw_process_info(pid_t pid, struct raw_process_info *raw) | |
252 | { | |
253 | unsigned long long int vsize, rss, start_time, utime, stime; | |
254 | long long int start_msec; | |
255 | unsigned long ppid; | |
256 | char file_name[128]; | |
257 | FILE *stream; | |
258 | int n; | |
259 | ||
2f51a7eb | 260 | ovs_assert(LINUX); |
ce887677 BP |
261 | |
262 | sprintf(file_name, "/proc/%lu/stat", (unsigned long int) pid); | |
263 | stream = fopen(file_name, "r"); | |
264 | if (!stream) { | |
10a89ef0 BP |
265 | VLOG_ERR_ONCE("%s: open failed (%s)", |
266 | file_name, ovs_strerror(errno)); | |
ce887677 BP |
267 | return false; |
268 | } | |
269 | ||
270 | n = fscanf(stream, | |
271 | "%*d " /* (1. pid) */ | |
272 | "%17s " /* 2. process name */ | |
273 | "%*c " /* (3. state) */ | |
274 | "%lu " /* 4. ppid */ | |
275 | "%*d " /* (5. pgid) */ | |
276 | "%*d " /* (6. sid) */ | |
277 | "%*d " /* (7. tty_nr) */ | |
278 | "%*d " /* (8. tty_pgrp) */ | |
279 | "%*u " /* (9. flags) */ | |
280 | "%*u " /* (10. min_flt) */ | |
281 | "%*u " /* (11. cmin_flt) */ | |
282 | "%*u " /* (12. maj_flt) */ | |
283 | "%*u " /* (13. cmaj_flt) */ | |
284 | "%llu " /* 14. utime */ | |
285 | "%llu " /* 15. stime */ | |
286 | "%*d " /* (16. cutime) */ | |
287 | "%*d " /* (17. cstime) */ | |
288 | "%*d " /* (18. priority) */ | |
289 | "%*d " /* (19. nice) */ | |
290 | "%*d " /* (20. num_threads) */ | |
291 | "%*d " /* (21. always 0) */ | |
292 | "%llu " /* 22. start_time */ | |
293 | "%llu " /* 23. vsize */ | |
294 | "%llu " /* 24. rss */ | |
295 | #if 0 | |
296 | /* These are here for documentation but #if'd out to save | |
297 | * actually parsing them from the stream for no benefit. */ | |
298 | "%*lu " /* (25. rsslim) */ | |
299 | "%*lu " /* (26. start_code) */ | |
300 | "%*lu " /* (27. end_code) */ | |
301 | "%*lu " /* (28. start_stack) */ | |
302 | "%*lu " /* (29. esp) */ | |
303 | "%*lu " /* (30. eip) */ | |
304 | "%*lu " /* (31. pending signals) */ | |
305 | "%*lu " /* (32. blocked signals) */ | |
306 | "%*lu " /* (33. ignored signals) */ | |
307 | "%*lu " /* (34. caught signals) */ | |
308 | "%*lu " /* (35. whcan) */ | |
309 | "%*lu " /* (36. always 0) */ | |
310 | "%*lu " /* (37. always 0) */ | |
311 | "%*d " /* (38. exit_signal) */ | |
312 | "%*d " /* (39. task_cpu) */ | |
313 | "%*u " /* (40. rt_priority) */ | |
314 | "%*u " /* (41. policy) */ | |
315 | "%*llu " /* (42. blkio_ticks) */ | |
316 | "%*lu " /* (43. gtime) */ | |
317 | "%*ld" /* (44. cgtime) */ | |
318 | #endif | |
319 | , raw->name, &ppid, &utime, &stime, &start_time, &vsize, &rss); | |
320 | fclose(stream); | |
321 | if (n != 7) { | |
322 | VLOG_ERR_ONCE("%s: fscanf failed", file_name); | |
323 | return false; | |
324 | } | |
325 | ||
326 | start_msec = get_boot_time() + ticks_to_ms(start_time); | |
327 | ||
328 | raw->vsz = vsize / 1024; | |
329 | raw->rss = rss * (getpagesize() / 1024); | |
330 | raw->uptime = time_wall_msec() - start_msec; | |
331 | raw->cputime = ticks_to_ms(utime + stime); | |
332 | raw->ppid = ppid; | |
333 | ||
334 | return true; | |
335 | } | |
336 | ||
337 | static int | |
338 | count_crashes(pid_t pid) | |
339 | { | |
340 | char file_name[128]; | |
341 | const char *paren; | |
342 | char line[128]; | |
343 | int crashes = 0; | |
344 | FILE *stream; | |
345 | ||
2f51a7eb | 346 | ovs_assert(LINUX); |
ce887677 BP |
347 | |
348 | sprintf(file_name, "/proc/%lu/cmdline", (unsigned long int) pid); | |
349 | stream = fopen(file_name, "r"); | |
350 | if (!stream) { | |
10a89ef0 | 351 | VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno)); |
ce887677 BP |
352 | goto exit; |
353 | } | |
354 | ||
355 | if (!fgets(line, sizeof line, stream)) { | |
356 | VLOG_WARN_ONCE("%s: read failed (%s)", file_name, | |
10a89ef0 | 357 | feof(stream) ? "end of file" : ovs_strerror(errno)); |
ce887677 BP |
358 | goto exit_close; |
359 | } | |
360 | ||
361 | paren = strchr(line, '('); | |
362 | if (paren) { | |
363 | int x; | |
c2c28dfd | 364 | if (ovs_scan(paren + 1, "%d", &x)) { |
ce887677 BP |
365 | crashes = x; |
366 | } | |
367 | } | |
368 | ||
369 | exit_close: | |
370 | fclose(stream); | |
371 | exit: | |
372 | return crashes; | |
373 | } | |
374 | ||
375 | struct process_info { | |
376 | unsigned long int vsz; /* Virtual size, in kB. */ | |
377 | unsigned long int rss; /* Resident set size, in kB. */ | |
378 | long long int booted; /* ms since monitor started. */ | |
379 | int crashes; /* # of crashes (usually 0). */ | |
380 | long long int uptime; /* ms since last (re)started by monitor. */ | |
381 | long long int cputime; /* ms of CPU used during 'uptime'. */ | |
382 | }; | |
383 | ||
384 | static bool | |
385 | get_process_info(pid_t pid, struct process_info *pinfo) | |
386 | { | |
387 | struct raw_process_info child; | |
388 | ||
2f51a7eb | 389 | ovs_assert(LINUX); |
ce887677 BP |
390 | if (!get_raw_process_info(pid, &child)) { |
391 | return false; | |
392 | } | |
393 | ||
394 | pinfo->vsz = child.vsz; | |
395 | pinfo->rss = child.rss; | |
396 | pinfo->booted = child.uptime; | |
397 | pinfo->crashes = 0; | |
398 | pinfo->uptime = child.uptime; | |
399 | pinfo->cputime = child.cputime; | |
400 | ||
401 | if (child.ppid) { | |
402 | struct raw_process_info parent; | |
403 | ||
404 | get_raw_process_info(child.ppid, &parent); | |
405 | if (!strcmp(child.name, parent.name)) { | |
406 | pinfo->booted = parent.uptime; | |
407 | pinfo->crashes = count_crashes(child.ppid); | |
408 | } | |
409 | } | |
410 | ||
411 | return true; | |
412 | } | |
413 | ||
414 | static void | |
57c8677b | 415 | get_process_stats(struct smap *stats) |
ce887677 | 416 | { |
6c04f67c | 417 | #ifndef _WIN32 |
ce887677 BP |
418 | struct dirent *de; |
419 | DIR *dir; | |
420 | ||
b43c6fe2 | 421 | dir = opendir(ovs_rundir()); |
ce887677 | 422 | if (!dir) { |
10a89ef0 BP |
423 | VLOG_ERR_ONCE("%s: open failed (%s)", |
424 | ovs_rundir(), ovs_strerror(errno)); | |
ce887677 BP |
425 | return; |
426 | } | |
427 | ||
428 | while ((de = readdir(dir)) != NULL) { | |
429 | struct process_info pinfo; | |
ce887677 BP |
430 | char *file_name; |
431 | char *extension; | |
57c8677b | 432 | char *key; |
ce887677 BP |
433 | pid_t pid; |
434 | ||
435 | #ifdef _DIRENT_HAVE_D_TYPE | |
436 | if (de->d_type != DT_UNKNOWN && de->d_type != DT_REG) { | |
437 | continue; | |
438 | } | |
439 | #endif | |
440 | ||
441 | extension = strrchr(de->d_name, '.'); | |
442 | if (!extension || strcmp(extension, ".pid")) { | |
443 | continue; | |
444 | } | |
445 | ||
b43c6fe2 | 446 | file_name = xasprintf("%s/%s", ovs_rundir(), de->d_name); |
ce887677 BP |
447 | pid = read_pidfile(file_name); |
448 | free(file_name); | |
8da7ca87 | 449 | if (pid < 0) { |
ce887677 BP |
450 | continue; |
451 | } | |
452 | ||
453 | key = xasprintf("process_%.*s", | |
454 | (int) (extension - de->d_name), de->d_name); | |
57c8677b | 455 | if (!smap_get(stats, key)) { |
2f51a7eb | 456 | if (LINUX && get_process_info(pid, &pinfo)) { |
57c8677b BP |
457 | smap_add_format(stats, key, "%lu,%lu,%lld,%d,%lld,%lld", |
458 | pinfo.vsz, pinfo.rss, pinfo.cputime, | |
459 | pinfo.crashes, pinfo.booted, pinfo.uptime); | |
460 | } else { | |
461 | smap_add(stats, key, ""); | |
462 | } | |
ce887677 | 463 | } |
57c8677b | 464 | free(key); |
ce887677 BP |
465 | } |
466 | ||
467 | closedir(dir); | |
6c04f67c | 468 | #endif /* _WIN32 */ |
ce887677 BP |
469 | } |
470 | ||
471 | static void | |
57c8677b | 472 | get_filesys_stats(struct smap *stats OVS_UNUSED) |
ce887677 | 473 | { |
ffb1cfd1 | 474 | #if HAVE_GETMNTENT_R && HAVE_STATVFS |
ce887677 | 475 | static const char file_name[] = "/etc/mtab"; |
ffb1cfd1 | 476 | struct mntent mntent; |
ce887677 | 477 | struct mntent *me; |
ffb1cfd1 | 478 | char buf[4096]; |
ce887677 BP |
479 | FILE *stream; |
480 | struct ds s; | |
481 | ||
482 | stream = setmntent(file_name, "r"); | |
483 | if (!stream) { | |
10a89ef0 | 484 | VLOG_ERR_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno)); |
ce887677 BP |
485 | return; |
486 | } | |
487 | ||
488 | ds_init(&s); | |
ffb1cfd1 | 489 | while ((me = getmntent_r(stream, &mntent, buf, sizeof buf)) != NULL) { |
ce887677 BP |
490 | unsigned long long int total, free; |
491 | struct statvfs vfs; | |
492 | char *p; | |
493 | ||
494 | /* Skip non-local and read-only filesystems. */ | |
495 | if (strncmp(me->mnt_fsname, "/dev", 4) | |
496 | || !strstr(me->mnt_opts, "rw")) { | |
497 | continue; | |
498 | } | |
499 | ||
500 | /* Given the mount point we can stat the file system. */ | |
501 | if (statvfs(me->mnt_dir, &vfs) && vfs.f_flag & ST_RDONLY) { | |
502 | /* That's odd... */ | |
503 | continue; | |
504 | } | |
505 | ||
506 | /* Now format the data. */ | |
507 | if (s.length) { | |
508 | ds_put_char(&s, ' '); | |
509 | } | |
510 | for (p = me->mnt_dir; *p != '\0'; p++) { | |
511 | ds_put_char(&s, *p == ' ' || *p == ',' ? '_' : *p); | |
512 | } | |
513 | total = (unsigned long long int) vfs.f_frsize * vfs.f_blocks / 1024; | |
514 | free = (unsigned long long int) vfs.f_frsize * vfs.f_bfree / 1024; | |
515 | ds_put_format(&s, ",%llu,%llu", total, total - free); | |
516 | } | |
517 | endmntent(stream); | |
518 | ||
519 | if (s.length) { | |
57c8677b | 520 | smap_add(stats, "file_systems", ds_cstr(&s)); |
ce887677 BP |
521 | } |
522 | ds_destroy(&s); | |
ffb1cfd1 | 523 | #endif /* HAVE_GETMNTENT_R && HAVE_STATVFS */ |
ce887677 | 524 | } |
35a22d8c BP |
525 | \f |
526 | #define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */ | |
ce887677 | 527 | |
834d6caf | 528 | static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; |
59987a62 | 529 | static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; |
97be1538 | 530 | static struct latch latch OVS_GUARDED_BY(mutex); |
59987a62 | 531 | static bool enabled; |
97be1538 EJ |
532 | static bool started OVS_GUARDED_BY(mutex); |
533 | static struct smap *system_stats OVS_GUARDED_BY(mutex); | |
59987a62 | 534 | |
cab50449 | 535 | OVS_NO_RETURN static void *system_stats_thread_func(void *); |
59987a62 | 536 | static void discard_stats(void); |
35a22d8c | 537 | |
5a13f2f4 | 538 | /* Enables or disables system stats collection, according to 'enable'. */ |
ce887677 | 539 | void |
5a13f2f4 | 540 | system_stats_enable(bool enable) |
35a22d8c | 541 | { |
59987a62 | 542 | if (enabled != enable) { |
97be1538 | 543 | ovs_mutex_lock(&mutex); |
59987a62 BP |
544 | if (enable) { |
545 | if (!started) { | |
8ba0a522 BP |
546 | ovs_thread_create("system_stats", |
547 | system_stats_thread_func, NULL); | |
59987a62 BP |
548 | latch_init(&latch); |
549 | started = true; | |
550 | } | |
551 | discard_stats(); | |
552 | xpthread_cond_signal(&cond); | |
553 | } | |
554 | enabled = enable; | |
97be1538 | 555 | ovs_mutex_unlock(&mutex); |
35a22d8c BP |
556 | } |
557 | } | |
558 | ||
559 | /* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL | |
560 | * milliseconds. | |
561 | * | |
562 | * When a new snapshot is available (which only occurs if system stats are | |
563 | * enabled), returns it as an smap owned by the caller. The caller must use | |
5a13f2f4 | 564 | * both smap_destroy() and free() to completely free the returned data. |
35a22d8c BP |
565 | * |
566 | * When no new snapshot is available, returns NULL. */ | |
567 | struct smap * | |
568 | system_stats_run(void) | |
ce887677 | 569 | { |
59987a62 | 570 | struct smap *stats = NULL; |
5a13f2f4 | 571 | |
97be1538 | 572 | ovs_mutex_lock(&mutex); |
59987a62 BP |
573 | if (system_stats) { |
574 | latch_poll(&latch); | |
5a13f2f4 | 575 | |
59987a62 BP |
576 | if (enabled) { |
577 | stats = system_stats; | |
578 | system_stats = NULL; | |
579 | } else { | |
580 | discard_stats(); | |
581 | } | |
35a22d8c | 582 | } |
97be1538 | 583 | ovs_mutex_unlock(&mutex); |
35a22d8c | 584 | |
59987a62 | 585 | return stats; |
35a22d8c BP |
586 | } |
587 | ||
588 | /* Causes poll_block() to wake up when system_stats_run() needs to be | |
589 | * called. */ | |
590 | void | |
591 | system_stats_wait(void) | |
592 | { | |
59987a62 BP |
593 | if (enabled) { |
594 | latch_wait(&latch); | |
595 | } | |
596 | } | |
597 | ||
598 | static void | |
344e21d4 | 599 | discard_stats(void) OVS_REQUIRES(mutex) |
59987a62 BP |
600 | { |
601 | if (system_stats) { | |
602 | smap_destroy(system_stats); | |
603 | free(system_stats); | |
604 | system_stats = NULL; | |
605 | } | |
606 | } | |
607 | ||
270f3286 | 608 | static void * |
59987a62 BP |
609 | system_stats_thread_func(void *arg OVS_UNUSED) |
610 | { | |
611 | pthread_detach(pthread_self()); | |
612 | ||
613 | for (;;) { | |
614 | long long int next_refresh; | |
615 | struct smap *stats; | |
616 | ||
97be1538 | 617 | ovs_mutex_lock(&mutex); |
59987a62 | 618 | while (!enabled) { |
5724fca4 DDP |
619 | /* The thread is sleeping, potentially for a long time, and it's |
620 | * not holding RCU protected references, so it makes sense to | |
621 | * quiesce */ | |
622 | ovsrcu_quiesce_start(); | |
97be1538 | 623 | ovs_mutex_cond_wait(&cond, &mutex); |
5724fca4 | 624 | ovsrcu_quiesce_end(); |
59987a62 | 625 | } |
97be1538 | 626 | ovs_mutex_unlock(&mutex); |
59987a62 BP |
627 | |
628 | stats = xmalloc(sizeof *stats); | |
629 | smap_init(stats); | |
630 | get_cpu_cores(stats); | |
631 | get_load_average(stats); | |
632 | get_memory_stats(stats); | |
633 | get_process_stats(stats); | |
634 | get_filesys_stats(stats); | |
635 | ||
97be1538 | 636 | ovs_mutex_lock(&mutex); |
59987a62 BP |
637 | discard_stats(); |
638 | system_stats = stats; | |
639 | latch_set(&latch); | |
97be1538 | 640 | ovs_mutex_unlock(&mutex); |
59987a62 BP |
641 | |
642 | next_refresh = time_msec() + SYSTEM_STATS_INTERVAL; | |
643 | do { | |
644 | poll_timer_wait_until(next_refresh); | |
645 | poll_block(); | |
646 | } while (time_msec() < next_refresh); | |
35a22d8c BP |
647 | } |
648 | } |