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