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