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