[PATCH] fio: pretty close to compiling/working on FreeBSD now
[disktools.git] / sgioread.c
CommitLineData
dc61eacc
JA
1/*
2 * small utility to read from atapi and scsi devices
3 */
dc61eacc
JA
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
21static char device[128];
22static char output[128];
23static char rate_output[128];
24
25static int block_size;
26static unsigned int blocks;
27static unsigned long nr_blocks;
28static int write_output;
29static unsigned long start_lba;
30static int force_unaligned;
31static int write_rate;
32
33int 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
77int 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
111unsigned 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
119void 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
127void 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
139int 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
9b6d74e1 150void hdr_init(struct sg_io_hdr *hdr, char *p, unsigned char *cdb)
4c9cc53d
JA
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
dc61eacc
JA
164int 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));
dc61eacc
JA
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
dc61eacc 220 lba = start_lba;
fce10cc6 221 if (!nr_blocks)
dc61eacc
JA
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
4c9cc53d
JA
234 hdr_init(hdr, buffer, cdb);
235
dc61eacc
JA
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
fce10cc6 280 } while (lba <= end_lba && nr_blocks);
dc61eacc
JA
281
282 print_datarate(sectors_since_last, time_spent);
283 printf("\n");
284 free(ptr);
285 close(fd);
286 return 0;
287}