Add sgioread tool
authorJens Axboe <axboe@suse.de>
Tue, 4 Oct 2005 10:35:57 +0000 (12:35 +0200)
committerJens Axboe <axboe@suse.de>
Tue, 4 Oct 2005 10:35:57 +0000 (12:35 +0200)
Makefile
sgioread.c [new file with mode: 0644]

index df705c8993eef5ecc4a65b499383852cbc00bf58..e6c583aa8dd542a94efe3459a9961f85cd01e287 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 CC     = gcc
 CFLAGS = -Wall -O2 -g -D_GNU_SOURCE
-PROGS  = dops fio
+PROGS  = dops fio sgioread
 
 all: $(PROGS)
 
@@ -10,6 +10,9 @@ dops: dops.o
 fio: fio.o
        $(CC) $(CFLAGS) -o $@ $(filter %.o,$^) -lpthread
 
+sgioread: sgioread.o
+       $(CC) $(CFLAGS) -o $@ $(filter %.o,$^)
+
 clean:
        -rm -f *.o $(PROGS)
 
diff --git a/sgioread.c b/sgioread.c
new file mode 100644 (file)
index 0000000..74a6567
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * small utility to read from atapi and scsi devices
+ */
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <scsi/sg.h>
+#include <linux/cdrom.h>
+
+#define DISK   "/dev/sda"
+
+#define MAX_XFER       (65535)
+
+#define ALIGN(buf)     (char *) (((unsigned long) (buf) + 4095) & ~(4095))
+
+static char device[128];
+static char output[128];
+static char rate_output[128];
+
+static int block_size;
+static unsigned int blocks;
+static unsigned long nr_blocks;
+static int write_output;
+static unsigned long start_lba;
+static int force_unaligned;
+static int write_rate;
+
+int parse_options(int argc, char *argv[])
+{
+       int c;
+
+       strcpy(device, DISK);
+
+       for (;;) {
+               c = getopt(argc, argv, "d:n:o:s:p:ur:");
+               if (c == -1)
+                       break;
+
+               switch (c) {
+                       case 'd':
+                               strcpy(device, optarg);
+                               break;
+                       case 'n':
+                               blocks = strtol(optarg, NULL, 10);
+                               break;
+                       case 'o':
+                               strcpy(output, optarg);
+                               write_output = 1;
+                               break;
+                       case 's':
+                               nr_blocks = strtol(optarg, NULL, 10);
+                               break;
+                       case 'p':
+                               start_lba = strtol(optarg, NULL, 10);
+                               break;
+                       case 'u':
+                               force_unaligned = 1;
+                               break;
+                       case 'r':
+                               strcpy(rate_output, optarg);
+                               write_rate = 1;
+                               break;
+                       default:
+                               printf("%s: -d device [-s # blocks] [-n # blocks per cmd] [-o output file] [-p start lba] [-u force unaligned buffer]\n", argv[0]);
+                               return -1;
+               }
+       }
+
+       return 0;
+}
+
+int get_capacity(int fd, int *bs, unsigned long *lba)
+{
+       struct sg_io_hdr *hdr = malloc(sizeof(*hdr));
+       unsigned char buf[8], cdb[16];
+
+       *bs = 0;
+
+       memset(hdr, 0, sizeof(*hdr));
+       memset(cdb, 0, sizeof(cdb));
+       memset(buf, 0, sizeof(buf));
+
+       hdr->interface_id = 'S';
+       hdr->cmd_len = sizeof(cdb);
+       hdr->dxfer_direction = SG_DXFER_FROM_DEV;
+       hdr->dxferp = buf;
+       hdr->dxfer_len = sizeof(buf);
+
+       hdr->cmdp = cdb;
+       hdr->cmdp[0] = GPCMD_READ_CDVD_CAPACITY;
+
+       if (ioctl(fd, SG_IO, hdr) < 0) {
+               perror("read capacity\n");
+               printf("sense key: %x\n", hdr->status);
+               return -1;
+       }
+
+       *lba = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+       if (!*lba)
+               *lba = ~0UL;
+
+       *bs = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+       return 0;
+}
+
+unsigned long time_elapsed(struct timeval *start, struct timeval *end)
+{
+       int seconds = end->tv_sec - start->tv_sec;
+       int useconds = end->tv_usec - start->tv_usec;
+
+       return (1000 * seconds) + (useconds / 1000);
+}
+
+void print_datarate(unsigned long sectors_since_last, int time_spent)
+{
+       if (!time_spent)
+               time_spent = 1;
+
+       printf("disk %s: data rate %luKib/sec\r", device, 1000 * (sectors_since_last * block_size / 1024) / time_spent);
+}
+
+void write_datarate(int fd, unsigned long lba, unsigned long sectors, int time)
+{
+       char buffer[32];
+
+       if (fd == -1)
+               return;
+
+       memset(buffer, 0, sizeof(buffer));
+       snprintf(buffer, sizeof(buffer), "%lu, %lu\n", lba, (sectors * block_size) / time);
+       write(fd, buffer, strlen(buffer));
+}
+
+int write_the_crap(int fd_out, char *buffer, int length)
+{
+       int ret;
+
+       ret = write(fd_out, buffer, length);
+       if (ret != length)
+               printf("\nwanted to write %u, wrote %d\n", length, ret);
+
+       return ret;
+}
+
+int main(int argc, char *argv[])
+{
+       struct sg_io_hdr *hdr;
+       unsigned long lba, bytes;
+       char *buffer, *ptr;
+       int fd, fd_out, fd_rate;
+       unsigned long time_spent;
+       unsigned long sectors_since_last, end_lba;
+       struct timeval start_time, end_time;
+       unsigned char cdb[16];
+
+       if (parse_options(argc, argv))
+               return 1;
+
+       fd = open(device, O_RDONLY | O_NONBLOCK);
+       if (fd == -1) {
+               perror("open input");
+               return 1;
+       }
+
+       fd_out = -1;
+       if (write_output) {
+               fd_out = open(output, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
+               if (fd_out == -1) {
+                       perror("open output");
+                       return 2;
+               }
+       }
+
+       fd_rate = -1;
+       if (write_rate) {
+               fd_rate = open(rate_output, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+               if (fd_rate == -1) {
+                       perror("open rate output");
+                       return 3;
+               }
+       }
+
+       if (get_capacity(fd, &block_size, &end_lba)) {
+               printf("%s: can't retrieve capacity\n", device);
+               return 2;
+       }
+
+       if (!blocks)
+               blocks = MAX_XFER / block_size;
+
+       hdr = malloc(sizeof(*hdr));
+       memset(hdr, 0, sizeof(*hdr));
+
+       ptr = malloc(blocks * block_size + block_size - 1);
+       if (force_unaligned) {
+               buffer = ptr;
+               if (!(((unsigned long) buffer) & (block_size - 1)))
+                       buffer++;
+       } else
+               buffer = ALIGN(ptr);
+
+       hdr->cmdp = cdb;
+       memset(hdr->cmdp, 0, sizeof(cdb));
+
+       hdr->interface_id = 'S';
+       hdr->cmd_len = sizeof(cdb);
+       hdr->dxfer_direction = SG_DXFER_FROM_DEV;
+       hdr->dxferp = buffer;
+       hdr->cmdp[0] = GPCMD_READ_10;
+
+       lba = start_lba;
+               nr_blocks = end_lba - lba + 1;
+
+       printf("disk %s: reading LBA %lu -> %lu", device, start_lba, end_lba);
+       printf(" in chunks of %u %ub blocks\n", blocks, block_size);
+
+       bytes = 0;
+       time_spent = sectors_since_last = 0;
+       gettimeofday(&start_time, NULL);
+       do {
+               if (blocks > nr_blocks)
+                       blocks = nr_blocks;
+
+               hdr->dxfer_len = blocks * block_size;
+               hdr->cmdp[2] = (lba >> 24) & 0xff;
+               hdr->cmdp[3] = (lba >> 16) & 0xff;
+               hdr->cmdp[4] = (lba >>  8) & 0xff;
+               hdr->cmdp[5] = lba & 0xff;
+               hdr->cmdp[7] = (blocks >> 8) & 0xff;
+               hdr->cmdp[8] = blocks & 0xff;
+
+               if (ioctl(fd, SG_IO, hdr) < 0) {
+                       int good_blocks = (hdr->dxfer_len - hdr->resid) / block_size;
+                       printf("sense key: %x\n", hdr->status);
+                       if (good_blocks) {
+                               printf("\n%s: IO error at lba %lu\n", device, lba + good_blocks);
+                               blocks = good_blocks;
+                       } else {
+                               printf("\n%s: giving up at lba %lu\n", device, lba);
+                               break;
+                       }
+               }
+
+               if (hdr->resid)
+                       printf("%s: residual %u\n", device, hdr->resid);
+
+               gettimeofday(&end_time, NULL);
+
+               nr_blocks -= blocks;
+
+               sectors_since_last += blocks;
+               time_spent = time_elapsed(&start_time, &end_time);
+               if (time_spent >= 500) {
+                       print_datarate(sectors_since_last, time_spent);
+                       fflush(stdout);
+                       if (fd_rate != -1)
+                               write_datarate(fd_rate, lba, sectors_since_last, time_spent);
+                       sectors_since_last = 0;
+                       gettimeofday(&start_time, NULL);
+               }
+
+               lba += blocks;
+               bytes += blocks * block_size;
+
+               if (fd_out != -1)
+                       write_the_crap(fd_out, buffer, blocks * block_size);
+
+       } while (lba <= end_lba);
+
+       print_datarate(sectors_since_last, time_spent);
+       printf("\n");
+       free(ptr);
+       close(fd);
+       return 0;
+}