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