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 */
24static 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
45static uint64_t distribution_n;
46
47static uint8_t randbuf[BLOCKSZ];
48
49static void
50rwc_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
69static void
70fillbuf(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
93static void
94exit_usage(void)
95{
96	(void) puts("usage: randwritecomp [-s] file [nwrites]");
97	exit(EXIT_FAILURE);
98}
99
100static void
101sequential_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
116static void
117random_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
125int
126main(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