1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2017 by Delphix. All rights reserved.
14  */
15 
16 #include <stdint.h>
17 #include <string.h>
18 #include "file_common.h"
19 
20 /*
21  * The following sample was derived from real-world data
22  * of a production Oracle database.
23  */
24 static const uint64_t size_distribution[] = {
25 	0,
26 	1499018,
27 	352084,
28 	1503485,
29 	4206227,
30 	5626657,
31 	5387001,
32 	3733756,
33 	2233094,
34 	874652,
35 	238635,
36 	81434,
37 	33357,
38 	13106,
39 	2009,
40 	1,
41 	23660,
42 };
43 
44 
45 static uint64_t distribution_n;
46 
47 static uint8_t randbuf[BLOCKSZ];
48 
49 static void
50 rwc_pwrite(int fd, const void *buf, size_t nbytes, off_t offset)
51 {
52 	size_t nleft = nbytes;
53 	ssize_t nwrite = 0;
54 
55 	nwrite = pwrite(fd, buf, nbytes, offset);
56 	if (nwrite < 0) {
57 		perror("pwrite");
58 		exit(EXIT_FAILURE);
59 	}
60 
61 	nleft -= nwrite;
62 	if (nleft != 0) {
63 		(void) fprintf(stderr, "warning: pwrite: "
64 		    "wrote %zu out of %zu bytes\n",
65 		    (nbytes - nleft), nbytes);
66 	}
67 }
68 
69 static void
70 fillbuf(char *buf)
71 {
72 	uint64_t rv = lrand48() % distribution_n;
73 	uint64_t sum = 0;
74 
75 	uint64_t i;
76 	for (i = 0;
77 	    i < sizeof (size_distribution) / sizeof (size_distribution[0]);
78 	    i++) {
79 		sum += size_distribution[i];
80 		if (rv < sum)
81 			break;
82 	}
83 
84 	memcpy(buf, randbuf, BLOCKSZ);
85 	if (i == 0)
86 		memset(buf, 0, BLOCKSZ - 10);
87 	else if (i < 16)
88 		memset(buf, 0, BLOCKSZ - i * 512 + 256);
89 	/*LINTED: E_BAD_PTR_CAST_ALIGN*/
90 	((uint32_t *)buf)[0] = lrand48();
91 }
92 
93 static void
94 exit_usage(void)
95 {
96 	(void) puts("usage: randwritecomp [-s] file [nwrites]");
97 	exit(EXIT_FAILURE);
98 }
99 
100 static void
101 sequential_writes(int fd, char *buf, uint64_t nblocks, int64_t n)
102 {
103 	for (int64_t i = 0; n == -1 || i < n; i++) {
104 		fillbuf(buf);
105 
106 		static uint64_t j = 0;
107 		if (j == 0)
108 			j = lrand48() % nblocks;
109 		rwc_pwrite(fd, buf, BLOCKSZ, j * BLOCKSZ);
110 		j++;
111 		if (j >= nblocks)
112 			j = 0;
113 	}
114 }
115 
116 static void
117 random_writes(int fd, char *buf, uint64_t nblocks, int64_t n)
118 {
119 	for (int64_t i = 0; n == -1 || i < n; i++) {
120 		fillbuf(buf);
121 		rwc_pwrite(fd, buf, BLOCKSZ, (lrand48() % nblocks) * BLOCKSZ);
122 	}
123 }
124 
125 int
126 main(int argc, char *argv[])
127 {
128 	int fd, err;
129 	char *filename = NULL;
130 	char buf[BLOCKSZ];
131 	struct stat ss;
132 	uint64_t nblocks;
133 	int64_t n = -1;
134 	int sequential = 0;
135 
136 	if (argc < 2)
137 		exit_usage();
138 
139 	argv++;
140 	if (strcmp("-s", argv[0]) == 0) {
141 		sequential = 1;
142 		argv++;
143 	}
144 
145 	if (argv[0] == NULL)
146 		exit_usage();
147 	else
148 		filename = argv[0];
149 
150 	argv++;
151 	if (argv[0] != NULL)
152 		n = strtoull(argv[0], NULL, 0);
153 
154 	fd = open(filename, O_RDWR|O_CREAT, 0666);
155 	if (fd == -1) {
156 		(void) fprintf(stderr, "open(%s) failed: %s\n", filename,
157 		    strerror(errno));
158 		exit(EXIT_FAILURE);
159 	}
160 	err = fstat(fd, &ss);
161 	if (err != 0) {
162 		(void) fprintf(stderr,
163 		    "error: fstat returned error code %d\n", err);
164 		exit(EXIT_FAILURE);
165 	}
166 
167 	nblocks = ss.st_size / BLOCKSZ;
168 	if (nblocks == 0) {
169 		(void) fprintf(stderr, "error: "
170 		    "file is too small (min allowed size is %d bytes)\n",
171 		    BLOCKSZ);
172 		exit(EXIT_FAILURE);
173 	}
174 
175 	srand48(getpid());
176 	for (int i = 0; i < BLOCKSZ; i++)
177 		randbuf[i] = lrand48();
178 
179 	distribution_n = 0;
180 	for (uint64_t i = 0;
181 	    i < sizeof (size_distribution) / sizeof (size_distribution[0]);
182 	    i++) {
183 		distribution_n += size_distribution[i];
184 	}
185 
186 	if (sequential)
187 		sequential_writes(fd, buf, nblocks, n);
188 	else
189 		random_writes(fd, buf, nblocks, n);
190 
191 	return (0);
192 }
193