]> granicus.if.org Git - sysstat/commitdiff
iostat: Add flush I/O statistics
authorKonstantin Khlebnikov <khlebnikov@yandex-team.ru>
Sun, 15 Dec 2019 09:29:28 +0000 (12:29 +0300)
committerKonstantin Khlebnikov <khlebnikov@yandex-team.ru>
Mon, 16 Dec 2019 14:12:36 +0000 (17:12 +0300)
Statistics for flush operations have been added into Linux 5.5

Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b6866318657717c8914673a6394894d12bc9ff5e
iostat.c
iostat.h
man/iostat.in

index 41a1dbd5b67f04b0df183df5a45590175536a97d..89bf2d0500ebbcb25068a529589406e43246fa05 100644 (file)
--- a/iostat.c
+++ b/iostat.c
@@ -333,19 +333,20 @@ int read_sysfs_file_stat(char *filename, struct io_stats *ios)
        FILE *fp;
        struct io_stats sdev;
        int i;
-       unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks;
+       unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
        unsigned long rd_ios, rd_merges_or_rd_sec, wr_ios, wr_merges;
        unsigned long rd_sec_or_wr_ios, wr_sec, rd_ticks_or_wr_sec;
-       unsigned long dc_ios, dc_merges, dc_sec, dc_ticks;
+       unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
 
        /* Try to read given stat file */
        if ((fp = fopen(filename, "r")) == NULL)
                return -1;
 
-       i = fscanf(fp, "%lu %lu %lu %lu %lu %lu %lu %u %u %u %u %lu %lu %lu %lu",
+       i = fscanf(fp, "%lu %lu %lu %lu %lu %lu %lu %u %u %u %u %lu %lu %lu %u %lu %u",
                   &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec,
                   &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
-                  &dc_ios, &dc_merges, &dc_sec, &dc_ticks);
+                  &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
+                  &fl_ios, &fl_ticks);
 
        memset(&sdev, 0, sizeof(struct io_stats));
 
@@ -363,13 +364,19 @@ int read_sysfs_file_stat(char *filename, struct io_stats *ios)
                sdev.tot_ticks  = tot_ticks;
                sdev.rq_ticks   = rq_ticks;
 
