187925606bac66fd697d8ae92e9fd24eb045ed14
[test-ssd.git] / test-ssd-write.c
1 /*
2 * test-ssd-write.c
3 *
4 * Hard disk write workload: write random (or zeroed) data at random
5 * locations in a file, and optionally validate by re-reading some
6 * blocks after a while.
7 *
8 * Compile with:
9 * gcc -O2 -Wall -o test-ssd-write test-ssd-write.c
10 *
11 * Hard disks known to stop responding with this program (requires a
12 * warm machine reboot to get the drive to respond, as soft reset is not
13 * enough):
14 *
15 * Vendor Model Firmware Controller # drives tested HW
16 * Intel (Lenovo) SSDSC2BW180A3L LE1i SandForce 2281 1 (3.5 Debian kernel) x230
17 * Intel (Lenovo) SSDSC2BW180A3L LF1i SandForce 2281 3 (3.2 Debian kernel) x230
18 * (3.7.9 Arch kernel) x230
19 *
20 * We ensured that the problem is not coming from other parts by running
21 * this test on other SSD drives, which don't show this problem:
22 *
23 * Vendor Model Firmware Controller # drives tested
24 * Intel SSDSA2M160G2GC 2CV102HD Intel 1
25 * Intel SSDSA2CW300G310 ??????? Intel 1 (over USB on 3.8 kernel)
26 * Intel SSDSA2CT040G3 4PC10302 Intel 1 (3.2 kernel)
27 * Intel SSDSA2CT040G3 4PC10362 Intel 1 (3.2 kernel)
28 * Intel SSDSC2CT120A3K5 ??????? SandForce 2281 1 (on HP SmartArray P212 with 3.2 kernel)
29 * OCZ OCZ-VERTEX3 2.25 SF-2281 1 (3.7.9 Arch kernel)
30 *
31 * Under Linux (Debian, Ubuntu, various kernels), after about 5 minutes,
32 * we get this result:
33 *
34 * ata1.00: exception Emask 0x0 SAct 0x1 SErr 0x0 action 0x6 frozen
35 * ata1.00: failed command: WRITE FPDMA QUEUED
36 * ata1.00: cmd 61/28:00:a8:a9:7f/00:00:02:00:00/40 tag 0 ncq 20480 out
37 * res 40/00:00:00:4f:c2/00:00:00:00:00/00 Emask 0x4 (timeout)
38 * ata1.00: status: { DRDY }
39 * ata1.00: COMRESET failed (errno=-16)
40 * ata1.00: COMRESET failed (errno=-16)
41 *
42 * This happens with random data, zeroed data (-z), and has been tested
43 * with file sizes of 1MB, 200MB, 3.1GB and 21GB. The error happens with
44 * and without the validation (-v) option.
45 *
46 * Copyright 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
47 *
48 * Permission is hereby granted, free of charge, to any person obtaining
49 * a copy of this software and associated documentation files (the
50 * "Software"), to deal in the Software without restriction, including
51 * without limitation the rights to use, copy, modify, merge, publish,
52 * distribute, sublicense, and/or sell copies of the Software, and to
53 * permit persons to whom the Software is furnished to do so, subject to
54 * the following conditions:
55 *
56 * The above copyright notice and this permission notice shall be
57 * included in all copies or substantial portions of the Software.
58 *
59 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
60 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
61 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
62 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
63 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
64 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
65 * SOFTWARE.
66 */
67
68 #include <stdio.h>
69 #include <sys/types.h>
70 #include <sys/stat.h>
71 #include <fcntl.h>
72 #include <assert.h>
73 #include <stdlib.h>
74 #include <unistd.h>
75 #include <stdint.h>
76 #include <inttypes.h>
77 #include <string.h>
78
79 enum write_mode {
80 WRITE_RANDOM = 0,
81 WRITE_ZEROES,
82 };
83
84 static enum write_mode write_mode;
85 static int verify_mode;
86
87 #define PRINT_FREQ 100000
88 #define VALIDATE_FREQ 10000
89 #define BUFLEN 4096
90
91 static void rand_buf(char *buf, size_t buflen)
92 {
93 size_t i;
94
95 for (i = 0; i < buflen; i += sizeof(int)) {
96 union {
97 int i;
98 char c[sizeof(int)];
99 } u;
100 u.i = rand();
101 memcpy(&buf[i], u.c, sizeof(int));
102 }
103 }
104
105 static uint64_t validate(int fd, const char *validate_buf, off_t offset)
106 {
107 char buf[BUFLEN];
108 ssize_t rret;
109 off_t pos;
110 int i;
111 uint64_t diffcnt = 0;
112
113 if (!verify_mode)
114 return 0;
115
116 pos = lseek(fd, offset, SEEK_SET);
117 if (pos < 0) {
118 perror("lseek");
119 exit(EXIT_FAILURE);
120 }
121 rret = read(fd, buf, BUFLEN);
122 if (rret != BUFLEN) {
123 fprintf(stderr, "Error at read from offset: %zu\n",
124 pos);
125 perror("read");
126 exit(EXIT_FAILURE);
127 }
128 for (i = 0; i < BUFLEN; i++) {
129 if (buf[i] != validate_buf[i]) {
130 diffcnt++;
131 }
132 }
133 return diffcnt;
134 }
135
136 static int rand_write(int fd, size_t len)
137 {
138 off_t pos, offset;
139 uint64_t write_nr = 0;
140 ssize_t wret;
141 char buf[BUFLEN];
142 char validate_buf[BUFLEN];
143 off_t validate_offset = 0;
144 uint64_t valcount;
145 int ret;
146
147 memset(buf, 0, BUFLEN);
148
149 for (;;) {
150 if (len > UINT32_MAX) {
151 offset = (((size_t) rand() << 32) + (size_t) rand()) % len;
152 } else {
153 offset = rand() % len;
154 }
155
156 if ((offset >= validate_offset &&
157 offset < validate_offset + BUFLEN)
158 || (validate_offset >= offset &&
159 validate_offset < offset + BUFLEN)) {
160 /* Don't overwrite the range we want to validate. */
161 continue;
162 }
163 if (write_mode == WRITE_RANDOM)
164 rand_buf(buf, BUFLEN);
165 /* Save validation buffer and position */
166 if (write_nr % VALIDATE_FREQ == 0 && verify_mode) {
167 memcpy(validate_buf, buf, BUFLEN);
168 validate_offset = offset;
169 }
170 pos = lseek(fd, offset, SEEK_SET);
171 if (pos < 0) {
172 perror("lseek");
173 exit(EXIT_FAILURE);
174 }
175 wret = write(fd, buf, BUFLEN);
176 if (wret != BUFLEN) {
177 fprintf(stderr, "Error at write to offset: %zu\n",
178 pos);
179 perror("write");
180 exit(EXIT_FAILURE);
181 }
182
183 /*
184 * Advise that we won't be re-reading the blocks. This
185 * will ask the kernel to drop pages related to this
186 * file quickly from its page cache, thus forcing a read
187 * from disk.
188 */
189 ret = fdatasync(fd);
190 if (ret) {
191 perror("fdatasync");
192 exit(EXIT_FAILURE);
193 }
194 ret = posix_fadvise(fd, offset, BUFLEN, POSIX_FADV_DONTNEED);
195 if (ret) {
196 perror("posix_fadvise");
197 exit(EXIT_FAILURE);
198 }
199
200 write_nr++;
201 if (write_nr % PRINT_FREQ == 0) {
202 printf("Status: %" PRIu64 " writes.\n", write_nr);
203 }
204
205 /*
206 * Use the validation buffer and position saved
207 * VALIDATE_FREQ operations earlier.
208 */
209 if (write_nr % VALIDATE_FREQ == 0 && verify_mode) {
210 valcount = validate(fd, validate_buf, validate_offset);
211 if (valcount) {
212 printf("VALIDATION ERROR at offset %zu, %" PRIu64 " bytes differ\n",
213 validate_offset, valcount);
214 }
215 }
216 }
217 return 0;
218 }
219
220 int main(int argc, char **argv)
221 {
222 int fd, ret, i, seed;
223 size_t len;
224 off_t pos;
225 ssize_t wret;
226
227 if (argc < 4) {
228 printf("Usage: %s <output file> <len (64-bit)> <seed (32-bit)> <-z to write zeroes> <-v to verify written data>\n", argv[0]);
229 exit(EXIT_FAILURE);
230 }
231
232 fd = open(argv[1], O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
233 if (fd < 0) {
234 perror("open");
235 exit(EXIT_FAILURE);
236 }
237
238 len = atoll(argv[2]);
239 seed = atoi(argv[3]);
240 srand(seed);
241
242 printf("Creating file %s of length %zu, random seed %u\n", argv[1], len,
243 seed);
244
245 for (i = 4; i < argc; i++) {
246 if (strcmp(argv[i], "-z") == 0) {
247 write_mode = WRITE_ZEROES;
248 } else if (strcmp(argv[i], "-v") == 0) {
249 verify_mode = 1;
250 }
251 }
252
253 switch (write_mode) {
254 case WRITE_RANDOM:
255 printf("Generating random data\n");
256 break;
257 case WRITE_ZEROES:
258 printf("Filling with zeroes (compressible pattern)\n");
259 break;
260 default:
261 printf("Unsupported write-mode\n");
262 exit(EXIT_FAILURE);
263 }
264
265 if (verify_mode) {
266 printf("Verification mode activated.\n");
267 }
268
269 /* Grow file */
270 pos = lseek(fd, len - 1, SEEK_SET);
271 if (pos < 0) {
272 perror("lseek");
273 exit(EXIT_FAILURE);
274 }
275 wret = write(fd, "", 1);
276 if (wret < 0) {
277 perror("write");
278 exit(EXIT_FAILURE);
279 }
280
281 /* Advise the OS that we are performing random accesses */
282 ret = posix_fadvise(fd, 0, len, POSIX_FADV_RANDOM);
283 if (ret) {
284 perror("posix_fadvise");
285 exit(EXIT_FAILURE);
286 }
287
288 ret = rand_write(fd, len);
289 if (ret) {
290 exit(EXIT_FAILURE);
291 }
292
293 ret = close(fd);
294 assert(!ret);
295 exit(EXIT_SUCCESS);
296 }
This page took 0.03408 seconds and 3 git commands to generate.