1 /* libxenstat: statistics-collection library for Xen
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 */
13
14 /*
15 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
16 * Use is subject to license terms.
17 */
18
19 #include <strings.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #include <kstat.h>
25
26 #include "xenstat_priv.h"
27
28 #define DEVICE_NIC 1
29 #define DEVICE_XDB 2
30
31 typedef struct stdevice {
32 int domid;
33 int used;
34 int type;
35 char name[256];
36 int instance;
37 uint64_t stats[2][8];
38 struct stdevice *next;
39 } stdevice_t;
40
41 typedef struct priv_data {
42 kstat_ctl_t *kc;
43 stdevice_t *devs;
44 } priv_data_t;
45
get_priv_data(xenstat_handle * handle)46 static priv_data_t *get_priv_data(xenstat_handle *handle)
47 {
48 priv_data_t *priv = handle->priv;
49
50 if (priv == NULL) {
51 priv = malloc(sizeof (priv_data_t));
52 if (priv == NULL)
53 return NULL;
54 priv->devs = NULL;
55 priv->kc = NULL;
56 }
57
58 if (priv->kc == NULL) {
59 if ((priv->kc = kstat_open()) == NULL) {
60 free(priv);
61 return NULL;
62 }
63 }
64
65 handle->priv = priv;
66 return handle->priv;
67 }
68
kstat_get(kstat_t * ksp,const char * name,uint64_t * val)69 static int kstat_get(kstat_t *ksp, const char *name, uint64_t *val)
70 {
71 kstat_named_t *ksn = kstat_data_lookup(ksp, (char *)name);
72 if (ksn == NULL)
73 return 0;
74 *val = ksn->value.ui64;
75 return 1;
76 }
77
gc_devs(priv_data_t * priv,int type)78 static void gc_devs(priv_data_t *priv, int type)
79 {
80 stdevice_t *start = NULL;
81 stdevice_t *dev;
82 stdevice_t *tmp;
83
84 for (dev = priv->devs; dev != NULL; dev = tmp) {
85 tmp = dev->next;
86
87 if (dev->used || dev->type != type) {
88 dev->next = start;
89 start = dev;
90 } else {
91 free(dev);
92 }
93 }
94
95 priv->devs = start;
96 }
97
xenstat_uninit_devs(xenstat_handle * handle,int type)98 static void xenstat_uninit_devs(xenstat_handle *handle, int type)
99 {
100 priv_data_t *priv = get_priv_data(handle);
101 stdevice_t *dev;
102
103 if (priv == NULL)
104 return;
105
106 for (dev = priv->devs; dev != NULL; dev = dev->next)
107 dev->used = 0;
108
109 gc_devs(priv, type);
110
111 if (priv->kc != NULL)
112 kstat_close(priv->kc);
113 priv->kc = NULL;
114 }
115
update_dev_stats(priv_data_t * priv,stdevice_t * dev)116 static int update_dev_stats(priv_data_t *priv, stdevice_t *dev)
117 {
118 kstat_t *ksp;
119
120 if (kstat_chain_update(priv->kc) == -1)
121 return 0;
122
123 if (dev->type == DEVICE_NIC) {
124 ksp = kstat_lookup(priv->kc, "link", 0, (char *)dev->name);
125 } else {
126 ksp = kstat_lookup(priv->kc, "xdb", dev->instance,
127 (char *)"req_statistics");
128 }
129
130 if (ksp == NULL)
131 return 0;
132
133 if (kstat_read(priv->kc, ksp, NULL) == -1)
134 return 0;
135
136 dev->used = 1;
137
138 bcopy(&(dev->stats[1][0]), &(dev->stats[0][0]), sizeof(dev->stats[0]));
139
140 if (dev->type == DEVICE_NIC) {
141 if (!kstat_get(ksp, "rbytes64", &dev->stats[1][0]) ||
142 !kstat_get(ksp, "ipackets64", &dev->stats[1][1]) ||
143 !kstat_get(ksp, "ierrors", &dev->stats[1][2]) ||
144 !kstat_get(ksp, "obytes64", &dev->stats[1][3]) ||
145 !kstat_get(ksp, "opackets64", &dev->stats[1][4]) ||
146 !kstat_get(ksp, "oerrors", &dev->stats[1][5]))
147 return 0;
148
149 dev->stats[1][6] = 0;
150 dev->stats[1][7] = 0;
151 } else {
152 if (!kstat_get(ksp, "rd_reqs", &dev->stats[1][0]) ||
153 !kstat_get(ksp, "wr_reqs", &dev->stats[1][1]) ||
154 !kstat_get(ksp, "oo_reqs", &dev->stats[1][2]))
155 return 0;
156 }
157
158 return 1;
159 }
160
init_dev(priv_data_t * priv,int type,const char * name,int instance,int domid)161 static int init_dev(priv_data_t *priv, int type, const char *name,
162 int instance, int domid)
163 {
164 stdevice_t *dev;
165
166 if (!(dev = malloc(sizeof(*dev))))
167 return 0;
168
169 bzero(dev, sizeof(*dev));
170 dev->type = type;
171 if (name != NULL)
172 strcpy(dev->name, name);
173 dev->instance = instance;
174 dev->domid = domid;
175 dev->next = priv->devs;
176 priv->devs = dev;
177
178 /*
179 * Update twice to avoid delta-since-boot.
180 */
181 if (!update_dev_stats(priv, dev))
182 return 0;
183 return update_dev_stats(priv, dev);
184 }
185
update_nic(priv_data_t * priv,xenstat_domain * dom,xenstat_network * net,const char * name)186 static int update_nic(priv_data_t *priv, xenstat_domain *dom,
187 xenstat_network *net, const char *name)
188 {
189 stdevice_t *dev;
190
191 for (dev = priv->devs; dev != NULL; dev = dev->next) {
192 if (dev->type == DEVICE_NIC && dev->domid == dom->id &&
193 strcmp(name, dev->name) == 0) {
194 if (!update_dev_stats(priv, dev))
195 return 0;
196 net->rbytes = dev->stats[1][0] - dev->stats[0][0];
197 net->rpackets = dev->stats[1][1] - dev->stats[0][1];
198 net->rerrs = dev->stats[1][2] - dev->stats[0][2];
199 net->tbytes = dev->stats[1][3] - dev->stats[0][3];
200 net->tpackets = dev->stats[1][4] - dev->stats[0][4];
201 net->terrs = dev->stats[1][5] - dev->stats[0][5];
202 net->rdrop = dev->stats[1][6] - dev->stats[0][6];
203 net->tdrop = dev->stats[1][7] - dev->stats[0][7];
204 return 1;
205 }
206 }
207
208 return init_dev(priv, DEVICE_NIC, name, 0, dom->id);
209 }
210
211 static int
collect_dom_networks(xenstat_node * node,priv_data_t * priv,xenstat_domain * dom)212 collect_dom_networks(xenstat_node *node, priv_data_t *priv, xenstat_domain *dom)
213 {
214 char path[PATH_MAX];
215 char **vifs;
216 int ret = 1;
217 int nr;
218 int i;
219
220 snprintf(path, sizeof(path), "/local/domain/%d/device/vif", dom->id);
221
222 dom->num_networks = 0;
223 free(dom->networks);
224 dom->networks = NULL;
225
226 vifs = xs_directory(node->handle->xshandle, XBT_NULL, path, &nr);
227 if (vifs == NULL)
228 goto out;
229
230 dom->num_networks = nr;
231 dom->networks = calloc(nr, sizeof(xenstat_network));
232
233 for (i = 0; i < dom->num_networks; i++) {
234 char *tmp;
235
236 snprintf(path, sizeof(path),
237 "/local/domain/%d/device/vif/%d/backend", dom->id, i);
238
239 tmp = xs_read(node->handle->xshandle, XBT_NULL, path, NULL);
240
241 if (tmp == NULL)
242 goto out;
243
244 snprintf(path, sizeof(path), "%s/nic", tmp);
245 free(tmp);
246
247 tmp = xs_read(node->handle->xshandle, XBT_NULL, path, NULL);
248
249 if (tmp == NULL || tmp[0] == '\0') {
250 free(tmp);
251 goto out;
252 }
253
254 if (!(ret = update_nic(priv, dom, &dom->networks[i], tmp))) {
255 free(tmp);
256 goto out;
257 }
258
259 free(tmp);
260 }
261
262 ret = 1;
263 out:
264 free(vifs);
265 return ret;
266 }
267
xenstat_collect_networks(xenstat_node * node)268 int xenstat_collect_networks(xenstat_node * node)
269 {
270 int i;
271 priv_data_t *priv = get_priv_data(node->handle);
272 stdevice_t *dev;
273
274 if (priv == NULL)
275 return 0;
276
277 for (dev = priv->devs; dev != NULL; dev = dev->next)
278 dev->used = 0;
279
280 for (i = 0; i < node->num_domains; i++) {
281 if (node->domains[i].id == 0)
282 continue;
283 if (!collect_dom_networks(node, priv, &node->domains[i]))
284 return 0;
285 }
286
287 gc_devs(priv, DEVICE_NIC);
288
289 return 1;
290 }
291
xenstat_uninit_networks(xenstat_handle * handle)292 void xenstat_uninit_networks(xenstat_handle *handle)
293 {
294 xenstat_uninit_devs(handle, DEVICE_NIC);
295 }
296
update_xdb(priv_data_t * priv,xenstat_domain * dom,xenstat_vbd * vbd,int instance)297 static int update_xdb(priv_data_t *priv, xenstat_domain *dom,
298 xenstat_vbd *vbd, int instance)
299 {
300 stdevice_t *dev;
301
302 for (dev = priv->devs; dev != NULL; dev = dev->next) {
303 if (dev->type == DEVICE_XDB && dev->domid == dom->id &&
304 dev->instance == instance) {
305 if (!update_dev_stats(priv, dev))
306 return 0;
307 vbd->dev = dev->instance;
308 vbd->rd_reqs = dev->stats[1][0] - dev->stats[0][0];
309 vbd->wr_reqs = dev->stats[1][1] - dev->stats[0][1];
310 vbd->oo_reqs = dev->stats[1][2] - dev->stats[0][2];
311 return 1;
312 }
313 }
314
315 return init_dev(priv, DEVICE_XDB, NULL, instance, dom->id);
316 }
317
318 static int
collect_dom_vbds(xenstat_node * node,priv_data_t * priv,xenstat_domain * dom)319 collect_dom_vbds(xenstat_node *node, priv_data_t *priv, xenstat_domain *dom)
320 {
321 char path[PATH_MAX];
322 char **vbds;
323 int ret = 1;
324 int nr;
325 int i;
326
327 snprintf(path, sizeof(path), "/local/domain/%d/device/vbd", dom->id);
328
329 dom->num_vbds = 0;
330 free(dom->vbds);
331 dom->vbds = NULL;
332
333 vbds = xs_directory(node->handle->xshandle, XBT_NULL, path, &nr);
334 if (vbds == NULL)
335 goto out;
336
337 dom->num_vbds = nr;
338 dom->vbds = calloc(nr, sizeof(xenstat_vbd));
339
340 for (i = 0; i < dom->num_vbds; i++) {
341 char *tmp;
342 int inst;
343
344 snprintf(path, sizeof(path),
345 "/local/domain/%d/device/vbd/%s/backend", dom->id, vbds[i]);
346
347 tmp = xs_read(node->handle->xshandle, XBT_NULL, path, NULL);
348
349 if (tmp == NULL)
350 goto out;
351
352 snprintf(path, sizeof(path), "%s/instance", tmp);
353 free(tmp);
354
355 tmp = xs_read(node->handle->xshandle, XBT_NULL, path, NULL);
356
357 /*
358 * Fails when connection is not completed; mark it clearly with
359 * a -1.
360 */
361 if (tmp == NULL || sscanf(tmp, "%d", &inst) != 1) {
362 dom->vbds[i].dev = -1;
363 free(tmp);
364 goto out;
365 }
366
367 free(tmp);
368
369 if (!(ret = update_xdb(priv, dom, &dom->vbds[i], inst)))
370 goto out;
371 }
372
373 out:
374 free(vbds);
375 return ret;
376 }
377
xenstat_collect_vbds(xenstat_node * node)378 int xenstat_collect_vbds(xenstat_node * node)
379 {
380 int i;
381 priv_data_t *priv = get_priv_data(node->handle);
382 stdevice_t *dev;
383
384 if (priv == NULL)
385 return 0;
386
387 for (dev = priv->devs; dev != NULL; dev = dev->next)
388 dev->used = 0;
389
390 for (i = 0; i < node->num_domains; i++) {
391 if (node->domains[i].id == 0)
392 continue;
393 if (!collect_dom_vbds(node, priv, &node->domains[i]))
394 return 0;
395 }
396
397 gc_devs(priv, DEVICE_XDB);
398
399 return 1;
400 }
401
xenstat_uninit_vbds(xenstat_handle * handle)402 void xenstat_uninit_vbds(xenstat_handle * handle)
403 {
404 xenstat_uninit_devs(handle, DEVICE_XDB);
405 }
406