-               if (i == 15) {
+               if (i >= 15) {
                        /* Discard I/O */
                        sdev.dc_ios     = dc_ios;
                        sdev.dc_merges  = dc_merges;
                        sdev.dc_sectors = dc_sec;
                        sdev.dc_ticks   = dc_ticks;
                }
+
+               if (i >= 17) {
+                       /* Flush I/O */
+                       sdev.fl_ios     = fl_ios;
+                       sdev.fl_ticks   = fl_ticks;
+               }
        }
        else if (i == 4) {
                /* Partition without extended statistics */
@@ -604,10 +611,10 @@ void read_diskstats_stat(int curr)
        struct io_device *d;
        struct io_stats sdev;
        int i;
-       unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks;
+       unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
        unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios;
        unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec;
-       unsigned long dc_ios, dc_merges, dc_sec, dc_ticks;
+       unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
        unsigned int major, minor;
 
        memset(&sdev, 0, sizeof(struct io_stats));
@@ -617,12 +624,13 @@ void read_diskstats_stat(int curr)
 
        while (fgets(line, sizeof(line), fp) != NULL) {
 
-               /* major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq dcio dcmerge dcsect dcuse*/
-               i = sscanf(line, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %u %u %lu %lu %lu %lu",
+               /* major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq dcio dcmerge dcsect dcuse flio fltm */
+               i = sscanf(line, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %u %u %lu %lu %lu %u %lu %u",
                           &major, &minor, dev_name,
                           &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec,
                           &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
-                          &dc_ios, &dc_merges, &dc_sec, &dc_ticks);
+                          &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
+                          &fl_ios, &fl_ticks);
 
                if (i >= 14) {
                        sdev.rd_ios     = rd_ios;
@@ -637,13 +645,19 @@ void read_diskstats_stat(int curr)
                        sdev.tot_ticks  = tot_ticks;
                        sdev.rq_ticks   = rq_ticks;
 
-                       if (i == 18) {
+                       if (i >= 18) {
                                /* Discard I/O */
                                sdev.dc_ios     = dc_ios;
                                sdev.dc_merges  = dc_merges;
                                sdev.dc_sectors = dc_sec;
                                sdev.dc_ticks   = dc_ticks;
                        }
+
+                       if (i >= 20) {
+                               /* Flush I/O */
+                               sdev.fl_ios     = fl_ios;
+                               sdev.fl_ticks   = fl_ticks;
+                       }
                }
                else if (i == 7) {
                        /* Partition without extended statistics */
@@ -683,7 +697,8 @@ void compute_device_groups_stats(int curr, struct io_device *d, struct io_device
        if (!DISPLAY_UNFILTERED(flags)) {
                if (!d->dev_stats[curr]->rd_ios &&
                    !d->dev_stats[curr]->wr_ios &&
-                   !d->dev_stats[curr]->dc_ios)
+                   !d->dev_stats[curr]->dc_ios &&
+                   !d->dev_stats[curr]->fl_ios)
                        return;
        }
 
@@ -699,6 +714,8 @@ void compute_device_groups_stats(int curr, struct io_device *d, struct io_device
        g->dev_stats[curr]->dc_merges  += d->dev_stats[curr]->dc_merges;
        g->dev_stats[curr]->dc_sectors += d->dev_stats[curr]->dc_sectors;
        g->dev_stats[curr]->dc_ticks   += d->dev_stats[curr]->dc_ticks;
+       g->dev_stats[curr]->fl_ios     += d->dev_stats[curr]->fl_ios;
+       g->dev_stats[curr]->fl_ticks   += d->dev_stats[curr]->fl_ticks;
        g->dev_stats[curr]->ios_pgr    += d->dev_stats[curr]->ios_pgr;
        g->dev_stats[curr]->tot_ticks  += d->dev_stats[curr]->tot_ticks;
        g->dev_stats[curr]->rq_ticks   += d->dev_stats[curr]->rq_ticks;
@@ -904,7 +921,7 @@ void write_disk_stat_header(int *fctr, int *tab, int hpart)
                                      spc, units);
                        }
                        if ((hpart == 4) || !hpart) {
-                              printf("  aqu-sz  %%util");
+                              printf("     f/s f_await  aqu-sz  %%util");
                        }
                }
        }
@@ -1068,6 +1085,12 @@ void write_plain_ext_stat(unsigned long long itv, int fctr, int hpart,
                                  xios->darqsz / 2);
                }
                if ((hpart == 4) || !hpart) {
+                       /* f/s */
+                       cprintf_f(NO_UNIT, 1, 7, 2,
+                                 S_VALUE(ioj->fl_ios, ioi->fl_ios, itv));
+                       /* f_await */
+                       cprintf_f(NO_UNIT, 1, 7, 2,
+                                 xios->f_await);
                        /* aqu-sz */
                        cprintf_f(NO_UNIT, 1, 7, 2,
                                  S_VALUE(ioj->rq_ticks, ioi->rq_ticks, itv) / 1000.0);
@@ -1147,10 +1170,11 @@ void write_json_ext_stat(int tab, unsigned long long itv, int fctr,
                       S_VALUE(ioj->rq_ticks, ioi->rq_ticks, itv) / 1000.0);
        }
        else {
-               printf("\"r/s\": %.2f, \"w/s\": %.2f, \"d/s\": %.2f, ",
+               printf("\"r/s\": %.2f, \"w/s\": %.2f, \"d/s\": %.2f, \"f/s\": %.2f, ",
                       S_VALUE(ioj->rd_ios, ioi->rd_ios, itv),
                       S_VALUE(ioj->wr_ios, ioi->wr_ios, itv),
-                      S_VALUE(ioj->dc_ios, ioi->dc_ios, itv));
+                      S_VALUE(ioj->dc_ios, ioi->dc_ios, itv),
+                      S_VALUE(ioj->fl_ios, ioi->fl_ios, itv));
                if (DISPLAY_MEGABYTES(flags)) {
                        sprintf(line, "\"rMB/s\": %%.2f, \"wMB/s\": %%.2f, \"dMB/s\": %%.2f, ");
                }
@@ -1166,7 +1190,7 @@ void write_json_ext_stat(int tab, unsigned long long itv, int fctr,
                       xios->dsectors /= fctr);
                printf("\"rrqm/s\": %.2f, \"wrqm/s\": %.2f, \"drqm/s\": %.2f, "
                       "\"rrqm\": %.2f, \"wrqm\": %.2f, \"drqm\": %.2f, "
-                      "\"r_await\": %.2f, \"w_await\": %.2f, \"d_await\": %.2f, "
+                      "\"r_await\": %.2f, \"w_await\": %.2f, \"d_await\": %.2f, \"f_await\": %.2f, "
                       "\"rareq-sz\": %.2f, \"wareq-sz\": %.2f, \"dareq-sz\": %.2f, "
                       "\"aqu-sz\": %.2f, ",
                       S_VALUE(ioj->rd_merges, ioi->rd_merges, itv),
@@ -1178,6 +1202,7 @@ void write_json_ext_stat(int tab, unsigned long long itv, int fctr,
                       xios->r_await,
                       xios->w_await,
                       xios->d_await,
+                      xios->f_await,
                       xios->rarqsz / 2,
                       xios->warqsz / 2,
                       xios->darqsz / 2,
@@ -1233,6 +1258,7 @@ void write_ext_stat(unsigned long long itv, int fctr, int hpart,
         */
 
        if ((hpart == 4) || !hpart || DISPLAY_SHORT_OUTPUT(flags)) {
+               /* Origin (unmerged) flush operations are counted as writes */
                sdc.nr_ios    = ioi->rd_ios + ioi->wr_ios + ioi->dc_ios;
                sdp.nr_ios    = ioj->rd_ios + ioj->wr_ios + ioj->dc_ios;
 
@@ -1310,6 +1336,12 @@ void write_ext_stat(unsigned long long itv, int fctr, int hpart,
                                      (ioi->dc_sectors - ioj->dc_sectors) / ((double) (ioi->dc_ios - ioj->dc_ios)) :
                                      0.0;
                }
+               if ((hpart == 4) || !hpart) {
+                       /* f_await */
+                       xios.f_await = (ioi->fl_ios - ioj->fl_ios) ?
+                                      (ioi->fl_ticks - ioj->fl_ticks) /
+                                      ((double) (ioi->fl_ios - ioj->fl_ios)) : 0.0;
+               }
        }
 
        if (DISPLAY_JSON_OUTPUT(flags)) {
@@ -1680,6 +1712,7 @@ void write_stats(int curr, struct tm *rectime, int skip)
                                                "rd_ios=%lu rd_merges=%lu rd_ticks=%u "
                                                "wr_ios=%lu wr_merges=%lu wr_ticks=%u "
                                                "dc_ios=%lu dc_merges=%lu dc_ticks=%u "
+                                               "fl_ios=%lu fl_ticks=%u "
                                                "ios_pgr=%u tot_ticks=%u "
                                                "rq_ticks=%u }\n",
                                                dname,
@@ -1697,6 +1730,8 @@ void write_stats(int curr, struct tm *rectime, int skip)
                                                ioi->dc_ios,
                                                ioi->dc_merges,
                                                ioi->dc_ticks,
+                                               ioi->fl_ios,
+                                               ioi->fl_ticks,
                                                ioi->ios_pgr,
                                                ioi->tot_ticks,
                                                ioi->rq_ticks);
index 2a0359ed85b7c96f62d1bbebff54f04be93d8367..1636306a5c832acd7d934ac0f72ec18ef95bf21a 100644 (file)
--- a/iostat.h
+++ b/iostat.h
@@ -92,12 +92,16 @@ struct io_stats {
        unsigned long dc_ios            __attribute__ ((packed));
        /* # of discard requests merged */
        unsigned long dc_merges         __attribute__ ((packed));
+       /* # of flush requests issued to the device */
+       unsigned long fl_ios            __attribute__ ((packed));
        /* Time of read requests in queue */
        unsigned int  rd_ticks          __attribute__ ((packed));
        /* Time of write requests in queue */
        unsigned int  wr_ticks          __attribute__ ((packed));
        /* Time of discard requests in queue */
        unsigned int  dc_ticks          __attribute__ ((packed));
+       /* Time of flush requests in queue */
+       unsigned int  fl_ticks          __attribute__ ((packed));
        /* # of I/Os in progress */
        unsigned int  ios_pgr           __attribute__ ((packed));
        /* # of ticks total (for this device) for I/O */
@@ -133,6 +137,8 @@ struct ext_io_stats {
        double w_await;
        /* d_await */
        double d_await;
+       /* f_await */
+       double f_await;
        /* rsec/s */
        double rsectors;
        /* wsec/s */
index 8e991a624c192a1be8d55075bc9346ddf6e3d0a1..437161a495c01343c00d08e126fe2696e6da2b60 100644 (file)
@@ -216,6 +216,12 @@ The number (after merges) of write requests completed per second for the device.
 .RS
 The number (after merges) of discard requests completed per second for the device.
 
+.RE
+.B f/s
+.RS
+The number (after merges) of flush requests completed per second for the device.
+Origin (unmerged) flush operations are counted as writes (likely with zero size).
+
 .RE
 .B sec/s (kB/s, MB/s)
 .RS
@@ -326,6 +332,13 @@ The average time (in milliseconds) for discard requests issued to the device
 to be served. This includes the time spent by the requests in queue and
 the time spent servicing them.
 
+.RE
+.B f_await
+.RS
+The average time (in milliseconds) for flush requests issued to the device
+to be served. Flush requests are issued one at a time, thus flush operation
+could be twice longer: wait current flush request, then issue and wait next.
+
 .RE
 .B aqu-sz
 .RS