Initial import
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 22 Feb 2013 20:08:43 +0000 (15:08 -0500)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 22 Feb 2013 20:08:43 +0000 (15:08 -0500)
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Makefile [new file with mode: 0644]
test-ssd-write.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..b555aea
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+all:
+       ${CC} -O2 -Wall -o test-ssd-write test-ssd-write.c
+
+.PHONY: clean
+clean:
+       rm test-ssd-write
diff --git a/test-ssd-write.c b/test-ssd-write.c
new file mode 100644 (file)
index 0000000..2bce0fc
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * test-ssd-write.c
+ *
+ * Hard disk write workload: write random (or zeroed) data at random
+ * locations in a file, and optionally validate by re-reading some
+ * blocks after a while.
+ *
+ * Compile with:
+ * gcc -O2 -Wall -o test-ssd-write test-ssd-write.c
+ *
+ * Hard disks known to stop responding with this program (requires a
+ * warm machine reboot to get the drive to respond, as soft reset is not
+ * enough):
+ *
+ * Vendor         Model          Firmware   Controller     # drives tested
+ * Intel (Lenovo) SSDSC2BW180A3L LE1i       SandForce 2281 1
+ * Intel (Lenovo) SSDSC2BW180A3L LF1i       SandForce 2281 2
+ *
+ * We ensured that the problem is not coming from other parts by running
+ * this test on other SSD drives, which don't show this problem:
+ * 
+ * Vendor         Model          Firmware   Controller     # drives tested
+ * Intel          SSDSA2M160G2GC 2CV102HD   Intel          1
+ *
+ * Under Linux (Debian, Ubuntu, various kernels), after about 5 minutes,
+ * we get this result:
+ *
+ * ata1.00: exception Emask 0x0 SAct 0x1 SErr 0x0 action 0x6 frozen
+ * ata1.00: failed command: WRITE FPDMA QUEUED
+ * ata1.00: cmd 61/28:00:a8:a9:7f/00:00:02:00:00/40 tag 0 ncq 20480 out
+ *          res 40/00:00:00:4f:c2/00:00:00:00:00/00 Emask 0x4 (timeout)
+ * ata1.00: status: { DRDY }
+ * ata1.00: COMRESET failed (errno=-16)
+ * ata1.00: COMRESET failed (errno=-16)
+ *
+ * This happens with random data, zeroed data (-z), and has been tested
+ * with file sizes of 1MB, 200MB, 3.1GB and 21GB. The error happens with
+ * and without the validation (-v) option.
+ *
+ * Copyright 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+
+enum write_mode {
+       WRITE_RANDOM,
+       WRITE_ZEROES,
+};
+
+static enum write_mode write_mode;
+static int verify_mode;
+
+#define PRINT_FREQ     100000
+#define VALIDATE_FREQ  10000
+#define BUFLEN         4096
+
+static void rand_buf(char *buf, size_t buflen)
+{
+       size_t i;
+
+       for (i = 0; i < buflen; i += sizeof(int)) {
+               union {
+                       int i;
+                       char c[sizeof(int)];
+               } u;
+               u.i = rand();
+               memcpy(&buf[i], u.c, sizeof(int));
+       }
+}
+
+static uint64_t validate(int fd, const char *validate_buf, off_t offset)
+{
+       char buf[BUFLEN];
+       ssize_t rret;
+       off_t pos;
+       int i;
+       uint64_t diffcnt = 0;
+
+       if (!verify_mode)
+               return 0;
+
+       pos = lseek(fd, offset, SEEK_SET);
+       if (pos < 0) {
+               perror("lseek");
+               exit(EXIT_FAILURE);
+       }
+       rret = read(fd, buf, BUFLEN);
+       if (rret != BUFLEN) {
+               fprintf(stderr, "Error at read from offset: %zu\n",
+                       pos);
+               perror("read");
+               exit(EXIT_FAILURE);
+       }
+       for (i = 0; i < BUFLEN; i++) {
+               if (buf[i] != validate_buf[i]) {
+                       diffcnt++;
+               }
+       }
+       return diffcnt;
+}
+
+static int rand_write(int fd, size_t len)
+{
+       off_t pos, offset;
+       uint64_t write_nr = 0;
+       ssize_t wret;
+       char buf[BUFLEN];
+       char validate_buf[BUFLEN];
+       off_t validate_offset = 0;
+       uint64_t valcount;
+       int ret;
+
+       memset(buf, 0, BUFLEN);
+
+       for (;;) {
+               if (len > UINT32_MAX) {
+                       offset = (((size_t) rand() << 32) + (size_t) rand()) % len;
+               } else {
+                       offset = rand() % len;
+               }
+
+               if ((offset >= validate_offset &&
+                       offset < validate_offset + BUFLEN)
+                       || (validate_offset >= offset &&
+                               validate_offset < offset + BUFLEN)) {
+                       /* Don't overwrite the range we want to validate. */
+                       continue;
+               }
+               if (write_mode == WRITE_RANDOM)
+                       rand_buf(buf, BUFLEN);
+               /* Save validation buffer and position */
+               if (write_nr % VALIDATE_FREQ == 0 && verify_mode) {
+                       memcpy(validate_buf, buf, BUFLEN);
+                       validate_offset = offset;
+               }
+               pos = lseek(fd, offset, SEEK_SET);
+               if (pos < 0) {
+                       perror("lseek");
+                       exit(EXIT_FAILURE);
+               }
+               wret = write(fd, buf, BUFLEN);
+               if (wret != BUFLEN) {
+                       fprintf(stderr, "Error at write to offset: %zu\n",
+                               pos);
+                       perror("write");
+                       exit(EXIT_FAILURE);
+               }
+
+               /*
+                * Advise that we won't be re-reading the blocks. This
+                * will ask the kernel to drop pages related to this
+                * file quickly from its page cache, thus forcing a read
+                * from disk.
+                */
+               ret = fdatasync(fd);
+               if (ret) {
+                       perror("fdatasync");
+                       exit(EXIT_FAILURE);
+               }
+               ret = posix_fadvise(fd, offset, BUFLEN, POSIX_FADV_DONTNEED);
+               if (ret) {
+                       perror("posix_fadvise");
+                       exit(EXIT_FAILURE);
+               }
+
+               write_nr++;
+               if (write_nr % PRINT_FREQ == 0) {
+                       printf("Status: %" PRIu64 " writes.\n", write_nr);
+               } 
+
+               /*
+                * Use the validation buffer and position saved
+                * VALIDATE_FREQ operations earlier.
+                */
+               if (write_nr % VALIDATE_FREQ == 0 && verify_mode) {
+                       valcount = validate(fd, validate_buf, validate_offset);
+                       if (valcount) {
+                               printf("VALIDATION ERROR at offset %zu, %" PRIu64 " bytes differ\n",
+                                       validate_offset, valcount);
+                       }
+               }
+       }
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       int fd, ret, i, seed;
+       size_t len;
+       off_t pos;
+       ssize_t wret;
+
+       if (argc < 4) {
+               printf("Usage: %s <output file> <len (64-bit)> <seed (32-bit)> <-z to write zeroes> <-v to verify written data>\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       fd = open(argv[1], O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+       if (fd < 0) {
+               perror("open");
+               exit(EXIT_FAILURE);
+       }
+
+       len = atoll(argv[2]);
+       seed = atoi(argv[3]);
+       srand(seed);
+
+       printf("Creating file %s of length %zu, random seed %u\n", argv[1], len,
+               seed);
+
+       if (strcmp(argv[4], "-r") == 0) {
+               printf("Generating random data\n");
+               write_mode = WRITE_RANDOM;
+       } else if (strcmp(argv[4], "-z") == 0) {
+               printf("Filling with zeroes (compressible pattern)\n");
+               write_mode = WRITE_ZEROES;
+       } else {
+               printf("Invalid argument %s\n", argv[4]);
+               exit(EXIT_FAILURE);
+       }
+
+       for (i = 4; i < argc; i++) {
+               if (strcmp(argv[i], "-z") == 0) {
+                       write_mode = WRITE_ZEROES;
+               } else if (strcmp(argv[i], "-v") == 0) {
+                       verify_mode = 1;
+               }
+       }
+
+       if (verify_mode) {
+               printf("Verification mode activated.\n");
+       }
+
+       switch (write_mode) {
+       case WRITE_RANDOM:
+       case WRITE_ZEROES:
+               break;
+       default:
+               printf("Unsupported write-mode\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Grow file */
+       pos = lseek(fd, len - 1, SEEK_SET);
+       if (pos < 0) {
+               perror("lseek");
+               exit(EXIT_FAILURE);
+       }
+       wret = write(fd, "", 1);
+       if (wret < 0) {
+               perror("write");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Advise the OS that we are performing random accesses */
+       ret = posix_fadvise(fd, 0, len, POSIX_FADV_RANDOM);
+       if (ret) {
+               perror("posix_fadvise");
+               exit(EXIT_FAILURE);
+       }
+
+       ret = rand_write(fd, len);
+       if (ret) {
+               exit(EXIT_FAILURE);
+       }
+
+       ret = close(fd);
+       assert(!ret);
+       exit(EXIT_SUCCESS);
+}
This page took 0.037104 seconds and 4 git commands to generate.