diskutil.c revision ccb0fa24f0ff37f9270754ea5a1b66de9fd7053e
1#include <stdio.h> 2#include <string.h> 3#include <sys/time.h> 4#include <sys/types.h> 5#include <sys/stat.h> 6#include <dirent.h> 7#include <libgen.h> 8#include <math.h> 9 10#include "fio.h" 11 12static int last_majdev, last_mindev; 13 14static struct list_head disk_list = LIST_HEAD_INIT(disk_list); 15 16static int get_io_ticks(struct disk_util *du, struct disk_util_stat *dus) 17{ 18 unsigned in_flight; 19 char line[256]; 20 FILE *f; 21 char *p; 22 int ret; 23 24 dprint(FD_DISKUTIL, "open stat file: %s\n", du->path); 25 26 f = fopen(du->path, "r"); 27 if (!f) 28 return 1; 29 30 p = fgets(line, sizeof(line), f); 31 if (!p) { 32 fclose(f); 33 return 1; 34 } 35 36 dprint(FD_DISKUTIL, "%s: %s", du->path, p); 37 38 ret = sscanf(p, "%u %u %llu %u %u %u %llu %u %u %u %u\n", &dus->ios[0], 39 &dus->merges[0], &dus->sectors[0], 40 &dus->ticks[0], &dus->ios[1], 41 &dus->merges[1], &dus->sectors[1], 42 &dus->ticks[1], &in_flight, 43 &dus->io_ticks, &dus->time_in_queue); 44 fclose(f); 45 dprint(FD_DISKUTIL, "%s: stat read ok? %d\n", du->path, ret == 1); 46 return ret != 11; 47} 48 49static void update_io_tick_disk(struct disk_util *du) 50{ 51 struct disk_util_stat __dus, *dus, *ldus; 52 struct timeval t; 53 54 if (get_io_ticks(du, &__dus)) 55 return; 56 57 dus = &du->dus; 58 ldus = &du->last_dus; 59 60 dus->sectors[0] += (__dus.sectors[0] - ldus->sectors[0]); 61 dus->sectors[1] += (__dus.sectors[1] - ldus->sectors[1]); 62 dus->ios[0] += (__dus.ios[0] - ldus->ios[0]); 63 dus->ios[1] += (__dus.ios[1] - ldus->ios[1]); 64 dus->merges[0] += (__dus.merges[0] - ldus->merges[0]); 65 dus->merges[1] += (__dus.merges[1] - ldus->merges[1]); 66 dus->ticks[0] += (__dus.ticks[0] - ldus->ticks[0]); 67 dus->ticks[1] += (__dus.ticks[1] - ldus->ticks[1]); 68 dus->io_ticks += (__dus.io_ticks - ldus->io_ticks); 69 dus->time_in_queue += (__dus.time_in_queue - ldus->time_in_queue); 70 71 fio_gettime(&t, NULL); 72 du->msec += mtime_since(&du->time, &t); 73 memcpy(&du->time, &t, sizeof(t)); 74 memcpy(ldus, &__dus, sizeof(__dus)); 75} 76 77void update_io_ticks(void) 78{ 79 struct list_head *entry; 80 struct disk_util *du; 81 82 dprint(FD_DISKUTIL, "update io ticks\n"); 83 84 list_for_each(entry, &disk_list) { 85 du = list_entry(entry, struct disk_util, list); 86 update_io_tick_disk(du); 87 } 88} 89 90static struct disk_util *disk_util_exists(int major, int minor) 91{ 92 struct list_head *entry; 93 struct disk_util *du; 94 95 list_for_each(entry, &disk_list) { 96 du = list_entry(entry, struct disk_util, list); 97 98 if (major == du->major && minor == du->minor) 99 return du; 100 } 101 102 return NULL; 103} 104 105static void disk_util_add(int majdev, int mindev, char *path) 106{ 107 struct disk_util *du, *__du; 108 struct list_head *entry; 109 110 dprint(FD_DISKUTIL, "add maj/min %d/%d: %s\n", majdev, mindev, path); 111 112 du = malloc(sizeof(*du)); 113 memset(du, 0, sizeof(*du)); 114 INIT_LIST_HEAD(&du->list); 115 sprintf(du->path, "%s/stat", path); 116 du->name = strdup(basename(path)); 117 du->sysfs_root = path; 118 du->major = majdev; 119 du->minor = mindev; 120 121 list_for_each(entry, &disk_list) { 122 __du = list_entry(entry, struct disk_util, list); 123 124 dprint(FD_DISKUTIL, "found %s in list\n", __du->name); 125 126 if (!strcmp(du->name, __du->name)) { 127 free(du->name); 128 free(du); 129 return; 130 } 131 } 132 133 dprint(FD_DISKUTIL, "add %s to list\n", du->name); 134 135 fio_gettime(&du->time, NULL); 136 get_io_ticks(du, &du->last_dus); 137 138 list_add_tail(&du->list, &disk_list); 139} 140 141static int check_dev_match(int majdev, int mindev, char *path) 142{ 143 int major, minor; 144 char line[256], *p; 145 FILE *f; 146 147 f = fopen(path, "r"); 148 if (!f) { 149 perror("open path"); 150 return 1; 151 } 152 153 p = fgets(line, sizeof(line), f); 154 if (!p) { 155 fclose(f); 156 return 1; 157 } 158 159 if (sscanf(p, "%u:%u", &major, &minor) != 2) { 160 fclose(f); 161 return 1; 162 } 163 164 if (majdev == major && mindev == minor) { 165 fclose(f); 166 return 0; 167 } 168 169 fclose(f); 170 return 1; 171} 172 173static int find_block_dir(int majdev, int mindev, char *path) 174{ 175 struct dirent *dir; 176 struct stat st; 177 int found = 0; 178 DIR *D; 179 180 D = opendir(path); 181 if (!D) 182 return 0; 183 184 while ((dir = readdir(D)) != NULL) { 185 char full_path[256]; 186 187 if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) 188 continue; 189 190 sprintf(full_path, "%s/%s", path, dir->d_name); 191 192 if (!strcmp(dir->d_name, "dev")) { 193 if (!check_dev_match(majdev, mindev, full_path)) { 194 found = 1; 195 break; 196 } 197 } 198 199 if (lstat(full_path, &st) == -1) { 200 perror("stat"); 201 break; 202 } 203 204 if (!S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) 205 continue; 206 207 found = find_block_dir(majdev, mindev, full_path); 208 if (found) { 209 strcpy(path, full_path); 210 break; 211 } 212 } 213 214 closedir(D); 215 return found; 216} 217 218static void __init_disk_util(struct thread_data *td, struct fio_file *f) 219{ 220 struct stat st; 221 char foo[PATH_MAX], tmp[PATH_MAX]; 222 struct disk_util *du; 223 int mindev, majdev; 224 char *p; 225 226 if (!lstat(f->file_name, &st)) { 227 if (S_ISBLK(st.st_mode)) { 228 majdev = major(st.st_rdev); 229 mindev = minor(st.st_rdev); 230 } else if (S_ISCHR(st.st_mode)) { 231 majdev = major(st.st_rdev); 232 mindev = minor(st.st_rdev); 233 if (fio_lookup_raw(st.st_rdev, &majdev, &mindev)) 234 return; 235 } else if (S_ISFIFO(st.st_mode)) 236 return; 237 else { 238 majdev = major(st.st_dev); 239 mindev = minor(st.st_dev); 240 } 241 } else { 242 /* 243 * must be a file, open "." in that path 244 */ 245 strncpy(foo, f->file_name, PATH_MAX - 1); 246 p = dirname(foo); 247 if (stat(p, &st)) { 248 perror("disk util stat"); 249 return; 250 } 251 252 majdev = major(st.st_dev); 253 mindev = minor(st.st_dev); 254 } 255 256 dprint(FD_DISKUTIL, "%s belongs to maj/min %d/%d\n", f->file_name, 257 majdev, mindev); 258 259 du = disk_util_exists(majdev, mindev); 260 if (du) { 261 if (td->o.ioscheduler && !td->sysfs_root) 262 td->sysfs_root = strdup(du->sysfs_root); 263 264 return; 265 } 266 267 /* 268 * for an fs without a device, we will repeatedly stat through 269 * sysfs which can take oodles of time for thousands of files. so 270 * cache the last lookup and compare with that before going through 271 * everything again. 272 */ 273 if (mindev == last_mindev && majdev == last_majdev) 274 return; 275 276 last_mindev = mindev; 277 last_majdev = majdev; 278 279 sprintf(foo, "/sys/block"); 280 if (!find_block_dir(majdev, mindev, foo)) 281 return; 282 283 /* 284 * If there's a ../queue/ directory there, we are inside a partition. 285 * Check if that is the case and jump back. For loop/md/dm etc we 286 * are already in the right spot. 287 */ 288 sprintf(tmp, "%s/../queue", foo); 289 if (!stat(tmp, &st)) { 290 p = dirname(foo); 291 sprintf(tmp, "%s/queue", p); 292 if (stat(tmp, &st)) { 293 log_err("unknown sysfs layout\n"); 294 return; 295 } 296 strncpy(tmp, p, PATH_MAX - 1); 297 sprintf(foo, "%s", tmp); 298 } 299 300 if (td->o.ioscheduler && !td->sysfs_root) 301 td->sysfs_root = strdup(foo); 302 303 disk_util_add(majdev, mindev, foo); 304} 305 306void init_disk_util(struct thread_data *td) 307{ 308 struct fio_file *f; 309 unsigned int i; 310 311 if (!td->o.do_disk_util || 312 (td->io_ops->flags & (FIO_DISKLESSIO | FIO_NODISKUTIL))) 313 return; 314 315 for_each_file(td, f, i) 316 __init_disk_util(td, f); 317} 318 319void show_disk_util(void) 320{ 321 struct disk_util_stat *dus; 322 struct list_head *entry, *next; 323 struct disk_util *du; 324 double util; 325 326 if (list_empty(&disk_list)) 327 return; 328 329 log_info("\nDisk stats (read/write):\n"); 330 331 list_for_each(entry, &disk_list) { 332 du = list_entry(entry, struct disk_util, list); 333 dus = &du->dus; 334 335 util = (double) 100 * du->dus.io_ticks / (double) du->msec; 336 if (util > 100.0) 337 util = 100.0; 338 339 log_info(" %s: ios=%u/%u, merge=%u/%u, ticks=%u/%u, " 340 "in_queue=%u, util=%3.2f%%\n", du->name, 341 dus->ios[0], dus->ios[1], 342 dus->merges[0], dus->merges[1], 343 dus->ticks[0], dus->ticks[1], 344 dus->time_in_queue, util); 345 } 346 347 /* 348 * now free the list 349 */ 350 list_for_each_safe(entry, next, &disk_list) { 351 list_del(entry); 352 du = list_entry(entry, struct disk_util, list); 353 free(du->name); 354 free(du); 355 } 356} 357