From e3027339976586284af5f5d437104bf5be217cc4 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Fri, 22 Feb 2013 15:08:43 -0500 Subject: [PATCH] Initial import Signed-off-by: Mathieu Desnoyers --- Makefile | 6 + test-ssd-write.c | 298 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 Makefile create mode 100644 test-ssd-write.c diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..2bce0fc --- /dev/null +++ b/test-ssd-write.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 <-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); +} -- 2.34.1