File: disk.c

package info (click to toggle)
topline 0.6-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 112 kB
  • sloc: ansic: 675; makefile: 18; sh: 11
file content (140 lines) | stat: -rw-r--r-- 3,303 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <string.h>
#include <time.h>
#include "topline.h"

typedef struct
{
    unsigned long rd;
    unsigned long wr;
    const char *name;
    int minor;
    short major;
    char part;
} bdevstat_t;

static bdevstat_t bdev[4096];
static struct timespec t0;

static FILE *ds;

void init_disks()
{
    ds = fopen("/proc/diskstats", "re");
    if (!ds)
        die("Can't open /proc/diskstats: %m\n");
}

static const char *bdprefs[] =
{
    "nvme",
    "sd",
    "hd",
    "mmcblk",
    "loop",
    "nbd",
    "sr",
    "fd",
    "md",
    "dm",
    // pmem is usually dax, which doesn't update stats here
    0
};

static const char *get_name(const char *inst)
{
    for (const char **pref=bdprefs; *pref; pref++)
        if (!strncmp(inst, *pref, strlen(*pref)))
            return *pref;
    return strdup(inst);
}

void do_disks()
{
    char buf[4096];

    rewind(ds);

    struct timespec t1;
    if (clock_gettime(CLOCK_MONOTONIC, &t1))
        die("Broken clock: %m\n");
    int64_t td = (t1.tv_sec-t0.tv_sec)*NANO + t1.tv_nsec-t0.tv_nsec;
    if (!td)
        td = 1;
    t0 = t1;

    unsigned int prev_major = -1;
    bdevstat_t *bs = &bdev[-1];

    while (fgets(buf, sizeof(buf), ds))
    {
        char namebuf[64];
        unsigned int major, minor;
        unsigned long rd, wr;

        if (sscanf(buf, "%u %u %63s %*u %*u %*u %lu %*u %*u %*u %lu",
            &major, &minor, namebuf, &rd, &wr) != 5)
        {
            die("A line of /proc/diskstats is corrupted: ā€œ%sā€\n", buf);
        }

        if (major > 65535)
            die("Invalid major:minor : %u:%u\n", major, minor);

        if (!rd && !wr)
            continue;

#define BDEND &bdev[ARRAYSZ(bdev)]
        if (bs < BDEND && bs[1].major==major && bs[1].minor==minor)
            bs++;
        else for (bs = bdev; bs < BDEND; bs++)
            if ((bs->major==major && bs->minor==minor) || !bs->name)
                break;
        if (bs >= BDEND)
            die("Too many block devices.\n");
        if (!bs->name)
        {
            bs->major = major;
            bs->minor = minor;
            if (!strncmp(namebuf, "mmcblk", 6) && strstr(namebuf, "boot"))
            {
                // Early boot partitions are not marked as such.
                bs->name = "mmcboot";
                bs->part = 1;
                continue;
            }

            if (!strncmp(namebuf, "mtdblock", 8))
            {
                // Raw legacy MTD -- special uses only.
                bs->name = "mtd";
                bs->part = 1;
                continue;
            }

            bs->name=get_name(namebuf);
            sprintf(namebuf, "/sys/dev/block/%u:%u/partition", major, minor);
            if (!access(namebuf, F_OK))
            {
                bs->part = 1;
                continue;
            }
            bs->part = 0;
        }
        else if (bs->part)
            continue;

        int r = ((int64_t)rd-bs->rd)*RES*1000000/td;
        int w = ((int64_t)wr-bs->wr)*RES*1000000/td;
        bs->rd = rd;
        bs->wr = wr;

        if (prev_major != major)
        {
            fprintf(log_output, prev_major==-1 ? "%s(" : ")%s(", bs->name);
            prev_major = major;
        }
        write_dual(r, w);
    }
    if (prev_major!=-1)
        fprintf(log_output, ") ");
}