]> rtime.felk.cvut.cz Git - frescor/frsh.git/blob - resources/disk_bfq/mngr/diskbfq_th.c
7bb3516b2fab00142b40679fd3d5f14dcf2fa603
[frescor/frsh.git] / resources / disk_bfq / mngr / diskbfq_th.c
1 /*
2  * The only function of interest is:
3  *   double estimate_throughput(char *device, int streamers);
4  * @device: the device we want to test.
5  * @streamers: the number of sequential readers we're interested in.
6  */
7
8 #include <diskbfq_th.h>
9
10 /* given a device name, return its size in sectors */
11 static int64_t read_size(char *device)
12 {
13         int count, size, i;
14         int64_t rval;
15         char *path, *devname = device;
16         FILE *fp;
17
18         for (i = strlen(device) - 1; i >= 0; i--) {
19                 if (device[i] == '/') {
20                         devname = device + i + 1;
21                         break;
22                 }
23         }
24
25         size = sizeof(SYSFS_BASE) + strlen(devname) + sizeof("/size");
26         path = calloc(1, size);
27         if (!path)
28                 return -ENOMEM;
29
30         snprintf(path, size, SYSFS_BASE "%s/size", devname);
31         fp = fopen(path, "r");
32         if (!fp) {
33                 rval = -ENODEV;
34                 goto err;
35         }
36
37         count = fscanf(fp, "%llu", (unsigned long long*) &rval);
38         if (count != 1)
39                 rval = -EIO;
40
41         fclose(fp);
42 err:
43         free(path);
44         return rval;
45 }
46
47 /* reader: reads from the device until stop_all becomes true */
48 static void *reader_body(void *arg)
49 {
50         struct reader_data *rdata = arg;
51         off_t offset = rdata->offset * 512;
52         char buffer[2 * READ_BLKSIZE], *bufptr;
53         ssize_t count;
54         int page_size;
55
56         /* O_DIRECT needs page-aligned buffer/offset/blksize */
57         page_size = sysconf(_SC_PAGESIZE);
58         bufptr = (char *)(((uintptr_t)buffer + page_size) & ~(page_size - 1));
59
60         while (!*rdata->stop) {
61                 /* offset may lose alignment if a read ends prematurely */
62                 offset = (offset + page_size) & ~(page_size - 1);
63
64                 /* should not happen, wrap if we're too fast */
65                 if (offset > READ_ROOM * 512 - READ_BLKSIZE)
66                         offset = rdata->offset * 512;
67
68                 /* use pread() to share devfd among all the threads */
69                 count = pread(rdata->devfd, bufptr, READ_BLKSIZE, offset);
70                 if (count < 0) {
71                         rdata->error = errno;
72                         break;
73                 }
74                 rdata->completed += count / 512;
75                 offset += count;
76         }
77
78         dprintf("reader: completed = %lld, error = %d\n",
79                 rdata->completed, rdata->error);
80         return NULL;
81 }
82
83 /* wait for the termination of all the readers (collecting errors, if any) */
84 static void wait_all(struct dprof_data *ddata, int *error)
85 {
86         struct reader_data *rdata;
87         int i;
88
89         ddata->stop_all = 1;
90         for (i = 0; i < ddata->streamers; i++) {
91                 rdata = ddata->reader_data + i;
92                 if (!rdata->started)
93                         continue;
94                 pthread_join(rdata->id, NULL);
95                 if (rdata->error && error)
96                         *error = rdata->error;
97         }
98 }
99
100 /* create and start all the readers */
101 static struct dprof_data *create_readers(int streamers, int devfd,
102                                          int64_t devsize)
103 {
104         struct dprof_data *ddata;
105         struct reader_data *rdata;
106         int i, error = 0;
107         off_t step;
108
109         ddata = calloc(1, sizeof(struct reader_data) * streamers +
110                        sizeof(struct dprof_data));
111         if (!ddata)
112                 return NULL;
113
114         ddata->streamers = streamers;
115
116         /* space the readers evenly over the disk surface */
117         step = (devsize - streamers * READ_ROOM) / streamers;
118
119         for (i = 0; i < streamers; i++) {
120                 rdata = ddata->reader_data + i;
121                 rdata->offset = step * i;
122                 rdata->stop = &ddata->stop_all;
123                 rdata->devfd = devfd;
124
125                 error = pthread_create(&rdata->id, NULL, reader_body, rdata);
126                 if (error) {
127                         wait_all(ddata, NULL);
128                         free(ddata);
129                         return NULL;
130                 }
131                 rdata->started = 1;
132         }
133
134         return ddata;
135 }
136
137 /* return the throughput, in KiB/s */
138 static double calc_throughput(struct dprof_data *ddata,
139                               struct timeval *begin,
140                               struct timeval *end)
141 {
142         struct reader_data *rdata;
143         double total = 0, delta;
144         int i;
145
146         for (i = 0; i < ddata->streamers; i++) {
147                 rdata = ddata->reader_data + i;
148                 total += rdata->completed;
149         }
150
151         delta = end->tv_usec - begin->tv_usec;
152         delta += (end->tv_sec - begin->tv_sec) * 1000000.0;
153
154         /* total is in sectors, delta in usecs, convert to KiB/s */
155         return total / delta * (512 * 1000000 / 1024);
156 }
157
158 double estimate_throughput(char *device, int streamers)
159 {
160         struct timeval begin, end;
161         struct dprof_data *ddata;
162         double throughput = -1;
163         int64_t devsize;
164         int devfd, error = 0;
165
166         /* get device size */
167         devsize = read_size(device);
168         if (devsize < 0)
169                 return -1;
170
171         /* open the device */
172         devfd = open(device, O_RDONLY | O_DIRECT);
173         if (devfd < 0)
174                 return -1;
175
176         /* let the good times roll... */
177         gettimeofday(&begin, NULL);
178
179         /* start all the threads */
180         ddata = create_readers(streamers, devfd, devsize);
181         if (!ddata)
182                 goto err;
183
184         /* let them read */
185         sleep(MEAS_INTERVAL);
186
187         /* wait for their completion */
188         wait_all(ddata, &error);
189
190         /* this is the end... */
191         gettimeofday(&end, NULL);
192
193         if (!error)
194                 throughput = calc_throughput(ddata, &begin, &end);
195         free(ddata);
196
197 err:
198         close(devfd);
199
200         return throughput;
201 }
202