ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.69
Committed: Sun Jun 20 17:00:25 2004 UTC (19 years, 10 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.68: +8 -2 lines
Log Message:
Since OpenBSD 3.5 disk io stats have been seperated into read and write,
just like NetBSD. This fixes libstatgrab to work on 3.5+.

Bug reported by Wijnand Wiersma and Christian Weisgerber.

File Contents

# Content
1 /*
2 * i-scream libstatgrab
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2004 i-scream
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 * 02111-1307 USA
20 *
21 * $Id: disk_stats.c,v 1.68 2004/04/08 14:30:43 tdb Exp $
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include "statgrab.h"
33 #include "vector.h"
34 #include "tools.h"
35
36 #ifdef SOLARIS
37 #include <sys/mnttab.h>
38 #include <sys/statvfs.h>
39 #include <kstat.h>
40 #define VALID_FS_TYPES {"ufs", "tmpfs"}
41 #endif
42
43 #if defined(LINUX) || defined(CYGWIN)
44 #include <mntent.h>
45 #include <sys/vfs.h>
46 #endif
47
48 #ifdef LINUX
49 #define VALID_FS_TYPES {"adfs", "affs", "befs", "bfs", "efs", "ext2", \
50 "ext3", "vxfs", "hfs", "hfsplus", "hpfs", "jffs", \
51 "jffs2", "minix", "msdos", "ntfs", "qnx4", "ramfs", \
52 "rootfs", "reiserfs", "sysv", "v7", "udf", "ufs", \
53 "umsdos", "vfat", "xfs", "jfs"}
54 #endif
55
56 #ifdef CYGWIN
57 #define VALID_FS_TYPES {"user"}
58 #endif
59
60 #ifdef ALLBSD
61 #include <sys/param.h>
62 #include <sys/ucred.h>
63 #include <sys/mount.h>
64 #endif
65 #if defined(FREEBSD) || defined(DFBSD)
66 #include <sys/dkstat.h>
67 #include <devstat.h>
68 #define VALID_FS_TYPES {"hpfs", "msdosfs", "ntfs", "udf", "ext2fs", \
69 "ufs", "mfs"}
70 #endif
71 #if defined(NETBSD) || defined(OPENBSD)
72 #include <sys/param.h>
73 #include <sys/sysctl.h>
74 #include <sys/disk.h>
75 #define VALID_FS_TYPES {"ffs", "mfs", "msdos", "lfs", "adosfs", "ext2fs", \
76 "ntfs"}
77 #endif
78
79 static void disk_stat_init(sg_fs_stats *d) {
80 d->device_name = NULL;
81 d->fs_type = NULL;
82 d->mnt_point = NULL;
83 }
84
85 static void disk_stat_destroy(sg_fs_stats *d) {
86 free(d->device_name);
87 free(d->fs_type);
88 free(d->mnt_point);
89 }
90
91 static int is_valid_fs_type(const char *type) {
92 const char *types[] = VALID_FS_TYPES;
93 int i;
94
95 for (i = 0; i < (sizeof types / sizeof *types); i++) {
96 if (strcmp(types[i], type) == 0) {
97 return 1;
98 }
99 }
100 return 0;
101 }
102
103 sg_fs_stats *sg_get_fs_stats(int *entries){
104 VECTOR_DECLARE_STATIC(disk_stats, sg_fs_stats, 10,
105 disk_stat_init, disk_stat_destroy);
106
107 int valid_type;
108 int num_disks=0;
109 #if defined(LINUX) || defined (SOLARIS) || defined(CYGWIN)
110 FILE *f;
111 #endif
112
113 sg_fs_stats *disk_ptr;
114
115 #ifdef SOLARIS
116 struct mnttab mp;
117 struct statvfs fs;
118 #endif
119 #if defined(LINUX) || defined(CYGWIN)
120 struct mntent *mp;
121 struct statfs fs;
122 #endif
123 #ifdef ALLBSD
124 int nummnt;
125 struct statfs *mp;
126 #endif
127
128 #ifdef ALLBSD
129 nummnt=getmntinfo(&mp , MNT_LOCAL);
130 if (nummnt<=0){
131 sg_set_error(SG_ERROR_GETMNTINFO, NULL);
132 return NULL;
133 }
134 for(;nummnt--; mp++){
135 valid_type = is_valid_fs_type(mp->f_fstypename);
136 #endif
137
138 #if defined(LINUX) || defined(CYGWIN)
139 if ((f=setmntent("/etc/mtab", "r" ))==NULL){
140 sg_set_error(SG_ERROR_SETMNTENT, NULL);
141 return NULL;
142 }
143
144 while((mp=getmntent(f))){
145 if((statfs(mp->mnt_dir, &fs)) !=0){
146 continue;
147 }
148
149 valid_type = is_valid_fs_type(mp->mnt_type);
150 #endif
151
152 #ifdef SOLARIS
153 if ((f=fopen("/etc/mnttab", "r" ))==NULL){
154 sg_set_error(SG_ERROR_OPEN, "/etc/mnttab");
155 return NULL;
156 }
157 while((getmntent(f, &mp)) == 0){
158 if ((statvfs(mp.mnt_mountp, &fs)) !=0){
159 continue;
160 }
161 valid_type = is_valid_fs_type(mp.mnt_fstype);
162 #endif
163
164 if(valid_type){
165 if (VECTOR_RESIZE(disk_stats, num_disks + 1) < 0) {
166 return NULL;
167 }
168 disk_ptr=disk_stats+num_disks;
169
170 #ifdef ALLBSD
171 if (sg_update_string(&disk_ptr->device_name, mp->f_mntfromname) < 0) {
172 return NULL;
173 }
174 if (sg_update_string(&disk_ptr->fs_type, mp->f_fstypename) < 0) {
175 return NULL;
176 }
177 if (sg_update_string(&disk_ptr->mnt_point, mp->f_mntonname) < 0) {
178 return NULL;
179 }
180
181 disk_ptr->size = (long long)mp->f_bsize * (long long) mp->f_blocks;
182 disk_ptr->avail = (long long)mp->f_bsize * (long long) mp->f_bavail;
183 disk_ptr->used = (disk_ptr->size) - ((long long)mp->f_bsize * (long long)mp->f_bfree);
184
185 disk_ptr->total_inodes=(long long)mp->f_files;
186 disk_ptr->free_inodes=(long long)mp->f_ffree;
187 /* Freebsd doesn't have a "available" inodes */
188 disk_ptr->used_inodes=disk_ptr->total_inodes-disk_ptr->free_inodes;
189 #endif
190 #if defined(LINUX) || defined(CYGWIN)
191 if (sg_update_string(&disk_ptr->device_name, mp->mnt_fsname) < 0) {
192 return NULL;
193 }
194
195 if (sg_update_string(&disk_ptr->fs_type, mp->mnt_type) < 0) {
196 return NULL;
197 }
198
199 if (sg_update_string(&disk_ptr->mnt_point, mp->mnt_dir) < 0) {
200 return NULL;
201 }
202 disk_ptr->size = (long long)fs.f_bsize * (long long)fs.f_blocks;
203 disk_ptr->avail = (long long)fs.f_bsize * (long long)fs.f_bavail;
204 disk_ptr->used = (disk_ptr->size) - ((long long)fs.f_bsize * (long long)fs.f_bfree);
205
206 disk_ptr->total_inodes=(long long)fs.f_files;
207 disk_ptr->free_inodes=(long long)fs.f_ffree;
208 /* Linux doesn't have a "available" inodes */
209 disk_ptr->used_inodes=disk_ptr->total_inodes-disk_ptr->free_inodes;
210 #endif
211
212 #ifdef SOLARIS
213 /* Maybe make this char[bigenough] and do strncpy's and put a null in the end?
214 * Downside is its a bit hungry for a lot of mounts, as MNT_MAX_SIZE would prob
215 * be upwards of a k each
216 */
217 if (sg_update_string(&disk_ptr->device_name, mp.mnt_special) < 0) {
218 return NULL;
219 }
220
221 if (sg_update_string(&disk_ptr->fs_type, mp.mnt_fstype) < 0) {
222 return NULL;
223 }
224
225 if (sg_update_string(&disk_ptr->mnt_point, mp.mnt_mountp) < 0) {
226 return NULL;
227 }
228
229 disk_ptr->size = (long long)fs.f_frsize * (long long)fs.f_blocks;
230 disk_ptr->avail = (long long)fs.f_frsize * (long long)fs.f_bavail;
231 disk_ptr->used = (disk_ptr->size) - ((long long)fs.f_frsize * (long long)fs.f_bfree);
232
233 disk_ptr->total_inodes=(long long)fs.f_files;
234 disk_ptr->used_inodes=disk_ptr->total_inodes - (long long)fs.f_ffree;
235 disk_ptr->free_inodes=(long long)fs.f_favail;
236 #endif
237 num_disks++;
238 }
239 }
240
241 *entries=num_disks;
242
243 /* If this fails, there is very little i can do about it, so
244 I'll ignore it :) */
245 #if defined(LINUX) || defined(CYGWIN)
246 endmntent(f);
247 #endif
248 #if defined(SOLARIS)
249 fclose(f);
250 #endif
251
252 return disk_stats;
253
254 }
255
256 int sg_fs_compare_device_name(const void *va, const void *vb) {
257 const sg_fs_stats *a = (const sg_fs_stats *)va;
258 const sg_fs_stats *b = (const sg_fs_stats *)vb;
259
260 return strcmp(a->device_name, b->device_name);
261 }
262
263 int sg_fs_compare_mnt_point(const void *va, const void *vb) {
264 const sg_fs_stats *a = (const sg_fs_stats *)va;
265 const sg_fs_stats *b = (const sg_fs_stats *)vb;
266
267 return strcmp(a->mnt_point, b->mnt_point);
268 }
269
270 static void diskio_stat_init(sg_disk_io_stats *d) {
271 d->disk_name = NULL;
272 }
273
274 static void diskio_stat_destroy(sg_disk_io_stats *d) {
275 free(d->disk_name);
276 }
277
278 VECTOR_DECLARE_STATIC(diskio_stats, sg_disk_io_stats, 10,
279 diskio_stat_init, diskio_stat_destroy);
280
281 #ifdef LINUX
282 typedef struct {
283 int major;
284 int minor;
285 } partition;
286 #endif
287
288 sg_disk_io_stats *sg_get_disk_io_stats(int *entries){
289 int num_diskio;
290 #ifndef LINUX
291 sg_disk_io_stats *diskio_stats_ptr;
292 #endif
293
294 #ifdef SOLARIS
295 kstat_ctl_t *kc;
296 kstat_t *ksp;
297 kstat_io_t kios;
298 #endif
299 #ifdef LINUX
300 FILE *f;
301 char *line_ptr;
302 int major, minor;
303 int has_pp_stats = 1;
304 VECTOR_DECLARE_STATIC(parts, partition, 16, NULL, NULL);
305 int i, n;
306 time_t now;
307 const char *format;
308 #endif
309 #if defined(FREEBSD) || defined(DFBSD)
310 static struct statinfo stats;
311 static int stats_init = 0;
312 int counter;
313 struct device_selection *dev_sel = NULL;
314 int n_selected, n_selections;
315 long sel_gen;
316 struct devstat *dev_ptr;
317 #endif
318 #ifdef NETBSD
319 struct disk_sysctl *stats;
320 #endif
321 #ifdef OPENBSD
322 int diskcount;
323 char *disknames, *name, *bufpp;
324 char **dk_name;
325 struct diskstats *stats;
326 #endif
327 #ifdef NETBSD
328 #define MIBSIZE 3
329 #endif
330 #ifdef OPENBSD
331 #define MIBSIZE 2
332 #endif
333 #if defined(NETBSD) || defined(OPENBSD)
334 int num_disks, i;
335 int mib[MIBSIZE];
336 size_t size;
337 #endif
338
339 num_diskio=0;
340
341 #ifdef OPENBSD
342 mib[0] = CTL_HW;
343 mib[1] = HW_DISKCOUNT;
344
345 size = sizeof(diskcount);
346 if (sysctl(mib, MIBSIZE, &diskcount, &size, NULL, 0) < 0) {
347 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKCOUNT");
348 return NULL;
349 }
350
351 mib[0] = CTL_HW;
352 mib[1] = HW_DISKNAMES;
353
354 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
355 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
356 return NULL;
357 }
358
359 disknames = sg_malloc(size);
360 if (disknames == NULL) {
361 return NULL;
362 }
363
364 if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) {
365 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES");
366 return NULL;
367 }
368
369 dk_name = sg_malloc(diskcount * sizeof(char *));
370 bufpp = disknames;
371 for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) {
372 dk_name[i] = name;
373 }
374 #endif
375
376 #if defined(NETBSD) || defined(OPENBSD)
377 mib[0] = CTL_HW;
378 mib[1] = HW_DISKSTATS;
379 #ifdef NETBSD
380 mib[2] = sizeof(struct disk_sysctl);
381 #endif
382
383 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
384 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
385 return NULL;
386 }
387
388 #ifdef NETBSD
389 num_disks = size / sizeof(struct disk_sysctl);
390 #else
391 num_disks = size / sizeof(struct diskstats);
392 #endif
393
394 stats = sg_malloc(size);
395 if (stats == NULL) {
396 return NULL;
397 }
398
399 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
400 sg_set_error(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS");
401 return NULL;
402 }
403
404 for (i = 0; i < num_disks; i++) {
405 const char *name;
406 u_int64_t rbytes, wbytes;
407
408 #ifdef NETBSD
409 #ifdef HAVE_DK_RBYTES
410 rbytes = stats[i].dk_rbytes;
411 wbytes = stats[i].dk_wbytes;
412 #else
413 /* Before 2.0, NetBSD merged reads and writes. */
414 rbytes = wbytes = stats[i].dk_bytes;
415 #endif
416 #else
417 #ifdef HAVE_DS_RBYTES
418 rbytes = stats[i].ds_rbytes;
419 wbytes = stats[i].ds_wbytes;
420 #else
421 /* Before 3.5, OpenBSD merged reads and writes */
422 rbytes = wbytes = stats[i].ds_bytes;
423 #endif
424 #endif
425
426 /* Don't keep stats for disks that have never been used. */
427 if (rbytes == 0 && wbytes == 0) {
428 continue;
429 }
430
431 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
432 return NULL;
433 }
434 diskio_stats_ptr = diskio_stats + num_diskio;
435
436 diskio_stats_ptr->read_bytes = rbytes;
437 diskio_stats_ptr->write_bytes = wbytes;
438 #ifdef NETBSD
439 name = stats[i].dk_name;
440 #else
441 name = dk_name[i];
442 #endif
443 if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) {
444 return NULL;
445 }
446 diskio_stats_ptr->systime = time(NULL);
447
448 num_diskio++;
449 }
450
451 free(stats);
452 #ifdef OPENBSD
453 free(dk_name);
454 free(disknames);
455 #endif
456 #endif
457
458 #if defined(FREEBSD) || defined(DFBSD)
459 if (!stats_init) {
460 stats.dinfo=sg_malloc(sizeof(struct devinfo));
461 if(stats.dinfo==NULL) return NULL;
462 bzero(stats.dinfo, sizeof(struct devinfo));
463 stats_init = 1;
464 }
465 #ifdef FREEBSD5
466 if ((devstat_getdevs(NULL, &stats)) < 0) {
467 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
468 return NULL;
469 }
470 /* Not aware of a get all devices, so i said 999. If we ever
471 * find a machine with more than 999 disks, then i'll change
472 * this number :)
473 */
474 if (devstat_selectdevs(&dev_sel, &n_selected, &n_selections, &sel_gen, stats.dinfo->generation, stats.dinfo->devices, stats.dinfo->numdevs, NULL, 0, NULL, 0, DS_SELECT_ONLY, 999, 1) < 0) {
475 sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
476 return NULL;
477 }
478 #else
479 if ((getdevs(&stats)) < 0) {
480 sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL);
481 return NULL;
482 }
483 /* Not aware of a get all devices, so i said 999. If we ever
484 * find a machine with more than 999 disks, then i'll change
485 * this number :)
486 */
487 if (selectdevs(&dev_sel, &n_selected, &n_selections, &sel_gen, stats.dinfo->generation, stats.dinfo->devices, stats.dinfo->numdevs, NULL, 0, NULL, 0, DS_SELECT_ONLY, 999, 1) < 0) {
488 sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL);
489 return NULL;
490 }
491 #endif
492
493 for(counter=0;counter<stats.dinfo->numdevs;counter++){
494 dev_ptr=&stats.dinfo->devices[dev_sel[counter].position];
495
496 /* Throw away devices that have done nothing, ever.. Eg "odd"
497 * devices.. like mem, proc.. and also doesn't report floppy
498 * drives etc unless they are doing stuff :)
499 */
500 #ifdef FREEBSD5
501 if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
502 #else
503 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
504 #endif
505
506 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
507 return NULL;
508 }
509 diskio_stats_ptr=diskio_stats+num_diskio;
510
511 #ifdef FREEBSD5
512 diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ];
513 diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE];
514 #else
515 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
516 diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
517 #endif
518 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
519 if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) {
520 sg_set_error(SG_ERROR_ASPRINTF, NULL);
521 return NULL;
522 }
523 diskio_stats_ptr->systime=time(NULL);
524
525 num_diskio++;
526 }
527 free(dev_sel);
528
529 #endif
530 #ifdef SOLARIS
531 if ((kc = kstat_open()) == NULL) {
532 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
533 return NULL;
534 }
535
536 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
537 if (!strcmp(ksp->ks_class, "disk")) {
538
539 if(ksp->ks_type != KSTAT_TYPE_IO) continue;
540 /* We dont want metadevices appearins as num_diskio */
541 if(strcmp(ksp->ks_module, "md")==0) continue;
542 if((kstat_read(kc, ksp, &kios))==-1){
543 }
544
545 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
546 kstat_close(kc);
547 return NULL;
548 }
549 diskio_stats_ptr=diskio_stats+num_diskio;
550
551 diskio_stats_ptr->read_bytes=kios.nread;
552 diskio_stats_ptr->write_bytes=kios.nwritten;
553 if (sg_update_string(&diskio_stats_ptr->disk_name,
554 sg_get_svr_from_bsd(ksp->ks_name)) < 0) {
555 return NULL;
556 }
557 diskio_stats_ptr->systime=time(NULL);
558
559 num_diskio++;
560 }
561 }
562
563 kstat_close(kc);
564 #endif
565
566 #ifdef LINUX
567 num_diskio = 0;
568 n = 0;
569
570 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
571 have statistics in here too, so we can use those directly.
572 2.6 kernels have /proc/diskstats instead with almost (but not quite)
573 the same format. */
574
575 f = fopen("/proc/diskstats", "r");
576 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
577 if (f == NULL) {
578 f = fopen("/proc/partitions", "r");
579 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
580 }
581 if (f == NULL) goto out;
582 now = time(NULL);
583
584 while ((line_ptr = sg_f_read_line(f, "")) != NULL) {
585 char name[100];
586 char *s;
587 long long rsect, wsect;
588
589 int nr = sscanf(line_ptr, format,
590 &major, &minor, name, &rsect, &wsect);
591 if (nr < 3) continue;
592
593 /* Skip device names ending in numbers, since they're
594 partitions. */
595 s = name;
596 while (*s != '\0') s++;
597 --s;
598 if (*s >= '0' && *s <= '9') continue;
599
600 if (nr < 5) {
601 has_pp_stats = 0;
602 rsect = 0;
603 wsect = 0;
604 }
605
606 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
607 goto out;
608 }
609 if (VECTOR_RESIZE(parts, n + 1) < 0) {
610 goto out;
611 }
612
613 if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) {
614 goto out;
615 }
616 diskio_stats[n].read_bytes = rsect * 512;
617 diskio_stats[n].write_bytes = wsect * 512;
618 diskio_stats[n].systime = now;
619 parts[n].major = major;
620 parts[n].minor = minor;
621
622 n++;
623 }
624
625 fclose(f);
626 f = NULL;
627
628 if (!has_pp_stats) {
629 /* This is an older kernel where /proc/partitions doesn't
630 contain stats. Read what we can from /proc/stat instead, and
631 fill in the appropriate bits of the list allocated above. */
632
633 f = fopen("/proc/stat", "r");
634 if (f == NULL) goto out;
635 now = time(NULL);
636
637 line_ptr = sg_f_read_line(f, "disk_io:");
638 if (line_ptr == NULL) goto out;
639
640 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
641 long long rsect, wsect;
642
643 if (*++line_ptr == '\0') break;
644
645 if((sscanf(line_ptr,
646 "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
647 &major, &minor, &rsect, &wsect)) != 4) {
648 continue;
649 }
650
651 /* Find the corresponding device from earlier.
652 Just to add to the fun, "minor" is actually the disk
653 number, not the device minor, so we need to figure
654 out the real minor number based on the major!
655 This list is not exhaustive; if you're running
656 an older kernel you probably don't have fancy
657 I2O hardware anyway... */
658 switch (major) {
659 case 3:
660 case 21:
661 case 22:
662 case 33:
663 case 34:
664 case 36:
665 case 56:
666 case 57:
667 case 88:
668 case 89:
669 case 90:
670 case 91:
671 minor *= 64;
672 break;
673 case 9:
674 case 43:
675 break;
676 default:
677 minor *= 16;
678 break;
679 }
680 for (i = 0; i < n; i++) {
681 if (major == parts[i].major
682 && minor == parts[i].minor)
683 break;
684 }
685 if (i == n) continue;
686
687 /* We read the number of blocks. Blocks are stored in
688 512 bytes */
689 diskio_stats[i].read_bytes = rsect * 512;
690 diskio_stats[i].write_bytes = wsect * 512;
691 diskio_stats[i].systime = now;
692 }
693 }
694
695 num_diskio = n;
696 out:
697 if (f != NULL) fclose(f);
698 #endif
699
700 #ifdef CYGWIN
701 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
702 return NULL;
703 #endif
704
705 *entries=num_diskio;
706
707 return diskio_stats;
708 }
709
710 sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){
711 VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1,
712 diskio_stat_init, diskio_stat_destroy);
713 sg_disk_io_stats *src = NULL, *dest;
714 int i, j, diff_count, new_count;
715
716 if (diskio_stats == NULL) {
717 /* No previous stats, so we can't calculate a difference. */
718 return sg_get_disk_io_stats(entries);
719 }
720
721 /* Resize the results array to match the previous stats. */
722 diff_count = VECTOR_SIZE(diskio_stats);
723 if (VECTOR_RESIZE(diff, diff_count) < 0) {
724 return NULL;
725 }
726
727 /* Copy the previous stats into the result. */
728 for (i = 0; i < diff_count; i++) {
729 src = &diskio_stats[i];
730 dest = &diff[i];
731
732 if (sg_update_string(&dest->disk_name, src->disk_name) < 0) {
733 return NULL;
734 }
735 dest->read_bytes = src->read_bytes;
736 dest->write_bytes = src->write_bytes;
737 dest->systime = src->systime;
738 }
739
740 /* Get a new set of stats. */
741 if (sg_get_disk_io_stats(&new_count) == NULL) {
742 return NULL;
743 }
744
745 /* For each previous stat... */
746 for (i = 0; i < diff_count; i++) {
747 dest = &diff[i];
748
749 /* ... find the corresponding new stat ... */
750 for (j = 0; j < new_count; j++) {
751 /* Try the new stat in the same position first,
752 since that's most likely to be it. */
753 src = &diskio_stats[(i + j) % new_count];
754 if (strcmp(src->disk_name, dest->disk_name) == 0) {
755 break;
756 }
757 }
758 if (j == new_count) {
759 /* No match found. */
760 continue;
761 }
762
763 /* ... and subtract the previous stat from it to get the
764 difference. */
765 dest->read_bytes = src->read_bytes - dest->read_bytes;
766 dest->write_bytes = src->write_bytes - dest->write_bytes;
767 dest->systime = src->systime - dest->systime;
768 }
769
770 *entries = diff_count;
771 return diff;
772 }
773
774 int sg_disk_io_compare_name(const void *va, const void *vb) {
775 const sg_disk_io_stats *a = (const sg_disk_io_stats *)va;
776 const sg_disk_io_stats *b = (const sg_disk_io_stats *)vb;
777
778 return strcmp(a->disk_name, b->disk_name);
779 }
780