From: Jens Axboe Date: Tue, 4 Oct 2005 10:35:57 +0000 (+0200) Subject: Add sgioread tool X-Git-Url: https://git.kernel.dk/?a=commitdiff_plain;h=dc61eacce52758c074b7f61a18a693e8eca52c29;p=disktools.git Add sgioread tool --- diff --git a/Makefile b/Makefile index df705c8..e6c583a 100644 --- 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 index 0000000..74a6567 --- /dev/null +++ b/sgioread.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}