Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / ksysguard / ksysguardd / Linux / diskstats.c
blob7843dfb2c02acc8c3054a9ecfbedbd7e6ac0adaa
1 /*
2 KSysGuard, the KDE System Guard
4 Copyright (c) 2006 Greg Martyn <greg.martyn@gmail.com>
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of version 2 or later of the GNU General Public
8 License as published by the Free Software Foundation.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 /* This file will read from /proc/diskstats.
22 /proc/diskstats support should exist in kernel versions 2.4.20, 2.5.45, 2.6 and up
25 #include <sys/time.h> /* for gettimeofday */
26 #include <string.h> /* for strcmp */
27 #include <stdlib.h> /* for malloc */
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <unistd.h>
35 #include "Command.h"
36 #include "ksysguardd.h"
38 #include "diskstats.h"
40 #define DISKSTATSBUFSIZE (32 * 1024)
41 #define DISKDEVNAMELEN 16
43 typedef struct
45 unsigned long delta;
46 unsigned long old;
47 } DiskLoadSample;
49 typedef struct
51 /* 5 types of samples are taken:
52 total, rio, wio, rBlk, wBlk */
53 DiskLoadSample s[ 5 ];
54 } DiskLoadInfo;
56 typedef struct DiskIOInfo
58 int major;
59 int minor;
60 char* devname;
62 int alive;
63 DiskLoadSample total; /* Total accesses - Fields 1+5 */
64 DiskLoadSample rio; /* Read Accesses - Field 1 - # of reads issued */
65 DiskLoadSample wio; /* Write Accesses - Field 5 - # of writes completed */
66 DiskLoadSample rblk; /* Read Data - Field 3 - # of sectors read */
67 DiskLoadSample wblk; /* Written Data - Field 7 - # of sectors written */
68 DiskLoadSample rtim; /* - Field 4 - # of milliseconds spent reading */
69 DiskLoadSample wtim; /* - Field 8 - # of milliseconds spent writing */
70 unsigned int ioqueue; /* - Field 9 - # of I/Os currently in progress */
71 struct DiskIOInfo* next;
72 } DiskIOInfo;
74 /* We have observed deviations of up to 5% in the accuracy of the timer
75 * interrupts. So we try to measure the interrupt interval and use this
76 * value to calculate timing dependant values. */
77 static float timeInterval = 0;
78 static struct timeval lastSampling;
79 static struct timeval currSampling;
80 static struct SensorModul* StatSM;
82 static DiskLoadInfo* DiskLoad = 0;
83 static DiskIOInfo* DiskIO = 0;
85 static char IOStatBuf[ DISKSTATSBUFSIZE ]; /* Buffer for /proc/diskstats */
86 static int Dirty = 0;
88 static void cleanup26DiskList( void );
89 static int process26DiskIO( const char* buf );
91 void initDiskstats( struct SensorModul* sm ) {
92 char format[ 32 ];
93 char buf[ 1024 ];
94 char* iostatBufP = IOStatBuf;
96 StatSM = sm;
97 sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
99 /* updateDiskstats() reopens /proc/diskstats as IOStatBuf */
100 if (updateDiskstats()) {
101 /* updateDiskstats() was unable to open file. die. */
102 return;
105 /* Process values from /proc/diskstats (Linux >= 2.6.x) */
106 while (sscanf(iostatBufP, format, buf) == 1) {
107 buf[sizeof(buf) - 1] = '\0';
108 iostatBufP += strlen(buf) + 1; /* move IOstatBufP to next line */
110 process26DiskIO(buf);
114 void exitDiskstats( void ) {
115 free( DiskLoad );
116 DiskLoad = 0;
119 int updateDiskstats( void ) {
120 size_t n;
121 int fd;
123 gettimeofday( &currSampling, 0 );
124 Dirty = 1;
127 IOStatBuf[ 0 ] = '\0';
128 if ( ( fd = open( "/proc/diskstats", O_RDONLY ) ) < 0 )
129 return -1; /* unable to open file. disable this module. */
131 n = read( fd, IOStatBuf, DISKSTATSBUFSIZE - 1 );
132 close( fd );
133 if ( n == DISKSTATSBUFSIZE - 1 || n <= 0 ) {
134 log_error( "Internal buffer too small to read \'/proc/diskstats\'" );
135 return -1;
138 IOStatBuf[ n ] = '\0';
140 return 0;
143 void processDiskstats( void ) {
144 /* Process values from /proc/diskstats (Linux >= 2.6.x) */
146 char* iostatBufP = IOStatBuf;
147 char format[ 32 ];
148 char buf[ 1024 ];
150 sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
152 while (sscanf(iostatBufP, format, buf) == 1) {
153 buf[sizeof(buf) - 1] = '\0';
154 iostatBufP += strlen(buf) + 1; /* move IOstatBufP to next line */
156 process26DiskIO(buf);
159 /* save exact time inverval between this and the last read of /proc/stat */
160 timeInterval = currSampling.tv_sec - lastSampling.tv_sec +
161 ( currSampling.tv_usec - lastSampling.tv_usec ) / 1000000.0;
162 lastSampling = currSampling;
164 cleanup26DiskList();
166 Dirty = 0;
169 static int process26DiskIO( const char* buf ) {
170 /* Process values from /proc/diskstats (Linux >= 2.6.x) */
172 /* For each disk /proc/diskstats includes lines as follows:
173 * 3 0 hda 1314558 74053 26451438 14776742 1971172 4607401 52658448 202855090 0 9597019 217637839
174 * 3 1 hda1 178 360 0 0
175 * 3 2 hda2 354 360 0 0
176 * 3 3 hda3 354 360 0 0
177 * 3 4 hda4 0 0 0 0
178 * 3 5 hda5 529506 9616000 4745856 37966848
180 * - See Documentation/iostats.txt for details on the changes
182 int major, minor;
183 char devname[DISKDEVNAMELEN];
184 unsigned long total,
185 rio, rmrg, rblk, rtim,
186 wio, wmrg, wblk, wtim,
187 ioqueue, iotim, iotimw;
188 DiskIOInfo *ptr = DiskIO;
189 DiskIOInfo *last = 0;
190 char sensorName[128];
193 From kernel 2.6.22.1's Documentation/iostats.txt:
195 First 3 fields of line are major, minor, devname
196 Then:
197 Field 1 -- # of reads issued
198 This is the total number of reads completed successfully.
199 Field 2 -- # of reads merged, field 6 -- # of writes merged
200 Reads and writes which are adjacent to each other may be merged for
201 efficiency. Thus two 4K reads may become one 8K read before it is
202 ultimately handed to the disk, and so it will be counted (and queued)
203 as only one I/O. This field lets you know how often this was done.
204 Field 3 -- # of sectors read
205 This is the total number of sectors read successfully.
206 Field 4 -- # of milliseconds spent reading
207 This is the total number of milliseconds spent by all reads (as
208 measured from __make_request() to end_that_request_last()).
209 Field 5 -- # of writes completed
210 This is the total number of writes completed successfully.
211 Field 7 -- # of sectors written
212 This is the total number of sectors written successfully.
213 Field 8 -- # of milliseconds spent writing
214 This is the total number of milliseconds spent by all writes (as
215 measured from __make_request() to end_that_request_last()).
216 Field 9 -- # of I/Os currently in progress
217 The only field that should go to zero. Incremented as requests are
218 given to appropriate request_queue_t and decremented as they finish.
219 Field 10 -- # of milliseconds spent doing I/Os
220 This field is increases so long as field 9 is nonzero.
221 Field 11 -- weighted # of milliseconds spent doing I/Os
222 This field is incremented at each I/O start, I/O completion, I/O
223 merge, or read of these stats by the number of I/Os in progress
224 (field 9) times the number of milliseconds spent doing I/O since the
225 last update of this field. This can provide an easy measure of both
226 I/O completion time and the backlog that may be accumulating.
229 switch (sscanf(buf, "%d %d %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
230 &major, &minor, devname,
231 &rio, &rmrg, &rblk, &rtim,
232 &wio, &wmrg, &wblk, &wtim,
233 &ioqueue, &iotim, &iotimw))
235 case 7:
236 /* Partition stats entry */
237 /* Adjust read fields rio rmrg rblk rtim -> rio rblk wio wblk */
238 wblk = rtim;
239 wio = rblk;
240 rblk = rmrg;
242 total = rio + wio;
244 break;
245 case 14:
246 /* Disk stats entry */
247 total = rio + wio;
249 break;
250 default:
251 /* Something unexepected */
252 return -1;
255 last = 0;
256 ptr = DiskIO;
257 while (ptr) {
258 if (ptr->major == major && ptr->minor == minor)
260 /* The IO device has already been registered. */
261 ptr->total.delta = total - ptr->total.old;
262 ptr->total.old = total;
263 ptr->rio.delta = rio - ptr->rio.old;
264 ptr->rio.old = rio;
265 ptr->wio.delta = wio - ptr->wio.old;
266 ptr->wio.old = wio;
267 ptr->rblk.delta = rblk - ptr->rblk.old;
268 ptr->rblk.old = rblk;
269 ptr->wblk.delta = wblk - ptr->wblk.old;
270 ptr->wblk.old = wblk;
271 ptr->rtim.delta = rtim - ptr->rtim.old;
272 ptr->rtim.old = rtim;
273 ptr->wtim.delta = wtim - ptr->wtim.old;
274 ptr->wtim.old = wtim;
275 /* fyi: ipqueue doesn't have a delta */
276 ptr->ioqueue = ioqueue;
278 ptr->alive = 1;
279 break;
282 last = ptr;
283 ptr = ptr->next;
286 if (!ptr) {
287 /* The IO device has not been registered yet. We need to add it. */
288 ptr = (DiskIOInfo*)malloc( sizeof( DiskIOInfo ) );
289 ptr->major = major;
290 ptr->minor = minor;
291 ptr->devname = devname;
292 ptr->total.delta = 0;
293 ptr->total.old = total;
294 ptr->rio.delta = 0;
295 ptr->rio.old = rio;
296 ptr->wio.delta = 0;
297 ptr->wio.old = wio;
298 ptr->rblk.delta = 0;
299 ptr->rblk.old = rblk;
300 ptr->wblk.delta = 0;
301 ptr->wblk.old = wblk;
302 ptr->rtim.delta = 0;
303 ptr->rtim.old = rtim;
304 ptr->wtim.delta = 0;
305 ptr->wtim.old = wtim;
306 /* fyi: ipqueue doesn't have a delta */
307 ptr->ioqueue = ioqueue;
309 ptr->alive = 1;
310 ptr->next = 0;
311 if (last) {
312 /* Append new entry at end of list. */
313 last->next = ptr;
315 else {
316 /* List is empty, so we insert the fist element into the list. */
317 DiskIO = ptr;
320 sprintf(sensorName, "disk/%s_(%d:%d)/Rate/totalio", devname, major, minor);
321 registerMonitor(sensorName, "float", print26DiskIO, print26DiskIOInfo,
322 StatSM);
323 sprintf(sensorName, "disk/%s_(%d:%d)/Rate/rio", devname, major, minor);
324 registerMonitor(sensorName, "float", print26DiskIO, print26DiskIOInfo,
325 StatSM);
326 sprintf(sensorName, "disk/%s_(%d:%d)/Rate/wio", devname, major, minor);
327 registerMonitor(sensorName, "float", print26DiskIO, print26DiskIOInfo,
328 StatSM);
329 sprintf(sensorName, "disk/%s_(%d:%d)/Rate/rblk", devname, major, minor);
330 registerMonitor(sensorName, "float", print26DiskIO, print26DiskIOInfo,
331 StatSM);
332 sprintf(sensorName, "disk/%s_(%d:%d)/Rate/wblk", devname, major, minor);
333 registerMonitor(sensorName, "float", print26DiskIO, print26DiskIOInfo,
334 StatSM);
336 sprintf(sensorName, "disk/%s_(%d:%d)/Delta/totalio", devname, major, minor);
337 registerMonitor(sensorName, "integer", print26DiskIO, print26DiskIOInfo,
338 StatSM);
339 sprintf(sensorName, "disk/%s_(%d:%d)/Delta/rio", devname, major, minor);
340 registerMonitor(sensorName, "integer", print26DiskIO, print26DiskIOInfo,
341 StatSM);
342 sprintf(sensorName, "disk/%s_(%d:%d)/Delta/wio", devname, major, minor);
343 registerMonitor(sensorName, "integer", print26DiskIO, print26DiskIOInfo,
344 StatSM);
345 sprintf(sensorName, "disk/%s_(%d:%d)/Delta/rblk", devname, major, minor);
346 registerMonitor(sensorName, "integer", print26DiskIO, print26DiskIOInfo,
347 StatSM);
348 sprintf(sensorName, "disk/%s_(%d:%d)/Delta/wblk", devname, major, minor);
349 registerMonitor(sensorName, "integer", print26DiskIO, print26DiskIOInfo,
350 StatSM);
351 sprintf(sensorName, "disk/%s_(%d:%d)/Delta/rtim", devname, major, minor);
352 registerMonitor(sensorName, "integer", print26DiskIO, print26DiskIOInfo,
353 StatSM);
354 sprintf(sensorName, "disk/%s_(%d:%d)/Delta/wtim", devname, major, minor);
355 registerMonitor(sensorName, "integer", print26DiskIO, print26DiskIOInfo,
356 StatSM);
358 sprintf(sensorName, "disk/%s_(%d:%d)/ioqueue", devname, major, minor);
359 registerMonitor(sensorName, "integer", print26DiskIO, print26DiskIOInfo,
360 StatSM);
363 return 0;
366 static void cleanup26DiskList( void ) {
367 DiskIOInfo* ptr = DiskIO;
368 DiskIOInfo* last = 0;
370 while ( ptr ) {
371 if ( ptr->alive == 0 ) {
372 DiskIOInfo* newPtr;
373 char sensorName[ 128 ];
375 /* Disk device has disappeared. We have to remove it from
376 * the list and unregister the monitors. */
377 sprintf( sensorName, "disk/%s_(%d:%d)/Rate/totalio", ptr->devname, ptr->major, ptr->minor );
378 removeMonitor( sensorName );
379 sprintf( sensorName, "disk/%s_(%d:%d)/Rate/rio", ptr->devname, ptr->major, ptr->minor );
380 removeMonitor( sensorName );
381 sprintf( sensorName, "disk/%s_(%d:%d)/Rate/wio", ptr->devname, ptr->major, ptr->minor );
382 removeMonitor( sensorName );
383 sprintf( sensorName, "disk/%s_(%d:%d)/Rate/rblk", ptr->devname, ptr->major, ptr->minor );
384 removeMonitor( sensorName );
385 sprintf( sensorName, "disk/%s_(%d:%d)/Rate/wblk", ptr->devname, ptr->major, ptr->minor );
386 removeMonitor( sensorName );
388 sprintf( sensorName, "disk/%s_(%d:%d)/Delta/totalio", ptr->devname, ptr->major, ptr->minor );
389 removeMonitor( sensorName );
390 sprintf( sensorName, "disk/%s_(%d:%d)/Delta/rio", ptr->devname, ptr->major, ptr->minor );
391 removeMonitor( sensorName );
392 sprintf( sensorName, "disk/%s_(%d:%d)/Delta/wio", ptr->devname, ptr->major, ptr->minor );
393 removeMonitor( sensorName );
394 sprintf( sensorName, "disk/%s_(%d:%d)/Delta/rblk", ptr->devname, ptr->major, ptr->minor );
395 removeMonitor( sensorName );
396 sprintf( sensorName, "disk/%s_(%d:%d)/Delta/wblk", ptr->devname, ptr->major, ptr->minor );
397 removeMonitor( sensorName );
398 sprintf( sensorName, "disk/%s_(%d:%d)/Delta/rtim", ptr->devname, ptr->major, ptr->minor );
399 removeMonitor( sensorName );
400 sprintf( sensorName, "disk/%s_(%d:%d)/Delta/wtim", ptr->devname, ptr->major, ptr->minor );
401 removeMonitor( sensorName );
403 sprintf( sensorName, "disk/%s_(%d:%d)/ioqueue", ptr->devname, ptr->major, ptr->minor );
404 removeMonitor( sensorName );
406 if ( last ) {
407 last->next = ptr->next;
408 newPtr = ptr->next;
410 else {
411 DiskIO = ptr->next;
412 newPtr = DiskIO;
413 last = 0;
416 free ( ptr );
417 ptr = newPtr;
419 else {
420 ptr->alive = 0;
421 last = ptr;
422 ptr = ptr->next;
427 void print26DiskIO( const char* cmd ) {
428 int major, minor;
429 char devname[DISKDEVNAMELEN];
430 char name[ 17 ];
431 DiskIOInfo* ptr;
433 if ( Dirty )
434 processDiskstats();
436 if(sscanf( cmd, "disk/%[^_]_(%d:%d)/Rate/%16s", devname, &major, &minor, name ) == 4) {
437 /* Show rate of change in sensor values in this interval */
439 ptr = DiskIO;
440 while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
441 ptr = ptr->next;
443 if ( !ptr ) {
444 print_error( "RECONFIGURE" );
445 fprintf( CurrentClient, "0\n" );
447 log_error( "Disk device disappeared" );
448 return;
451 if ( strcmp( name, "total" ) == 0 )
452 fprintf( CurrentClient, "%f\n", (float)( ptr->total.delta / timeInterval ) );
453 else if ( strcmp( name, "rio" ) == 0 )
454 fprintf( CurrentClient, "%f\n", (float)( ptr->rio.delta / timeInterval ) );
455 else if ( strcmp( name, "wio" ) == 0 )
456 fprintf( CurrentClient, "%f\n", (float)( ptr->wio.delta / timeInterval ) );
457 else if ( strcmp( name, "rblk" ) == 0 )
458 fprintf( CurrentClient, "%f\n", (float)( ptr->rblk.delta / ( timeInterval * 2 ) ) );
459 else if ( strcmp( name, "wblk" ) == 0 )
460 fprintf( CurrentClient, "%f\n", (float)( ptr->wblk.delta / ( timeInterval * 2 ) ) );
461 else {
462 fprintf( CurrentClient, "0\n" );
463 log_error( "Unknown disk device property \'%s\'", name );
466 else if(sscanf( cmd, "disk/%[^_]_(%d:%d)/Delta/%16s", devname, &major, &minor, name ) == 4) {
467 /* Show change in sensor values per this interval */
469 ptr = DiskIO;
470 while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
471 ptr = ptr->next;
473 if ( !ptr ) {
474 print_error( "RECONFIGURE" );
475 fprintf( CurrentClient, "0\n" );
477 log_error( "Disk device disappeared" );
478 return;
481 if ( strcmp( name, "total" ) == 0 )
482 fprintf( CurrentClient, "%lu\n", ptr->total.delta );
483 else if ( strcmp( name, "rio" ) == 0 )
484 fprintf( CurrentClient, "%lu\n", ptr->rio.delta );
485 else if ( strcmp( name, "wio" ) == 0 )
486 fprintf( CurrentClient, "%lu\n", ptr->wio.delta );
487 else if ( strcmp( name, "rblk" ) == 0 )
488 fprintf( CurrentClient, "%lu\n", ptr->rblk.delta );
489 else if ( strcmp( name, "wblk" ) == 0 )
490 fprintf( CurrentClient, "%lu\n", ptr->wblk.delta );
491 else if ( strcmp( name, "rtim" ) == 0 )
492 fprintf( CurrentClient, "%lu\n", ptr->rtim.delta );
493 else if ( strcmp( name, "wtim" ) == 0 )
494 fprintf( CurrentClient, "%lu\n", ptr->wtim.delta );
495 else {
496 fprintf( CurrentClient, "0\n" );
497 log_error( "Unknown disk device property \'%s\'", name );
500 else if(sscanf( cmd, "disk/%[^_]_(%d:%d)/%16s", devname, &major, &minor, name ) == 4) {
501 /* Show raw sensor values */
503 ptr = DiskIO;
504 while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
505 ptr = ptr->next;
507 if ( !ptr ) {
508 print_error( "RECONFIGURE" );
509 fprintf( CurrentClient, "0\n" );
511 log_error( "Disk device disappeared" );
512 return;
515 if ( strcmp( name, "ioqueue" ) == 0 )
516 fprintf( CurrentClient, "%u\n", ptr->ioqueue );
517 else {
518 fprintf( CurrentClient, "0\n" );
519 log_error( "Unknown disk device property \'%s\'", name );
524 void print26DiskIOInfo( const char* cmd ) {
525 int major, minor;
526 char devname[DISKDEVNAMELEN];
527 char name[ 17 ];
528 DiskIOInfo* ptr = DiskIO;
530 /* For now we print the same info regardless of whether it was a rate, delta, or raw monitor */
531 if(sscanf( cmd, "disk/%[^_]_(%d:%d)/Rate/%16s", devname, &major, &minor, name ) == 4) {
533 else if(sscanf( cmd, "disk/%[^_]_(%d:%d)/Delta/%16s", devname, &major, &minor, name ) == 4) {
535 else if(sscanf( cmd, "disk/%[^_]_(%d:%d)/%16s", devname, &major, &minor, name ) == 4) {
537 else {
538 fprintf( CurrentClient, "Dummy\t0\t0\t\n" );
539 log_error( "Request for unknown device property \'%s\'", cmd );
542 while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
543 ptr = ptr->next;
545 if ( !ptr ) {
546 /* Disk device has disappeared. Print a dummy answer. */
547 fprintf( CurrentClient, "Dummy\t0\t0\t\n" );
548 return;
551 /* remove trailing '?' */
552 name[ strlen( name ) - 1 ] = '\0';
554 if ( strcmp( name, "total" ) == 0 )
555 fprintf( CurrentClient, "Total accesses device %s (%d:%d)\t0\t0\t1/s\n",
556 devname, major, minor );
557 else if ( strcmp( name, "rio" ) == 0 )
558 fprintf( CurrentClient, "Read data device %s (%d:%d)\t0\t0\t1/s\n",
559 devname, major, minor );
560 else if ( strcmp( name, "wio" ) == 0 )
561 fprintf( CurrentClient, "Write data device %s (%d:%d)\t0\t0\t1/s\n",
562 devname, major, minor );
563 else if ( strcmp( name, "rblk" ) == 0 )
564 fprintf( CurrentClient, "Read accesses device %s (%d:%d)\t0\t0\tkBytes/s\n",
565 devname, major, minor );
566 else if ( strcmp( name, "wblk" ) == 0 )
567 fprintf( CurrentClient, "Write accesses device %s (%d:%d)\t0\t0\tkBytes/s\n",
568 devname, major, minor );
569 else if ( strcmp( name, "rtim" ) == 0 )
570 fprintf( CurrentClient, "# of milliseconds spent reading device %s (%d:%d)\t0\t0\ts\n",
571 devname, major, minor );
572 else if ( strcmp( name, "wtim" ) == 0 )
573 fprintf( CurrentClient, "# of milliseconds spent writing device %s (%d:%d)\t0\t0\ts\n",
574 devname, major, minor );
575 else if ( strcmp( name, "ioqueue" ) == 0 )
576 fprintf( CurrentClient, "# of I/Os currently in progress on device %s (%d:%d)\t0\t0\t\n",
577 devname, major, minor );
578 else {
579 fprintf( CurrentClient, "Dummy\t0\t0\t\n" );
580 log_error( "Request for unknown device property \'%s\'", name );