ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.65
Committed: Wed Apr 7 14:53:40 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.64: +22 -22 lines
Log Message:
Whitespace tidyup - change spaces to tabs.

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