Advertisement
Guest User

test-rawio.c

a guest
Jun 28th, 2020
390
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.91 KB | None | 0 0
  1. /*
  2.  * test-rawio.c - Random IO generator
  3.  *
  4.  * Tejun Heo <tj@kernel.org>
  5.  */
  6. #define _GNU_SOURCE
  7. #define _FILE_OFFSET_BITS 64
  8.  
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <fcntl.h>
  14. #include <errno.h>
  15. #include <ctype.h>
  16. #include <unistd.h>
  17. #include <inttypes.h>
  18. #include <sys/ioctl.h>
  19. #include <signal.h>
  20. #include <pthread.h>
  21. #include <time.h>
  22. #include <string.h>
  23. #include <sys/time.h>
  24. #include <libaio.h>
  25.  
  26. #include <sys/user.h>
  27. #include <linux/fs.h>
  28.  
  29. #define US_TO_NS    1000LLU
  30. #define MS_TO_NS    1000000LLU
  31. #define S_TO_NS     1000000000LLU
  32.  
  33. static int dev_fd, blocks_per_rq, concurrency, do_aio, do_buffered, do_write;
  34. static int block_size;
  35. static uint64_t device_size, nr_blocks;
  36.  
  37. static int exiting, nr_exited;
  38.  
  39. static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  40. static uint64_t *dispenser_ar;
  41. static unsigned nr_succeeded, nr_failed;
  42.  
  43. #define LAT_NR_SLOTS    1000        /* 1000 slots */
  44. #define LAT_CEIL_DFL    (10 * MS_TO_NS) /* 10ms max, 10us resolution */
  45. #define LAT_CEIL_SCALE_BY 10        /* grow 10x when ceiling is hit */
  46.  
  47. static int64_t lat_slots_a[LAT_NR_SLOTS];
  48. static int64_t lat_slots_b[LAT_NR_SLOTS];
  49. static int64_t *lat_slots = lat_slots_a;
  50. static int64_t lat_min = INT64_MAX;
  51. static int64_t lat_max;
  52. static int64_t lat_sum;
  53. static int64_t lat_nr_samples;
  54. static int64_t lat_ceil = LAT_CEIL_DFL;
  55.  
  56. static struct timespec ts_200ms = { 0, 200 * 1000 * 1000 };
  57.  
  58. /* monotonic clock in nsec */
  59. static int64_t clock_monotonic(void)
  60. {
  61.     struct timespec ts;
  62.  
  63.     if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
  64.         abort();
  65.  
  66.     return (int64_t)ts.tv_sec * S_TO_NS + ts.tv_nsec;
  67. }
  68.  
  69. static void scale_lat_slots(int64_t lat)
  70. {
  71.     int64_t new_ceil;
  72.     int64_t *old_slots = lat_slots;
  73.     int64_t *new_slots = old_slots == lat_slots_a ? lat_slots_b : lat_slots_a;
  74.     int i;
  75.  
  76.     new_ceil = LAT_CEIL_DFL;
  77.     while (new_ceil < lat)
  78.         new_ceil *= 10;
  79.  
  80.     if (new_ceil == lat_ceil)
  81.         return;
  82.  
  83.     if (lat_nr_samples) {
  84.         int64_t old_step = lat_ceil / LAT_NR_SLOTS;
  85.         int64_t old_pos = old_step / 2;
  86.  
  87.         for (i = 0; i < LAT_NR_SLOTS; i++) {
  88.             int new_i = old_pos * LAT_NR_SLOTS / new_ceil;
  89.  
  90.             new_slots[new_i] += old_slots[i];
  91.             old_pos += old_step;
  92.         }
  93.  
  94.         memset(old_slots, 0, sizeof(old_slots[0]) * LAT_NR_SLOTS);
  95.         lat_slots = new_slots;
  96.     }
  97.  
  98.     lat_ceil = new_ceil;
  99. }
  100.  
  101. static void account_lat(int64_t lat)
  102. {
  103.     if (lat > lat_ceil)
  104.         scale_lat_slots(lat);
  105.  
  106.     lat_slots[lat * LAT_NR_SLOTS / lat_ceil]++;
  107.  
  108.     lat_min = lat < lat_min ? lat : lat_min;
  109.     lat_max = lat > lat_max ? lat : lat_max;
  110.     lat_sum += lat;
  111.     lat_nr_samples++;
  112. }
  113.  
  114. static void calculate_lat(int nr_pcts, const double target_pcts[],
  115.               int64_t pcts[])
  116. {
  117.     int64_t step = lat_ceil / LAT_NR_SLOTS;
  118.     int64_t cnt = 0;
  119.     int slot, i;
  120.  
  121.     slot = LAT_NR_SLOTS;
  122.  
  123.     for (i = nr_pcts - 1; i >= 0; i--) {
  124.         double target_pct = target_pcts[i];
  125.         int target_cnt = lat_nr_samples * (100 - target_pct) / 100;
  126.  
  127.         while (cnt < target_cnt && slot > 0)
  128.             cnt += lat_slots[--slot];
  129.  
  130.         pcts[i] = slot * step + step / 2;
  131.     }
  132. }
  133.  
  134. static void reset_lat(void)
  135. {
  136.     int64_t prev_max = lat_max;
  137.  
  138.     lat_min = INT64_MAX;
  139.     lat_max = 0;
  140.     lat_sum = 0;
  141.     lat_nr_samples = 0;
  142.     memset(lat_slots, 0, sizeof(lat_slots[0]) * LAT_NR_SLOTS);
  143.  
  144.     scale_lat_slots(prev_max);
  145. }
  146.  
  147. static void print_report(int64_t now, int force)
  148. {
  149.     static unsigned report_succeeded, report_failed;
  150.     static int64_t started_at = -1, report_tstmp = -1;
  151.     const double target_pcts[] = { 90, 95, 99 };
  152.     int64_t pcts[3];
  153.  
  154.     if (report_tstmp < 0) {
  155.         started_at = now;
  156.         report_tstmp = now;
  157.     }
  158.  
  159.     if (!force && (now - report_tstmp < 5 * S_TO_NS ||
  160.                nr_succeeded + nr_failed <=
  161.                report_succeeded + report_failed + 10))
  162.         return;
  163.  
  164.     calculate_lat(3, target_pcts, pcts);
  165.  
  166.     printf("\r%05.1lf: ok=%u err=%u iops=%.03lf avg/p90/p95/p99(ms)=%.03lf:%.03lf:%.03lf:%.03lf\n",
  167.            ((double)now - started_at) / S_TO_NS,
  168.            nr_succeeded - report_succeeded,
  169.            nr_failed - report_failed,
  170.            (double)(nr_succeeded - report_succeeded) / ((now - report_tstmp) / S_TO_NS),
  171.            (double)lat_sum / lat_nr_samples / MS_TO_NS,
  172.            (double)pcts[0] / MS_TO_NS,
  173.            (double)pcts[1] / MS_TO_NS,
  174.            (double)pcts[2] / MS_TO_NS);
  175.  
  176.     reset_lat();
  177.     report_succeeded = nr_succeeded;
  178.     report_failed = nr_failed;
  179.     report_tstmp = now;
  180. }
  181.  
  182. static void sigexit_handler(int dummy)
  183. {
  184.     exiting = 1;
  185. }
  186.  
  187. static uint64_t dispense_block(int idx)
  188. {
  189.     while (1) {
  190.         uint64_t block;
  191.         int i;
  192.         block = ((uint64_t)random() << 31 | random())
  193.             % (nr_blocks - blocks_per_rq + 1);
  194.         for (i = 0; i < concurrency; i++) {
  195.             if (block + blocks_per_rq > dispenser_ar[i] &&
  196.                 block < dispenser_ar[i] + blocks_per_rq)
  197.                 break;
  198.         }
  199.         if (i == concurrency) {
  200.             dispenser_ar[idx] = block;
  201.             return block;
  202.         }
  203.     }
  204. }
  205.  
  206. static void *do_rawio_fn(void *arg)
  207. {
  208.     int idx = (int)(unsigned long)arg, my_exiting = 0;
  209.     size_t bufsz = blocks_per_rq * block_size;
  210.     char *buf;
  211.     uint64_t block, started_at;
  212.     ssize_t ret;
  213.  
  214.     if ((buf = malloc(bufsz + PAGE_SIZE)) == NULL) {
  215.         perror("malloc");
  216.         exit(1);
  217.     }
  218.  
  219.     buf = (void *)((unsigned long)(buf + PAGE_SIZE-1) & ~(PAGE_SIZE-1));
  220.  
  221.     pthread_mutex_lock(&mutex);
  222.  again:
  223.     if (exiting || my_exiting) {
  224.         nr_exited++;
  225.         pthread_mutex_unlock(&mutex);
  226.         return NULL;
  227.     }
  228.     block = dispense_block(idx);
  229.     pthread_mutex_unlock(&mutex);
  230.  
  231.     started_at = clock_monotonic();
  232.  
  233.     ret = pread(dev_fd, buf, bufsz, block * block_size);
  234.     if (do_write && ret == bufsz)
  235.         ret = pwrite(dev_fd, buf, bufsz, block * block_size);
  236.  
  237.     if (ret != bufsz) {
  238.         fprintf(stderr, "\rThread %02d: read/write failed on "
  239.             "block %"PRIu64" ret=%zd errno=%d buf=%p\n",
  240.             idx, block, ret, errno, buf);
  241.         goto failed;
  242.     }
  243.  
  244.     account_lat(clock_monotonic() - started_at);
  245.  
  246.     nr_succeeded++;
  247.     pthread_mutex_lock(&mutex);
  248.     goto again;
  249.  
  250.  failed:
  251.     nr_failed++;
  252.     my_exiting = 1;
  253.     pthread_mutex_lock(&mutex);
  254.     goto again;
  255. }
  256.  
  257. static void *do_aio_fn(void *arg)
  258. {
  259.     size_t bufsz = blocks_per_rq * block_size;
  260.     static io_context_t io_ctx;
  261.     int64_t *started_at;
  262.     struct iocb *iocbs;
  263.     struct io_event *ioevs;
  264.     char **bufs;
  265.     ssize_t ret;
  266.     int i;
  267.  
  268.     ret = io_setup(concurrency, &io_ctx);
  269.     if (ret < 0) {
  270.         errno = -ret;
  271.         perror("io_setup");
  272.         exit(1);
  273.     }
  274.  
  275.     if (!(started_at = calloc(concurrency, sizeof(int64_t))) ||
  276.         !(iocbs = calloc(concurrency, sizeof(struct iocb))) ||
  277.         !(ioevs = calloc(concurrency, sizeof(struct io_event))) ||
  278.         !(bufs = calloc(concurrency, sizeof(char *)))) {
  279.         perror("malloc");
  280.         exit(1);
  281.     }
  282.  
  283.     for (i = 0; i < concurrency; i++) {
  284.         if (!(bufs[i] = malloc(bufsz + PAGE_SIZE))) {
  285.             perror("malloc");
  286.             exit(1);
  287.         }
  288.         bufs[i] = (void *)((unsigned long)(bufs[i] + PAGE_SIZE-1) &
  289.                    ~(PAGE_SIZE-1));
  290.     }
  291. again:
  292.     for (i = 0; i < concurrency; i++) {
  293.         struct iocb *iocbp;
  294.  
  295.         if (iocbs[i].u.c.nbytes || exiting)
  296.             continue;
  297.  
  298.         if (do_write)
  299.             io_prep_pwrite(&iocbs[i], dev_fd, bufs[i], bufsz,
  300.                        dispense_block(i) * block_size);
  301.         else
  302.             io_prep_pread(&iocbs[i], dev_fd, bufs[i], bufsz,
  303.                       dispense_block(i) * block_size);
  304.  
  305.         started_at[i] = clock_monotonic();
  306.  
  307.         iocbp = &iocbs[i];
  308.         if ((ret = io_submit(io_ctx, 1, &iocbp)) != 1) {
  309.             errno = -ret;
  310.             perror("io_submit");
  311.             exit(1);
  312.         }
  313.     }
  314.  
  315.     ret = io_getevents(io_ctx, 1, concurrency, ioevs, &ts_200ms);
  316.     if (ret < 0) {
  317.         errno = -ret;
  318.         perror("io_getevents");
  319.         exit(1);
  320.     }
  321.  
  322.     for (i = 0; i < ret; i++) {
  323.         int iocb_idx = ioevs[i].obj - iocbs;
  324.  
  325.         if (ioevs[i].res < 0)
  326.             nr_failed++;
  327.         else
  328.             nr_succeeded++;
  329.  
  330.         iocbs[iocb_idx].u.c.nbytes = 0;
  331.         account_lat(clock_monotonic() - started_at[iocb_idx]);
  332.     }
  333.  
  334.     if (!exiting)
  335.         goto again;
  336.  
  337.     for (i = 0; i < concurrency; i++)
  338.         if (iocbs[i].u.c.nbytes)
  339.             goto again;
  340.     nr_exited++;
  341.     return NULL;
  342. }
  343.  
  344. int main(int argc, char **argv)
  345. {
  346.     struct stat sbuf;
  347.     int i, summary_only;
  348.     pthread_t *thrs;
  349.     int64_t started_at, last_tstmp;
  350.     unsigned last_succeeded = 0;
  351.     double iops = 0;
  352.  
  353.     if (argc < 5) {
  354.         fprintf(stderr,
  355.         "Usage: test_rawio BLOCKDEV BLOCKS_PER_RQ CONCURRENCY [a|b](r|w) [s(ummary)|w(ait)]\n");
  356.         return 1;
  357.     }
  358.  
  359.     blocks_per_rq = atoi(argv[2]);
  360.     concurrency = atoi(argv[3]);
  361.  
  362.     if (blocks_per_rq <= 0 || concurrency <= 0) {
  363.         fprintf(stderr, "invalid parameters\n");
  364.         return 1;
  365.     }
  366.  
  367.     if (!(dispenser_ar = malloc(sizeof(dispenser_ar[0]) * concurrency)) ||
  368.         !(thrs = malloc(sizeof(thrs[0]) * concurrency))) {
  369.         perror("malloc");
  370.         return 1;
  371.     }
  372.     memset(dispenser_ar, 0, sizeof(dispenser_ar[0]) * concurrency);
  373.  
  374.     if (tolower(argv[4][0]) == 'a')
  375.         do_aio = 1;
  376.     else if (tolower(argv[4][0] == 'b'))
  377.         do_buffered = 1;
  378.     do_write = tolower(argv[4][do_aio | do_buffered]) == 'w';
  379.  
  380.     summary_only = 0;
  381.     if (argc >= 6 && strchr(argv[5], 's'))
  382.         summary_only = 1;
  383.  
  384.     if (argc >= 6 && strchr(argv[5], 'w')) {
  385.         char buf[64];
  386.         printf("press enter to continue\n");
  387.         fgets(buf, sizeof(buf), stdin);
  388.     }
  389.  
  390.     dev_fd = open(argv[1], (do_write ? O_RDWR : O_RDONLY) |
  391.               (do_buffered ? 0 : O_DIRECT));
  392.     if (dev_fd < 0) {
  393.         perror("open");
  394.         return 1;
  395.     }
  396.  
  397.     if (fstat(dev_fd, &sbuf) < 0) {
  398.         perror("fstat");
  399.         return 1;
  400.     }
  401.  
  402.     if (S_ISBLK(sbuf.st_mode)) {
  403.         if (ioctl(dev_fd, BLKSSZGET, &block_size) < 0 ||
  404.             ioctl(dev_fd, BLKGETSIZE64, &device_size) < 0) {
  405.             perror("ioctl");
  406.             return 1;
  407.         }
  408.     } else {
  409.         device_size = sbuf.st_size;
  410.         block_size = sbuf.st_blksize;
  411.     }
  412.  
  413.     nr_blocks = device_size / block_size;
  414.  
  415.     if (nr_blocks < 2 * concurrency * blocks_per_rq) {
  416.         fprintf(stderr, "device too small\n");
  417.         return 1;
  418.     }
  419.  
  420.     if (!summary_only)
  421.         printf("%s block_size=%d nr_blocks=%"PRIu64" (%.2lfGiB)\n",
  422.                argv[1], block_size, nr_blocks,
  423.                (double)device_size / (1 << 30));
  424.  
  425.     if (signal(SIGINT, sigexit_handler) == SIG_ERR) {
  426.         perror("signal");
  427.         return 1;
  428.     }
  429.  
  430.     srandom(getpid());
  431.  
  432.     for (i = 0; i < (do_aio ? 1 : concurrency); i++) {
  433.         if ((errno = pthread_create(&thrs[i], NULL,
  434.                         do_aio ? do_aio_fn : do_rawio_fn,
  435.                         (void *)(unsigned long)i))) {
  436.             perror("pthread_create");
  437.             return 1;
  438.         }
  439.     }
  440.  
  441.     started_at = last_tstmp = clock_monotonic();
  442.  
  443.     while (nr_exited < (do_aio ? 1 : concurrency)) {
  444.         const char pgstr[] = "|/-\\";
  445.  
  446.         if (!summary_only) {
  447.             int64_t now = clock_monotonic();
  448.             double time_delta = ((double)now - last_tstmp) / S_TO_NS;
  449.             double io_delta = nr_succeeded - last_succeeded;
  450.  
  451.             if (last_tstmp - started_at < S_TO_NS)
  452.                 iops = io_delta / time_delta;
  453.             else
  454.                 iops = iops * 0.9 + io_delta / time_delta * 0.1;
  455.  
  456.             print_report(now, 0);
  457.  
  458.             printf("\r%s ok=%-8u err=%-8u iops=%7.03lf %s%c",
  459.                    do_aio ? "aio" : "thr",
  460.                    nr_succeeded, nr_failed, iops,
  461.                    exiting ? "exiting..." : "",
  462.                    pgstr[i++%(sizeof(pgstr)-1)]);
  463.  
  464.             last_tstmp = now;
  465.             last_succeeded += io_delta;
  466.         }
  467.  
  468.         fflush(stdout);
  469.         nanosleep(&ts_200ms, NULL);
  470.     }
  471.  
  472.     if (!summary_only)
  473.         printf("\n");
  474.     else
  475.         print_report(clock_monotonic(), 1);
  476.  
  477.     return 0;
  478. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement