ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.70
Committed: Sun Jun 20 20:32:24 2004 UTC (19 years, 10 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.69: +5 -1 lines
Log Message:
On NetBSD 2.0 (in fact, all BSD's, but only know of NetBSD 2.0) use statvfs
instead of statfs when available.

No guarantees I've got this right - I need to upgrade our NetBSD 2 test
machine to a more recent build.

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