blob: e5c02fc841d4ee499a9023af1bf455d6d4d102dd [file] [log] [blame]
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>
#include "sg_include.h"
#include "sg_err.h"
/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
device driver.
* Copyright (C) 1999-2002 D. Gilbert
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
This program uses the SCSI command READ BUFFER on the given sg
device, first to find out how big it is and then to read that
buffer. The '-q' option skips the data transfer from the kernel
DMA buffers to the user space. The '-b=num' option allows the
buffer size (in KBytes) to be specified (default is to use the
number obtained from READ BUFFER (descriptor) SCSI command).
The '-s=num' option allows the total size of the transfer to be
set (in megabytes, the default is 200 MB). The '-d' option requests
direct io (and is overridden by '-q').
The '-m' option request mmap-ed IO (and overrides the '-q' and '-d'
options if they are also given).
The ability to time transfers internally (based on gettimeofday()) has
been added with the '-t' option.
Version 4.73 (20020520)
*/
#define RB_MODE_DESC 3
#define RB_MODE_DATA 2
#define RB_DESC_LEN 4
#define RB_MB_TO_READ 200
#define RB_OPCODE 0x3C
#define RB_CMD_LEN 10
/* #define SG_DEBUG */
#ifndef SG_FLAG_MMAP_IO
#define SG_FLAG_MMAP_IO 4
#endif
#define ME "sg_rbuf: "
int main(int argc, char * argv[])
{
int sg_fd, res, j, m;
unsigned int k, num;
unsigned char rbCmdBlk [RB_CMD_LEN];
unsigned char * rbBuff = NULL;
void * rawp = NULL;
unsigned char sense_buffer[32];
int buf_capacity = 0;
int do_quick = 0;
int do_dio = 0;
int do_mmap = 0;
int do_time = 0;
int buf_size = 0;
unsigned int total_size_mb = RB_MB_TO_READ;
char * file_name = 0;
size_t psz = getpagesize();
int dio_incomplete = 0;
sg_io_hdr_t io_hdr;
struct timeval start_tm, end_tm;
#ifdef SG_DEBUG
int clear = 1;
#endif
for (j = 1; j < argc; ++j) {
if (0 == strncmp("-b=", argv[j], 3)) {
m = 3;
num = sscanf(argv[j] + m, "%d", &buf_size);
if ((1 != num) || (buf_size <= 0)) {
printf("Couldn't decode number after '-b' switch\n");
file_name = 0;
break;
}
buf_size *= 1024;
}
else if (0 == strncmp("-s=", argv[j], 3)) {
m = 3;
num = sscanf(argv[j] + m, "%u", &total_size_mb);
if (1 != num) {
printf("Couldn't decode number after '-s' switch\n");
file_name = 0;
break;
}
}
else if (0 == strcmp("-q", argv[j]))
do_quick = 1;
else if (0 == strcmp("-d", argv[j]))
do_dio = 1;
else if (0 == strcmp("-m", argv[j]))
do_mmap = 1;
else if (0 == strcmp("-t", argv[j]))
do_time = 1;
else if (*argv[j] == '-') {
printf("Unrecognized switch: %s\n", argv[j]);
file_name = 0;
break;
}
else
file_name = argv[j];
}
if (0 == file_name) {
printf("Usage: sg_rbuf [[-q] | [-d] | [-m]] [-t] [-b=num] [-s=num] "
"<generic_device>\n");
printf(" where: -q quick, don't xfer to user space\n");
printf(" -d requests dio ('-q' overrides it)\n");
printf(" -m requests mmap-ed IO (overrides -q, -d)\n");
printf(" -t time the data transfer\n");
printf(" -b=num num is buffer size to use (in KBytes)\n");
printf(" -s=num num is total size to read (in MBytes)\n");
printf(" default total size is 200 MBytes\n");
printf(" max total size is 4000 MBytes\n");
return 1;
}
sg_fd = open(file_name, O_RDONLY);
if (sg_fd < 0) {
perror(ME "open error");
return 1;
}
/* Don't worry, being very careful not to write to a none-sg file ... */
res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
if ((res < 0) || (k < 30000)) {
printf(ME "not a sg device, or driver prior to 3.x\n");
return 1;
}
if (do_mmap) {
do_dio = 0;
do_quick = 0;
}
if (NULL == (rawp = malloc(512))) {
printf(ME "out of memory (query)\n");
return 1;
}
rbBuff = rawp;
memset(rbCmdBlk, 0, RB_CMD_LEN);
rbCmdBlk[0] = RB_OPCODE;
rbCmdBlk[1] = RB_MODE_DESC;
rbCmdBlk[8] = RB_DESC_LEN;
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(rbCmdBlk);
io_hdr.mx_sb_len = sizeof(sense_buffer);
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = RB_DESC_LEN;
io_hdr.dxferp = rbBuff;
io_hdr.cmdp = rbCmdBlk;
io_hdr.sbp = sense_buffer;
io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
/* do normal IO to find RB size (not dio or mmap-ed at this stage) */
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
perror(ME "SG_IO READ BUFFER descriptor error");
if (rawp) free(rawp);
return 1;
}
/* now for the error processing */
switch (sg_err_category3(&io_hdr)) {
case SG_ERR_CAT_CLEAN:
break;
case SG_ERR_CAT_RECOVERED:
printf("Recovered error on READ BUFFER descriptor, continuing\n");
break;
default: /* won't bother decoding other categories */
sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr);
if (rawp) free(rawp);
return 1;
}
buf_capacity = ((rbBuff[1] << 16) | (rbBuff[2] << 8) | rbBuff[3]);
printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n",
buf_capacity, (int)rbBuff[0]);
if (0 == buf_size)
buf_size = buf_capacity;
else if (buf_size > buf_capacity) {
printf("Requested buffer size=%d exceeds reported capacity=%d\n",
buf_size, buf_capacity);
if (rawp) free(rawp);
return 1;
}
if (rawp) {
free(rawp);
rawp = NULL;
}
if (! do_dio) {
k = buf_size;
if (do_mmap && (0 != (k % psz)))
k = ((k / psz) + 1) * psz; /* round up to page size */
res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, &k);
if (res < 0)
perror(ME "SG_SET_RESERVED_SIZE error");
}
if (do_mmap) {
rbBuff = mmap(NULL, buf_size, PROT_READ, MAP_SHARED, sg_fd, 0);
if (MAP_FAILED == rbBuff) {
if (ENOMEM == errno)
printf(ME "mmap() out of memory, try a smaller "
"buffer size than %d KB\n", buf_size / 1024);
else
perror(ME "error using mmap()");
return 1;
}
}
else { /* non mmap-ed IO */
rawp = malloc(buf_size + (do_dio ? psz : 0));
if (NULL == rawp) {
printf(ME "out of memory (data)\n");
return 1;
}
if (do_dio) /* align to page boundary */
rbBuff= (unsigned char *)(((unsigned long)rawp + psz - 1) &
(~(psz - 1)));
else
rbBuff = rawp;
}
num = (total_size_mb * 1024U * 1024U) / (unsigned int)buf_size;
if (do_time) {
start_tm.tv_sec = 0;
start_tm.tv_usec = 0;
gettimeofday(&start_tm, NULL);
}
/* main data reading loop */
for (k = 0; k < num; ++k) {
memset(rbCmdBlk, 0, RB_CMD_LEN);
rbCmdBlk[0] = RB_OPCODE;
rbCmdBlk[1] = RB_MODE_DATA;
rbCmdBlk[6] = 0xff & (buf_size >> 16);
rbCmdBlk[7] = 0xff & (buf_size >> 8);
rbCmdBlk[8] = 0xff & buf_size;
#ifdef SG_DEBUG
memset(rbBuff, 0, buf_size);
#endif
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(rbCmdBlk);
io_hdr.mx_sb_len = sizeof(sense_buffer);
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = buf_size;
if (! do_mmap)
io_hdr.dxferp = rbBuff;
io_hdr.cmdp = rbCmdBlk;
io_hdr.sbp = sense_buffer;
io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
io_hdr.pack_id = k;
if (do_mmap)
io_hdr.flags |= SG_FLAG_MMAP_IO;
else if (do_dio)
io_hdr.flags |= SG_FLAG_DIRECT_IO;
else if (do_quick)
io_hdr.flags |= SG_FLAG_NO_DXFER;
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
if (ENOMEM == errno)
printf(ME "SG_IO data; out of memory, try a smaller "
"buffer size than %d KB\n", buf_size / 1024);
else
perror(ME "SG_IO READ BUFFER data error");
if (rawp) free(rawp);
return 1;
}
/* now for the error processing */
switch (sg_err_category3(&io_hdr)) {
case SG_ERR_CAT_CLEAN:
break;
case SG_ERR_CAT_RECOVERED:
printf("Recovered error on READ BUFFER data, continuing\n");
break;
default: /* won't bother decoding other categories */
sg_chk_n_print3("READ BUFFER data error", &io_hdr);
if (rawp) free(rawp);
return 1;
}
if (do_dio &&
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
dio_incomplete = 1; /* flag that dio not done (completely) */
#ifdef SG_DEBUG
if (clear) {
for (j = 0; j < buf_size; ++j) {
if (rbBuff[j] != 0) {
clear = 0;
break;
}
}
}
#endif
}
if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
struct timeval res_tm;
double a, b;
gettimeofday(&end_tm, NULL);
res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
if (res_tm.tv_usec < 0) {
--res_tm.tv_sec;
res_tm.tv_usec += 1000000;
}
a = res_tm.tv_sec;
a += (0.000001 * res_tm.tv_usec);
b = (double)buf_size * num;
printf("time to read data from buffer was %d.%06d secs",
(int)res_tm.tv_sec, (int)res_tm.tv_usec);
if ((a > 0.00001) && (b > 511))
printf(", %.2f MB/sec\n", b / (a * 1000000.0));
else
printf("\n");
}
if (dio_incomplete)
printf(">> direct IO requested but not done\n");
printf("Read %u MBytes (actual %u MB, %u bytes), buffer size=%d KBytes\n",
total_size_mb, (num * buf_size) / 1048576, num * buf_size,
buf_size / 1024);
if (rawp) free(rawp);
res = close(sg_fd);
if (res < 0) {
perror(ME "close error");
return 1;
}
#ifdef SG_DEBUG
if (clear)
printf("read buffer always zero\n");
else
printf("read buffer non-zero\n");
#endif
return 0;
}