[PATCH] fio: ->verror need not be so large
[disktools.git] / sgioread.c
1 /*
2  * small utility to read from atapi and scsi devices
3  */
4 #include <stdio.h>
5 #include <fcntl.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <getopt.h>
10 #include <sys/ioctl.h>
11 #include <sys/time.h>
12 #include <scsi/sg.h>
13 #include <linux/cdrom.h>
14
15 #define DISK    "/dev/sda"
16
17 #define MAX_XFER        (65535)
18
19 #define ALIGN(buf)      (char *) (((unsigned long) (buf) + 4095) & ~(4095))
20
21 static char device[128];
22 static char output[128];
23 static char rate_output[128];
24
25 static int block_size;
26 static unsigned int blocks;
27 static unsigned long nr_blocks;
28 static int write_output;
29 static unsigned long start_lba;
30 static int force_unaligned;
31 static int write_rate;
32
33 int parse_options(int argc, char *argv[])
34 {
35         int c;
36
37         strcpy(device, DISK);
38
39         for (;;) {
40                 c = getopt(argc, argv, "d:n:o:s:p:ur:");
41                 if (c == -1)
42                         break;
43
44                 switch (c) {
45                         case 'd':
46                                 strcpy(device, optarg);
47                                 break;
48                         case 'n':
49                                 blocks = strtol(optarg, NULL, 10);
50                                 break;
51                         case 'o':
52                                 strcpy(output, optarg);
53                                 write_output = 1;
54                                 break;
55                         case 's':
56                                 nr_blocks = strtol(optarg, NULL, 10);
57                                 break;
58                         case 'p':
59                                 start_lba = strtol(optarg, NULL, 10);
60                                 break;
61                         case 'u':
62                                 force_unaligned = 1;
63                                 break;
64                         case 'r':
65                                 strcpy(rate_output, optarg);
66                                 write_rate = 1;
67                                 break;
68                         default:
69                                 printf("%s: -d device [-s # blocks] [-n # blocks per cmd] [-o output file] [-p start lba] [-u force unaligned buffer]\n", argv[0]);
70                                 return -1;
71                 }
72         }
73
74         return 0;
75 }
76
77 int get_capacity(int fd, int *bs, unsigned long *lba)
78 {
79         struct sg_io_hdr *hdr = malloc(sizeof(*hdr));
80         unsigned char buf[8], cdb[16];
81
82         *bs = 0;
83
84         memset(hdr, 0, sizeof(*hdr));
85         memset(cdb, 0, sizeof(cdb));
86         memset(buf, 0, sizeof(buf));
87
88         hdr->interface_id = 'S';
89         hdr->cmd_len = sizeof(cdb);
90         hdr->dxfer_direction = SG_DXFER_FROM_DEV;
91         hdr->dxferp = buf;
92         hdr->dxfer_len = sizeof(buf);
93
94         hdr->cmdp = cdb;
95         hdr->cmdp[0] = GPCMD_READ_CDVD_CAPACITY;
96
97         if (ioctl(fd, SG_IO, hdr) < 0) {
98                 perror("read capacity\n");
99                 printf("sense key: %x\n", hdr->status);
100                 return -1;
101         }
102
103         *lba = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
104         if (!*lba)
105                 *lba = ~0UL;
106
107         *bs = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
108         return 0;
109 }
110
111 unsigned long time_elapsed(struct timeval *start, struct timeval *end)
112 {
113         int seconds = end->tv_sec - start->tv_sec;
114         int useconds = end->tv_usec - start->tv_usec;
115
116         return (1000 * seconds) + (useconds / 1000);
117 }
118
119 void print_datarate(unsigned long sectors_since_last, int time_spent)
120 {
121         if (!time_spent)
122                 time_spent = 1;
123
124         printf("disk %s: data rate %luKib/sec\r", device, 1000 * (sectors_since_last * block_size / 1024) / time_spent);
125 }
126
127 void write_datarate(int fd, unsigned long lba, unsigned long sectors, int time)
128 {
129         char buffer[32];
130
131         if (fd == -1)
132                 return;
133
134         memset(buffer, 0, sizeof(buffer));
135         snprintf(buffer, sizeof(buffer), "%lu, %lu\n", lba, (sectors * block_size) / time);
136         write(fd, buffer, strlen(buffer));
137 }
138
139 int write_the_crap(int fd_out, char *buffer, int length)
140 {
141         int ret;
142
143         ret = write(fd_out, buffer, length);
144         if (ret != length)
145                 printf("\nwanted to write %u, wrote %d\n", length, ret);
146
147         return ret;
148 }
149
150 void hdr_init(struct sg_io_hdr *hdr, char *p, unsigned char *cdb)
151 {
152         memset(hdr, 0, sizeof(*hdr));
153
154         hdr->cmdp = cdb;
155         memset(hdr->cmdp, 0, 10);
156
157         hdr->interface_id = 'S';
158         hdr->cmd_len = 10;
159         hdr->dxfer_direction = SG_DXFER_FROM_DEV;
160         hdr->dxferp = p;
161         hdr->cmdp[0] = GPCMD_READ_10;
162 }
163
164 int main(int argc, char *argv[])
165 {
166         struct sg_io_hdr *hdr;
167         unsigned long lba, bytes;
168         char *buffer, *ptr;
169         int fd, fd_out, fd_rate;
170         unsigned long time_spent;
171         unsigned long sectors_since_last, end_lba;
172         struct timeval start_time, end_time;
173         unsigned char cdb[16];
174
175         if (parse_options(argc, argv))
176                 return 1;
177
178         fd = open(device, O_RDONLY | O_NONBLOCK);
179         if (fd == -1) {
180                 perror("open input");
181                 return 1;
182         }
183
184         fd_out = -1;
185         if (write_output) {
186                 fd_out = open(output, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
187                 if (fd_out == -1) {
188                         perror("open output");
189                         return 2;
190                 }
191         }
192
193         fd_rate = -1;
194         if (write_rate) {
195                 fd_rate = open(rate_output, O_WRONLY | O_CREAT | O_TRUNC, 0644);
196                 if (fd_rate == -1) {
197                         perror("open rate output");
198                         return 3;
199                 }
200         }
201
202         if (get_capacity(fd, &block_size, &end_lba)) {
203                 printf("%s: can't retrieve capacity\n", device);
204                 return 2;
205         }
206
207         if (!blocks)
208                 blocks = MAX_XFER / block_size;
209
210         hdr = malloc(sizeof(*hdr));
211
212         ptr = malloc(blocks * block_size + block_size - 1);
213         if (force_unaligned) {
214                 buffer = ptr;
215                 if (!(((unsigned long) buffer) & (block_size - 1)))
216                         buffer++;
217         } else
218                 buffer = ALIGN(ptr);
219
220         lba = start_lba;
221         if (!nr_blocks)
222                 nr_blocks = end_lba - lba + 1;
223
224         printf("disk %s: reading LBA %lu -> %lu", device, start_lba, end_lba);
225         printf(" in chunks of %u %ub blocks\n", blocks, block_size);
226
227         bytes = 0;
228         time_spent = sectors_since_last = 0;
229         gettimeofday(&start_time, NULL);
230         do {
231                 if (blocks > nr_blocks)
232                         blocks = nr_blocks;
233
234                 hdr_init(hdr, buffer, cdb);
235
236                 hdr->dxfer_len = blocks * block_size;
237                 hdr->cmdp[2] = (lba >> 24) & 0xff;
238                 hdr->cmdp[3] = (lba >> 16) & 0xff;
239                 hdr->cmdp[4] = (lba >>  8) & 0xff;
240                 hdr->cmdp[5] = lba & 0xff;
241                 hdr->cmdp[7] = (blocks >> 8) & 0xff;
242                 hdr->cmdp[8] = blocks & 0xff;
243
244                 if (ioctl(fd, SG_IO, hdr) < 0) {
245                         int good_blocks = (hdr->dxfer_len - hdr->resid) / block_size;
246                         printf("sense key: %x\n", hdr->status);
247                         if (good_blocks) {
248                                 printf("\n%s: IO error at lba %lu\n", device, lba + good_blocks);
249                                 blocks = good_blocks;
250                         } else {
251                                 printf("\n%s: giving up at lba %lu\n", device, lba);
252                                 break;
253                         }
254                 }
255
256                 if (hdr->resid)
257                         printf("%s: residual %u\n", device, hdr->resid);
258
259                 gettimeofday(&end_time, NULL);
260
261                 nr_blocks -= blocks;
262
263                 sectors_since_last += blocks;
264                 time_spent = time_elapsed(&start_time, &end_time);
265                 if (time_spent >= 500) {
266                         print_datarate(sectors_since_last, time_spent);
267                         fflush(stdout);
268                         if (fd_rate != -1)
269                                 write_datarate(fd_rate, lba, sectors_since_last, time_spent);
270                         sectors_since_last = 0;
271                         gettimeofday(&start_time, NULL);
272                 }
273
274                 lba += blocks;
275                 bytes += blocks * block_size;
276
277                 if (fd_out != -1)
278                         write_the_crap(fd_out, buffer, blocks * block_size);
279
280         } while (lba <= end_lba && nr_blocks);
281
282         print_datarate(sectors_since_last, time_spent);
283         printf("\n");
284         free(ptr);
285         close(fd);
286         return 0;
287 }