Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * test-rawio.c - Random IO generator
- *
- * Tejun Heo <tj@kernel.org>
- */
- #define _GNU_SOURCE
- #define _FILE_OFFSET_BITS 64
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <ctype.h>
- #include <unistd.h>
- #include <inttypes.h>
- #include <sys/ioctl.h>
- #include <signal.h>
- #include <pthread.h>
- #include <time.h>
- #include <string.h>
- #include <sys/time.h>
- #include <libaio.h>
- #include <sys/user.h>
- #include <linux/fs.h>
- #define US_TO_NS 1000LLU
- #define MS_TO_NS 1000000LLU
- #define S_TO_NS 1000000000LLU
- static int dev_fd, blocks_per_rq, concurrency, do_aio, do_buffered, do_write;
- static int block_size;
- static uint64_t device_size, nr_blocks;
- static int exiting, nr_exited;
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- static uint64_t *dispenser_ar;
- static unsigned nr_succeeded, nr_failed;
- #define LAT_NR_SLOTS 1000 /* 1000 slots */
- #define LAT_CEIL_DFL (10 * MS_TO_NS) /* 10ms max, 10us resolution */
- #define LAT_CEIL_SCALE_BY 10 /* grow 10x when ceiling is hit */
- static int64_t lat_slots_a[LAT_NR_SLOTS];
- static int64_t lat_slots_b[LAT_NR_SLOTS];
- static int64_t *lat_slots = lat_slots_a;
- static int64_t lat_min = INT64_MAX;
- static int64_t lat_max;
- static int64_t lat_sum;
- static int64_t lat_nr_samples;
- static int64_t lat_ceil = LAT_CEIL_DFL;
- static struct timespec ts_200ms = { 0, 200 * 1000 * 1000 };
- /* monotonic clock in nsec */
- static int64_t clock_monotonic(void)
- {
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
- abort();
- return (int64_t)ts.tv_sec * S_TO_NS + ts.tv_nsec;
- }
- static void scale_lat_slots(int64_t lat)
- {
- int64_t new_ceil;
- int64_t *old_slots = lat_slots;
- int64_t *new_slots = old_slots == lat_slots_a ? lat_slots_b : lat_slots_a;
- int i;
- new_ceil = LAT_CEIL_DFL;
- while (new_ceil < lat)
- new_ceil *= 10;
- if (new_ceil == lat_ceil)
- return;
- if (lat_nr_samples) {
- int64_t old_step = lat_ceil / LAT_NR_SLOTS;
- int64_t old_pos = old_step / 2;
- for (i = 0; i < LAT_NR_SLOTS; i++) {
- int new_i = old_pos * LAT_NR_SLOTS / new_ceil;
- new_slots[new_i] += old_slots[i];
- old_pos += old_step;
- }
- memset(old_slots, 0, sizeof(old_slots[0]) * LAT_NR_SLOTS);
- lat_slots = new_slots;
- }
- lat_ceil = new_ceil;
- }
- static void account_lat(int64_t lat)
- {
- if (lat > lat_ceil)
- scale_lat_slots(lat);
- lat_slots[lat * LAT_NR_SLOTS / lat_ceil]++;
- lat_min = lat < lat_min ? lat : lat_min;
- lat_max = lat > lat_max ? lat : lat_max;
- lat_sum += lat;
- lat_nr_samples++;
- }
- static void calculate_lat(int nr_pcts, const double target_pcts[],
- int64_t pcts[])
- {
- int64_t step = lat_ceil / LAT_NR_SLOTS;
- int64_t cnt = 0;
- int slot, i;
- slot = LAT_NR_SLOTS;
- for (i = nr_pcts - 1; i >= 0; i--) {
- double target_pct = target_pcts[i];
- int target_cnt = lat_nr_samples * (100 - target_pct) / 100;
- while (cnt < target_cnt && slot > 0)
- cnt += lat_slots[--slot];
- pcts[i] = slot * step + step / 2;
- }
- }
- static void reset_lat(void)
- {
- int64_t prev_max = lat_max;
- lat_min = INT64_MAX;
- lat_max = 0;
- lat_sum = 0;
- lat_nr_samples = 0;
- memset(lat_slots, 0, sizeof(lat_slots[0]) * LAT_NR_SLOTS);
- scale_lat_slots(prev_max);
- }
- static void print_report(int64_t now, int force)
- {
- static unsigned report_succeeded, report_failed;
- static int64_t started_at = -1, report_tstmp = -1;
- const double target_pcts[] = { 90, 95, 99 };
- int64_t pcts[3];
- if (report_tstmp < 0) {
- started_at = now;
- report_tstmp = now;
- }
- if (!force && (now - report_tstmp < 5 * S_TO_NS ||
- nr_succeeded + nr_failed <=
- report_succeeded + report_failed + 10))
- return;
- calculate_lat(3, target_pcts, pcts);
- printf("\r%05.1lf: ok=%u err=%u iops=%.03lf avg/p90/p95/p99(ms)=%.03lf:%.03lf:%.03lf:%.03lf\n",
- ((double)now - started_at) / S_TO_NS,
- nr_succeeded - report_succeeded,
- nr_failed - report_failed,
- (double)(nr_succeeded - report_succeeded) / ((now - report_tstmp) / S_TO_NS),
- (double)lat_sum / lat_nr_samples / MS_TO_NS,
- (double)pcts[0] / MS_TO_NS,
- (double)pcts[1] / MS_TO_NS,
- (double)pcts[2] / MS_TO_NS);
- reset_lat();
- report_succeeded = nr_succeeded;
- report_failed = nr_failed;
- report_tstmp = now;
- }
- static void sigexit_handler(int dummy)
- {
- exiting = 1;
- }
- static uint64_t dispense_block(int idx)
- {
- while (1) {
- uint64_t block;
- int i;
- block = ((uint64_t)random() << 31 | random())
- % (nr_blocks - blocks_per_rq + 1);
- for (i = 0; i < concurrency; i++) {
- if (block + blocks_per_rq > dispenser_ar[i] &&
- block < dispenser_ar[i] + blocks_per_rq)
- break;
- }
- if (i == concurrency) {
- dispenser_ar[idx] = block;
- return block;
- }
- }
- }
- static void *do_rawio_fn(void *arg)
- {
- int idx = (int)(unsigned long)arg, my_exiting = 0;
- size_t bufsz = blocks_per_rq * block_size;
- char *buf;
- uint64_t block, started_at;
- ssize_t ret;
- if ((buf = malloc(bufsz + PAGE_SIZE)) == NULL) {
- perror("malloc");
- exit(1);
- }
- buf = (void *)((unsigned long)(buf + PAGE_SIZE-1) & ~(PAGE_SIZE-1));
- pthread_mutex_lock(&mutex);
- again:
- if (exiting || my_exiting) {
- nr_exited++;
- pthread_mutex_unlock(&mutex);
- return NULL;
- }
- block = dispense_block(idx);
- pthread_mutex_unlock(&mutex);
- started_at = clock_monotonic();
- ret = pread(dev_fd, buf, bufsz, block * block_size);
- if (do_write && ret == bufsz)
- ret = pwrite(dev_fd, buf, bufsz, block * block_size);
- if (ret != bufsz) {
- fprintf(stderr, "\rThread %02d: read/write failed on "
- "block %"PRIu64" ret=%zd errno=%d buf=%p\n",
- idx, block, ret, errno, buf);
- goto failed;
- }
- account_lat(clock_monotonic() - started_at);
- nr_succeeded++;
- pthread_mutex_lock(&mutex);
- goto again;
- failed:
- nr_failed++;
- my_exiting = 1;
- pthread_mutex_lock(&mutex);
- goto again;
- }
- static void *do_aio_fn(void *arg)
- {
- size_t bufsz = blocks_per_rq * block_size;
- static io_context_t io_ctx;
- int64_t *started_at;
- struct iocb *iocbs;
- struct io_event *ioevs;
- char **bufs;
- ssize_t ret;
- int i;
- ret = io_setup(concurrency, &io_ctx);
- if (ret < 0) {
- errno = -ret;
- perror("io_setup");
- exit(1);
- }
- if (!(started_at = calloc(concurrency, sizeof(int64_t))) ||
- !(iocbs = calloc(concurrency, sizeof(struct iocb))) ||
- !(ioevs = calloc(concurrency, sizeof(struct io_event))) ||
- !(bufs = calloc(concurrency, sizeof(char *)))) {
- perror("malloc");
- exit(1);
- }
- for (i = 0; i < concurrency; i++) {
- if (!(bufs[i] = malloc(bufsz + PAGE_SIZE))) {
- perror("malloc");
- exit(1);
- }
- bufs[i] = (void *)((unsigned long)(bufs[i] + PAGE_SIZE-1) &
- ~(PAGE_SIZE-1));
- }
- again:
- for (i = 0; i < concurrency; i++) {
- struct iocb *iocbp;
- if (iocbs[i].u.c.nbytes || exiting)
- continue;
- if (do_write)
- io_prep_pwrite(&iocbs[i], dev_fd, bufs[i], bufsz,
- dispense_block(i) * block_size);
- else
- io_prep_pread(&iocbs[i], dev_fd, bufs[i], bufsz,
- dispense_block(i) * block_size);
- started_at[i] = clock_monotonic();
- iocbp = &iocbs[i];
- if ((ret = io_submit(io_ctx, 1, &iocbp)) != 1) {
- errno = -ret;
- perror("io_submit");
- exit(1);
- }
- }
- ret = io_getevents(io_ctx, 1, concurrency, ioevs, &ts_200ms);
- if (ret < 0) {
- errno = -ret;
- perror("io_getevents");
- exit(1);
- }
- for (i = 0; i < ret; i++) {
- int iocb_idx = ioevs[i].obj - iocbs;
- if (ioevs[i].res < 0)
- nr_failed++;
- else
- nr_succeeded++;
- iocbs[iocb_idx].u.c.nbytes = 0;
- account_lat(clock_monotonic() - started_at[iocb_idx]);
- }
- if (!exiting)
- goto again;
- for (i = 0; i < concurrency; i++)
- if (iocbs[i].u.c.nbytes)
- goto again;
- nr_exited++;
- return NULL;
- }
- int main(int argc, char **argv)
- {
- struct stat sbuf;
- int i, summary_only;
- pthread_t *thrs;
- int64_t started_at, last_tstmp;
- unsigned last_succeeded = 0;
- double iops = 0;
- if (argc < 5) {
- fprintf(stderr,
- "Usage: test_rawio BLOCKDEV BLOCKS_PER_RQ CONCURRENCY [a|b](r|w) [s(ummary)|w(ait)]\n");
- return 1;
- }
- blocks_per_rq = atoi(argv[2]);
- concurrency = atoi(argv[3]);
- if (blocks_per_rq <= 0 || concurrency <= 0) {
- fprintf(stderr, "invalid parameters\n");
- return 1;
- }
- if (!(dispenser_ar = malloc(sizeof(dispenser_ar[0]) * concurrency)) ||
- !(thrs = malloc(sizeof(thrs[0]) * concurrency))) {
- perror("malloc");
- return 1;
- }
- memset(dispenser_ar, 0, sizeof(dispenser_ar[0]) * concurrency);
- if (tolower(argv[4][0]) == 'a')
- do_aio = 1;
- else if (tolower(argv[4][0] == 'b'))
- do_buffered = 1;
- do_write = tolower(argv[4][do_aio | do_buffered]) == 'w';
- summary_only = 0;
- if (argc >= 6 && strchr(argv[5], 's'))
- summary_only = 1;
- if (argc >= 6 && strchr(argv[5], 'w')) {
- char buf[64];
- printf("press enter to continue\n");
- fgets(buf, sizeof(buf), stdin);
- }
- dev_fd = open(argv[1], (do_write ? O_RDWR : O_RDONLY) |
- (do_buffered ? 0 : O_DIRECT));
- if (dev_fd < 0) {
- perror("open");
- return 1;
- }
- if (fstat(dev_fd, &sbuf) < 0) {
- perror("fstat");
- return 1;
- }
- if (S_ISBLK(sbuf.st_mode)) {
- if (ioctl(dev_fd, BLKSSZGET, &block_size) < 0 ||
- ioctl(dev_fd, BLKGETSIZE64, &device_size) < 0) {
- perror("ioctl");
- return 1;
- }
- } else {
- device_size = sbuf.st_size;
- block_size = sbuf.st_blksize;
- }
- nr_blocks = device_size / block_size;
- if (nr_blocks < 2 * concurrency * blocks_per_rq) {
- fprintf(stderr, "device too small\n");
- return 1;
- }
- if (!summary_only)
- printf("%s block_size=%d nr_blocks=%"PRIu64" (%.2lfGiB)\n",
- argv[1], block_size, nr_blocks,
- (double)device_size / (1 << 30));
- if (signal(SIGINT, sigexit_handler) == SIG_ERR) {
- perror("signal");
- return 1;
- }
- srandom(getpid());
- for (i = 0; i < (do_aio ? 1 : concurrency); i++) {
- if ((errno = pthread_create(&thrs[i], NULL,
- do_aio ? do_aio_fn : do_rawio_fn,
- (void *)(unsigned long)i))) {
- perror("pthread_create");
- return 1;
- }
- }
- started_at = last_tstmp = clock_monotonic();
- while (nr_exited < (do_aio ? 1 : concurrency)) {
- const char pgstr[] = "|/-\\";
- if (!summary_only) {
- int64_t now = clock_monotonic();
- double time_delta = ((double)now - last_tstmp) / S_TO_NS;
- double io_delta = nr_succeeded - last_succeeded;
- if (last_tstmp - started_at < S_TO_NS)
- iops = io_delta / time_delta;
- else
- iops = iops * 0.9 + io_delta / time_delta * 0.1;
- print_report(now, 0);
- printf("\r%s ok=%-8u err=%-8u iops=%7.03lf %s%c",
- do_aio ? "aio" : "thr",
- nr_succeeded, nr_failed, iops,
- exiting ? "exiting..." : "",
- pgstr[i++%(sizeof(pgstr)-1)]);
- last_tstmp = now;
- last_succeeded += io_delta;
- }
- fflush(stdout);
- nanosleep(&ts_200ms, NULL);
- }
- if (!summary_only)
- printf("\n");
- else
- print_report(clock_monotonic(), 1);
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement