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