1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2019 Red Hat, Inc. All Rights reserved.
5 * This program is used to do AIO write test. Each <-a size=N,off=M> field
6 * specifies an AIO write. More this kind of fields will be combined to a
7 * group of AIO write, then io_submit them together. All AIO write field can
8 * overlap or be sparse.
10 * After all AIO write operations done, each [size, off] content will be read
11 * back, verify if there's corruption.
13 * Before doing a series of AIO write, an optional ftruncate operation can be
14 * chosen. To truncate the file i_size to a specified location for testing.
18 #include <sys/types.h>
28 #define MAX_EVENT_NUM 128
30 * Use same fill pattern each time, so even overlap write won't get
33 #define IO_PATTERN 0x5a
35 void usage(char *progname)
37 fprintf(stderr, "usage: %s [-t truncsize ] <-a size=N,off=M [-a ...]> filename\n"
38 "\t-t truncsize: truncate the file to a special size before AIO wirte\n"
39 "\t-a: specify once AIO write size and startoff, this option can be specified many times, but less than 128\n"
40 "\t\tsize=N: AIO write size\n"
41 "\t\toff=M: AIO write startoff\n"
42 "e.g: %s -t 4608 -a size=4096,off=512 -a size=4096,off=4608 filename\n",
47 void dump_buffer(void *buf, off64_t offset, ssize_t len)
53 for (i = 0, p = (char *)buf; i < len; i += 16) {
56 if (i && !memcmp(p, p - 16, 16)) {
69 printf("%08llx ", (unsigned long long)offset + i);
70 for (j = 0; j < 16 && i + j < len; j++, p++)
73 for (j = 0; j < 16 && i + j < len; j++, s++) {
82 printf("%08llx\n", (unsigned long long)offset + i);
85 /* Parameters for once AIO write&verify testing */
89 unsigned long buf_size; /* the size of AIO write */
90 unsigned long offset; /* AIO write offset*/
93 struct io_params_node {
95 struct io_params *param;
96 struct io_params_node *next;
99 struct io_params_node *iop_head = NULL;
101 /* Suboptions of '-a' option */
107 char *const token[] = {
108 [BFS_OPT] = "size", /* buffer size of once AIO write */
109 [OFS_OPT] = "off", /* start offset */
114 * Combine each AIO write operation things to a linked list.
116 * Note: There won't be link structure free, due to the process will
117 * exit directly when hit a error)
119 static int add_io_param(unsigned long bs, unsigned long off)
121 struct io_params_node *io = NULL;
122 struct io_params_node **p = &iop_head;
124 io = malloc(sizeof(struct io_params_node));
128 io->param = malloc(sizeof(struct io_params));
132 io->param->buf_size = bs;
133 io->param->offset = off;
138 if (posix_memalign(&io->param->buf, getpagesize(), bs))
140 io->param->cmp_buf = malloc(bs);
141 if (io->param->cmp_buf == NULL)
143 memset(io->param->buf, IO_PATTERN, bs);
144 memset(io->param->cmp_buf, IO_PATTERN, bs);
158 perror("alloc memory");
162 static int parse_subopts(char *arg)
166 unsigned long bsize = 0;
167 unsigned long off = 0;
172 switch(getsubopt(&p, token, &value)) {
174 bsize = strtoul(value, &endp, 0);
177 off = strtoul(value, &endp, 0);
180 fprintf(stderr, "No match found for subopt %s\n",
186 return add_io_param(bsize, off);
189 static int io_write(int fd, int num_events)
192 struct io_params_node *p = iop_head;
194 struct io_event *evs;
195 struct io_context *ctx = NULL;
198 err = io_setup(num_events, &ctx);
204 iocbs = (struct iocb **)malloc(num_events * sizeof(struct iocb *));
210 evs = malloc(num_events * sizeof(struct io_event));
218 iocbs[i] = &(p->iocb);
219 io_prep_pwrite(&p->iocb, fd, p->param->buf,
220 p->param->buf_size, p->param->offset);
225 err = io_submit(ctx, num_events, iocbs);
226 if (err != num_events) {
227 fprintf(stderr, "error %s during %s\n",
233 err = io_getevents(ctx, num_events, num_events, evs, NULL);
234 if (err != num_events) {
235 fprintf(stderr, "error %s during %s\n",
241 /* Try to destroy at here, not necessary, so don't check result */
247 static int io_verify(int fd)
249 struct io_params_node *p = iop_head;
254 sret = pread(fd, p->param->buf,
255 p->param->buf_size, p->param->offset);
259 } else if (sret != p->param->buf_size) {
260 fprintf(stderr, "short read %zd was less than %zu\n",
261 sret, p->param->buf_size);
264 if (memcmp(p->param->buf,
265 p->param->cmp_buf, p->param->buf_size)) {
266 printf("Find corruption\n");
267 dump_buffer(p->param->buf, p->param->offset,
277 int main(int argc, char *argv[])
281 char *filename = NULL;
285 while ((c = getopt(argc, argv, "a:t:")) != -1) {
290 if (parse_subopts(optarg) == 0) {
293 fprintf(stderr, "Bad subopt %s\n", optarg);
298 tsize = strtoul(optarg, &endp, 0);
305 if (num_events > MAX_EVENT_NUM) {
306 fprintf(stderr, "Too many AIO events, %d > %d\n",
307 num_events, MAX_EVENT_NUM);
311 if (optind == argc - 1)
312 filename = argv[optind];
316 fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
323 if (ftruncate(fd, tsize)) {
329 if (io_write(fd, num_events) != 0) {
330 fprintf(stderr, "AIO write fails\n");
334 if (io_verify(fd) != 0) {
335 fprintf(stderr, "Data verification fails\n");