]> git.proxmox.com Git - mirror_ovs.git/blob - vswitchd/system-stats.c
tunnel: Add layer 2 IPv6 GRE encapsulation support.
[mirror_ovs.git] / vswitchd / system-stats.c
1 /* Copyright (c) 2010, 2012, 2013, 2014 Nicira, Inc.
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
18 #include "system-stats.h"
19
20 #include <ctype.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #if HAVE_MNTENT_H
24 #include <mntent.h>
25 #endif
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"
36 #include "openvswitch/dynamic-string.h"
37 #include "openvswitch/json.h"
38 #include "latch.h"
39 #include "openvswitch/ofpbuf.h"
40 #include "ovs-rcu.h"
41 #include "ovs-thread.h"
42 #include "openvswitch/poll-loop.h"
43 #include "openvswitch/shash.h"
44 #include "process.h"
45 #include "smap.h"
46 #include "timeval.h"
47 #include "openvswitch/vlog.h"
48
49 VLOG_DEFINE_THIS_MODULE(system_stats);
50
51 /* #ifdefs make it a pain to maintain code: you have to try to build both ways.
52 * Thus, this file tries to compile as much of the code as possible regardless
53 * of the target, by writing "if (LINUX)" instead of "#ifdef __linux__" where
54 * this is possible. */
55 #ifdef __linux__
56 #define LINUX 1
57 #include <asm/param.h>
58 #else
59 #define LINUX 0
60 #endif
61
62 static void
63 get_cpu_cores(struct smap *stats)
64 {
65 long int n_cores = count_cpu_cores();
66 if (n_cores > 0) {
67 smap_add_format(stats, "cpu", "%ld", n_cores);
68 }
69 }
70
71 static void
72 get_load_average(struct smap *stats OVS_UNUSED)
73 {
74 #if HAVE_GETLOADAVG
75 double loadavg[3];
76
77 if (getloadavg(loadavg, 3) == 3) {
78 smap_add_format(stats, "load_average", "%.2f,%.2f,%.2f",
79 loadavg[0], loadavg[1], loadavg[2]);
80 }
81 #endif
82 }
83
84 static void
85 get_memory_stats(struct smap *stats)
86 {
87 if (!LINUX) {
88 unsigned int pagesize = get_page_size();
89 #ifdef _SC_PHYS_PAGES
90 long int phys_pages = sysconf(_SC_PHYS_PAGES);
91 #else
92 long int phys_pages = 0;
93 #endif
94 #ifdef _SC_AVPHYS_PAGES
95 long int avphys_pages = sysconf(_SC_AVPHYS_PAGES);
96 #else
97 long int avphys_pages = 0;
98 #endif
99 int mem_total, mem_used;
100
101 #ifndef _WIN32
102 if (pagesize <= 0 || phys_pages <= 0 || avphys_pages <= 0) {
103 return;
104 }
105
106 mem_total = phys_pages * (pagesize / 1024);
107 mem_used = (phys_pages - avphys_pages) * (pagesize / 1024);
108 #else
109 MEMORYSTATUS memory_status;
110 GlobalMemoryStatus(&memory_status);
111
112 mem_total = memory_status.dwTotalPhys;
113 mem_used = memory_status.dwTotalPhys - memory_status.dwAvailPhys;
114 #endif
115 smap_add_format(stats, "memory", "%d,%d", mem_total, mem_used);
116 } else {
117 static const char file_name[] = "/proc/meminfo";
118 int mem_used, mem_cache, swap_used;
119 int mem_free = 0;
120 int buffers = 0;
121 int cached = 0;
122 int swap_free = 0;
123 int mem_total = 0;
124 int swap_total = 0;
125 struct shash dict;
126 char line[128];
127 FILE *stream;
128
129 stream = fopen(file_name, "r");
130 if (!stream) {
131 VLOG_WARN_ONCE("%s: open failed (%s)",
132 file_name, ovs_strerror(errno));
133 return;
134 }
135
136 shash_init(&dict);
137 shash_add(&dict, "MemTotal", &mem_total);
138 shash_add(&dict, "MemFree", &mem_free);
139 shash_add(&dict, "Buffers", &buffers);
140 shash_add(&dict, "Cached", &cached);
141 shash_add(&dict, "SwapTotal", &swap_total);
142 shash_add(&dict, "SwapFree", &swap_free);
143 while (fgets(line, sizeof line, stream)) {
144 char key[16];
145 int value;
146
147 if (ovs_scan(line, "%15[^:]: %u", key, &value)) {
148 int *valuep = shash_find_data(&dict, key);
149 if (valuep) {
150 *valuep = value;
151 }
152 }
153 }
154 fclose(stream);
155 shash_destroy(&dict);
156
157 mem_used = mem_total - mem_free;
158 mem_cache = buffers + cached;
159 swap_used = swap_total - swap_free;
160 smap_add_format(stats, "memory", "%d,%d,%d,%d,%d",
161 mem_total, mem_used, mem_cache, swap_total, swap_used);
162 }
163 }
164
165 static void
166 get_process_stats(struct smap *stats)
167 {
168 #ifndef _WIN32
169 struct dirent *de;
170 DIR *dir;
171
172 dir = opendir(ovs_rundir());
173 if (!dir) {
174 VLOG_ERR_ONCE("%s: open failed (%s)",
175 ovs_rundir(), ovs_strerror(errno));
176 return;
177 }
178
179 while ((de = readdir(dir)) != NULL) {
180 struct process_info pinfo;
181 char *file_name;
182 char *extension;
183 char *key;
184 pid_t pid;
185
186 #ifdef _DIRENT_HAVE_D_TYPE
187 if (de->d_type != DT_UNKNOWN && de->d_type != DT_REG) {
188 continue;
189 }
190 #endif
191
192 extension = strrchr(de->d_name, '.');
193 if (!extension || strcmp(extension, ".pid")) {
194 continue;
195 }
196
197 file_name = xasprintf("%s/%s", ovs_rundir(), de->d_name);
198 pid = read_pidfile(file_name);
199 free(file_name);
200 if (pid < 0) {
201 continue;
202 }
203
204 key = xasprintf("process_%.*s",
205 (int) (extension - de->d_name), de->d_name);
206 if (!smap_get(stats, key)) {
207 if (LINUX && get_process_info(pid, &pinfo)) {
208 smap_add_format(stats, key, "%lu,%lu,%lld,%d,%lld,%lld,%d",
209 pinfo.vsz, pinfo.rss, pinfo.cputime,
210 pinfo.crashes, pinfo.booted, pinfo.uptime,
211 pinfo.core_id);
212 } else {
213 smap_add(stats, key, "");
214 }
215 }
216 free(key);
217 }
218
219 closedir(dir);
220 #endif /* _WIN32 */
221 }
222
223 static void
224 get_filesys_stats(struct smap *stats OVS_UNUSED)
225 {
226 #if HAVE_GETMNTENT_R && HAVE_STATVFS
227 static const char file_name[] = "/etc/mtab";
228 struct mntent mntent;
229 struct mntent *me;
230 char buf[4096];
231 FILE *stream;
232 struct ds s;
233
234 stream = setmntent(file_name, "r");
235 if (!stream) {
236 VLOG_ERR_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
237 return;
238 }
239
240 ds_init(&s);
241 while ((me = getmntent_r(stream, &mntent, buf, sizeof buf)) != NULL) {
242 unsigned long long int total, free;
243 struct statvfs vfs;
244 char *p;
245
246 /* Skip non-local and read-only filesystems. */
247 if (strncmp(me->mnt_fsname, "/dev", 4)
248 || !strstr(me->mnt_opts, "rw")) {
249 continue;
250 }
251
252 /* Given the mount point we can stat the file system. */
253 if (statvfs(me->mnt_dir, &vfs) && vfs.f_flag & ST_RDONLY) {
254 /* That's odd... */
255 continue;
256 }
257
258 /* Now format the data. */
259 if (s.length) {
260 ds_put_char(&s, ' ');
261 }
262 for (p = me->mnt_dir; *p != '\0'; p++) {
263 ds_put_char(&s, *p == ' ' || *p == ',' ? '_' : *p);
264 }
265 total = (unsigned long long int) vfs.f_frsize * vfs.f_blocks / 1024;
266 free = (unsigned long long int) vfs.f_frsize * vfs.f_bfree / 1024;
267 ds_put_format(&s, ",%llu,%llu", total, total - free);
268 }
269 endmntent(stream);
270
271 if (s.length) {
272 smap_add(stats, "file_systems", ds_cstr(&s));
273 }
274 ds_destroy(&s);
275 #endif /* HAVE_GETMNTENT_R && HAVE_STATVFS */
276 }
277 \f
278 #define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
279
280 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
281 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
282 static struct latch latch OVS_GUARDED_BY(mutex);
283 static bool enabled;
284 static bool started OVS_GUARDED_BY(mutex);
285 static struct smap *system_stats OVS_GUARDED_BY(mutex);
286
287 OVS_NO_RETURN static void *system_stats_thread_func(void *);
288 static void discard_stats(void);
289
290 /* Enables or disables system stats collection, according to 'enable'. */
291 void
292 system_stats_enable(bool enable)
293 {
294 if (enabled != enable) {
295 ovs_mutex_lock(&mutex);
296 if (enable) {
297 if (!started) {
298 ovs_thread_create("system_stats",
299 system_stats_thread_func, NULL);
300 latch_init(&latch);
301 started = true;
302 }
303 discard_stats();
304 xpthread_cond_signal(&cond);
305 }
306 enabled = enable;
307 ovs_mutex_unlock(&mutex);
308 }
309 }
310
311 /* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL
312 * milliseconds.
313 *
314 * When a new snapshot is available (which only occurs if system stats are
315 * enabled), returns it as an smap owned by the caller. The caller must use
316 * both smap_destroy() and free() to completely free the returned data.
317 *
318 * When no new snapshot is available, returns NULL. */
319 struct smap *
320 system_stats_run(void)
321 {
322 struct smap *stats = NULL;
323
324 ovs_mutex_lock(&mutex);
325 if (system_stats) {
326 latch_poll(&latch);
327
328 if (enabled) {
329 stats = system_stats;
330 system_stats = NULL;
331 } else {
332 discard_stats();
333 }
334 }
335 ovs_mutex_unlock(&mutex);
336
337 return stats;
338 }
339
340 /* Causes poll_block() to wake up when system_stats_run() needs to be
341 * called. */
342 void
343 system_stats_wait(void)
344 {
345 if (enabled) {
346 latch_wait(&latch);
347 }
348 }
349
350 static void
351 discard_stats(void) OVS_REQUIRES(mutex)
352 {
353 if (system_stats) {
354 smap_destroy(system_stats);
355 free(system_stats);
356 system_stats = NULL;
357 }
358 }
359
360 static void *
361 system_stats_thread_func(void *arg OVS_UNUSED)
362 {
363 pthread_detach(pthread_self());
364
365 for (;;) {
366 long long int next_refresh;
367 struct smap *stats;
368
369 ovs_mutex_lock(&mutex);
370 while (!enabled) {
371 /* The thread is sleeping, potentially for a long time, and it's
372 * not holding RCU protected references, so it makes sense to
373 * quiesce */
374 ovsrcu_quiesce_start();
375 ovs_mutex_cond_wait(&cond, &mutex);
376 ovsrcu_quiesce_end();
377 }
378 ovs_mutex_unlock(&mutex);
379
380 stats = xmalloc(sizeof *stats);
381 smap_init(stats);
382 get_cpu_cores(stats);
383 get_load_average(stats);
384 get_memory_stats(stats);
385 get_process_stats(stats);
386 get_filesys_stats(stats);
387
388 ovs_mutex_lock(&mutex);
389 discard_stats();
390 system_stats = stats;
391 latch_set(&latch);
392 ovs_mutex_unlock(&mutex);
393
394 next_refresh = time_msec() + SYSTEM_STATS_INTERVAL;
395 do {
396 poll_timer_wait_until(next_refresh);
397 poll_block();
398 } while (time_msec() < next_refresh);
399 }
400 }