ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.79
Committed: Sun Nov 7 15:06:49 2004 UTC (19 years, 6 months ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_11
Changes since 1.78: +6 -3 lines
Log Message:
HP-UX disk IO calculation, version 3.

File Contents

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