add a parameter to se the iscsi port to use
[tridge/dbench.git] / linux_scsi.c
1 /* 
2    Copyright (C) by Ronnie Sahlberg <sahlberg@samba.org> 2008
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8    
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13    
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #define _GNU_SOURCE
19 #include <stdio.h>
20 #undef _GNU_SOURCE
21
22 #include "dbench.h"
23
24 #ifdef HAVE_LINUX_SCSI_SG
25
26 #include <fcntl.h>
27 #include <sys/ioctl.h>
28 #include <scsi/sg.h>
29 #include <stdint.h>
30
31 #define SCSI_TIMEOUT 5000 /* ms */
32
33 #define discard_const(ptr) ((void *)((intptr_t)(ptr)))
34
35 struct scsi_device {
36         int fd;
37         uint32_t blocks;
38 };
39
40 static int check_sense(unsigned char sc, const char *expected);
41 static int scsi_io(int fd, unsigned char *cdb, unsigned char cdb_size, int xfer_dir, unsigned int *data_size, char *data, unsigned char *sc);
42
43 static void num_device_blocks(struct scsi_device *sd)
44 {
45         unsigned char cdb[]={0x25,0,0,0,0,0,0,0,0,0};
46         int res;
47         unsigned int data_size=8;
48         char data[data_size];
49         unsigned char sc;
50
51         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
52         if(res){
53                 printf("SCSI_IO failed when reading disk capacity\n");
54                 exit(10);
55         }
56         if (!check_sense(sc, "0x00")) {
57                 printf("READCAPACITY10 failed (0x%02x) - expected 0x00\n", sc);
58                 exit(10);
59         }
60
61         sd->blocks = (0xff & data[0]);
62         sd->blocks = (sd->blocks<<8) | (0xff & data[1]);
63         sd->blocks = (sd->blocks<<8) | (0xff & data[2]);
64         sd->blocks = (sd->blocks<<8) | (0xff & data[3]);
65
66         sd->blocks++;
67 }
68
69 static void scsi_setup(struct child_struct *child)
70 {
71         int vers;
72         struct scsi_device *sd;
73
74         sd = malloc(sizeof(struct scsi_device));
75         if (sd == NULL) {
76                 printf("Failed to allocate scsi device structure\n");
77                         exit(10);
78         }
79         child->private=sd;
80         if((sd->fd=open(options.scsi_dev, O_RDWR))<0){
81                 printf("Failed to open scsi device node : %s\n", options.scsi_dev);
82                 free(sd);
83                 exit(10);
84         }
85         if ((ioctl(sd->fd, SG_GET_VERSION_NUM, &vers) < 0) || (vers < 30000)) {
86                 printf("%s is not a SCSI device node\n", options.scsi_dev);
87                 close(sd->fd);
88                 free(sd);
89                 exit(10);
90         }
91
92         /* read disk capacity */
93         num_device_blocks(sd);
94 }
95
96 static void scsi_cleanup(struct child_struct *child)
97 {
98         struct scsi_device *sd;
99
100         sd=child->private;
101         close(sd->fd);
102         sd->fd=-1;
103         free(sd);
104 }
105
106
107 static int scsi_io(int fd, unsigned char *cdb, unsigned char cdb_size, int xfer_dir, unsigned int *data_size, char *data, unsigned char *sc)
108 {
109         sg_io_hdr_t io_hdr;
110         unsigned int sense_len=32;
111         unsigned char sense[sense_len];
112
113         *sc = 0;
114
115         memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
116         io_hdr.interface_id = 'S';
117
118         /* CDB */
119         io_hdr.cmdp = cdb;
120         io_hdr.cmd_len = cdb_size;
121
122         /* Where to store the sense_data, if there was an error */
123         io_hdr.sbp = sense;
124         io_hdr.mx_sb_len = sense_len;
125         sense_len=0;
126
127         /* Transfer direction, either in or out. Linux does not yet
128            support bidirectional SCSI transfers ?
129          */
130         io_hdr.dxfer_direction = xfer_dir;
131
132         /* Where to store the DATA IN/OUT from the device and how big the
133            buffer is
134          */
135         io_hdr.dxferp = data;
136         io_hdr.dxfer_len = *data_size;
137
138         /* SCSI timeout in ms */
139         io_hdr.timeout = SCSI_TIMEOUT;
140
141
142         if(ioctl(fd, SG_IO, &io_hdr) < 0){
143                 perror("SG_IO ioctl failed");
144                 return -1;
145         }
146
147         /* now for the error processing */
148         if((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK){
149                 if(io_hdr.sb_len_wr > 0){
150                         sense_len=io_hdr.sb_len_wr;
151                         *sc=sense[2]&0x0f;
152                         return 0;
153                 }
154         }
155         if(io_hdr.masked_status){
156                 printf("SCSI status=0x%x\n", io_hdr.status);
157                 printf("SCSI masked_status=0x%x\n", io_hdr.masked_status);
158                 return -2;
159         }
160         if(io_hdr.host_status){
161                 printf("SCSI host_status=0x%x\n", io_hdr.host_status);
162                 return -3;
163         }
164         if(io_hdr.driver_status){
165                 printf("driver_status=0x%x\n", io_hdr.driver_status);
166                 return -4;
167         }
168
169         return 0;
170 }
171
172
173 static int check_sense(unsigned char sc, const char *expected)
174 {
175         if (strcmp(expected, "*") == 0){
176                 return 1;
177         }
178         if (strncmp(expected, "0x", 2) == 0) {
179                 return sc == strtol(expected, NULL, 16);
180         }
181         return 0;
182 }
183
184 static void failed(struct child_struct *child)
185 {
186         child->failed = 1;
187         printf("ERROR: child %d failed at line %d\n", child->id, child->line);
188         exit(1);
189 }
190
191 static void scsi_testunitready(struct dbench_op *op)
192 {
193         struct scsi_device *sd;
194         unsigned char cdb[]={0,0,0,0,0,0};
195         int res;
196         unsigned char sc;
197         unsigned int data_size=200;
198         char data[data_size];
199
200         sd = op->child->private;
201
202         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
203         if(res){
204                 printf("SCSI_IO failed\n");
205                 failed(op->child);
206         }
207         if (!check_sense(sc, op->status)) {
208                 printf("[%d] TESTUNITREADY \"%s\" failed (0x%02x) - expected %s\n", 
209                        op->child->line, op->fname, sc, op->status);
210                 failed(op->child);
211         }
212
213         return;
214 }
215
216
217 static void scsi_read6(struct dbench_op *op)
218 {
219         struct scsi_device *sd=op->child->private;
220         unsigned char cdb[]={0x08,0,0,0,0,0};
221         int res;
222         uint32_t lba = op->params[0];
223         uint32_t xferlen = op->params[1];
224         unsigned int data_size=1024*1024;
225         char data[data_size];
226         unsigned char sc;
227
228         if (lba == 0xffffffff) {
229                 lba = random();
230                 lba = (lba / xferlen) * xferlen;
231         }
232
233         /* we only have 24 bit addresses in read 6 */
234         if (lba > 0x00ffffff) {
235                 lba &= 0x00ffffff;
236         }
237
238         /* make sure we wrap properly instead of failing if the loadfile
239            is bigger than our device
240         */
241         if (sd->blocks <= lba) {
242                 lba = lba%sd->blocks;
243         }
244         if (sd->blocks <= lba+xferlen) {
245                 xferlen=1;
246         }
247
248         cdb[1] = (lba>>16)&0x1f;
249         cdb[2] = (lba>> 8)&0xff;
250         cdb[3] = (lba    )&0xff;
251
252         cdb[4] = xferlen&0xff;
253         data_size = xferlen*512;
254
255         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
256         if(res){
257                 printf("SCSI_IO failed\n");
258                 failed(op->child);
259         }
260         if (!check_sense(sc, op->status)) {
261                 printf("[%d] READ6 \"%s\" failed (0x%02x) - expected %s\n", 
262                        op->child->line, op->fname, sc, op->status);
263                 failed(op->child);
264         }
265
266         op->child->bytes += xferlen*512;
267 }
268
269 static void scsi_read10(struct dbench_op *op)
270 {
271         struct scsi_device *sd=op->child->private;
272         unsigned char cdb[]={0x28,0,0,0,0,0,0,0,0,0};
273         int res;
274         uint32_t lba = op->params[0];
275         uint32_t xferlen = op->params[1];
276         int rd = op->params[2];
277         int grp = op->params[3];
278         unsigned int data_size=1024*1024;
279         char data[data_size];
280         unsigned char sc;
281
282         if (lba == 0xffffffff) {
283                 lba = random();
284                 lba = (lba / xferlen) * xferlen;
285         }
286
287         /* make sure we wrap properly instead of failing if the loadfile
288            is bigger than our device
289         */
290         if (sd->blocks <= lba) {
291                 lba = lba%sd->blocks;
292         }
293         if (sd->blocks <= lba+xferlen) {
294                 xferlen=1;
295         }
296
297         cdb[1] = rd;
298
299         cdb[2] = (lba>>24)&0xff;
300         cdb[3] = (lba>>16)&0xff;
301         cdb[4] = (lba>> 8)&0xff;
302         cdb[5] = (lba    )&0xff;
303
304         cdb[6] = grp&0x1f;
305
306         cdb[7] = (xferlen>>8)&0xff;
307         cdb[8] = xferlen&0xff;
308         data_size = xferlen*512;
309
310         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
311         if(res){
312                 printf("SCSI_IO failed\n");
313                 failed(op->child);
314         }
315         if (!check_sense(sc, op->status)) {
316                 printf("[%d] READ10 \"%s\" failed (0x%02x) - expected %s\n", 
317                        op->child->line, op->fname, sc, op->status);
318                 failed(op->child);
319         }
320
321         op->child->bytes += xferlen*512;
322 }
323
324 static void scsi_write10(struct dbench_op *op)
325 {
326         struct scsi_device *sd=op->child->private;
327         unsigned char cdb[]={0x2a,0,0,0,0,0,0,0,0,0};
328         int res;
329         uint32_t lba = op->params[0];
330         uint32_t xferlen = op->params[1];
331         int rd = op->params[2];
332         int fua = op->params[3];
333         unsigned int data_size=1024*1024;
334         char data[data_size];
335         unsigned char sc;
336
337         if (!options.allow_scsi_writes) {
338                 printf("Ignoring SCSI write\n");
339                 return;
340         }
341
342         if (lba == 0xffffffff) {
343                 lba = random();
344                 lba = (lba / xferlen) * xferlen;
345         }
346
347         /* make sure we wrap properly instead of failing if the loadfile
348            is bigger than our device
349         */
350         if (sd->blocks <= lba) {
351                 lba = lba%sd->blocks;
352         }
353         if (sd->blocks <= lba+xferlen) {
354                 xferlen=1;
355         }
356
357         cdb[1] = rd;
358
359         cdb[2] = (lba>>24)&0xff;
360         cdb[3] = (lba>>16)&0xff;
361         cdb[4] = (lba>> 8)&0xff;
362         cdb[5] = (lba    )&0xff;
363
364         cdb[6] = fua;
365
366         cdb[7] = (xferlen>>8)&0xff;
367         cdb[8] = xferlen&0xff;
368         data_size = xferlen*512;
369
370         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_TO_DEV, &data_size, data, &sc);
371         if(res){
372                 printf("SCSI_IO failed\n");
373                 failed(op->child);
374         }
375         if (!check_sense(sc, op->status)) {
376                 printf("[%d] READ10 \"%s\" failed (0x%02x) - expected %s\n", 
377                        op->child->line, op->fname, sc, op->status);
378                 failed(op->child);
379         }
380
381         op->child->bytes += xferlen*512;
382 }
383
384 static void scsi_readcapacity10(struct dbench_op *op)
385 {
386         struct scsi_device *sd;
387         unsigned char cdb[]={0x25,0,0,0,0,0,0,0,0,0};
388         int res;
389         int lba = op->params[0];
390         int pmi = op->params[1];
391         unsigned int data_size=8;
392         char data[data_size];
393         unsigned char sc;
394
395         cdb[2] = (lba>>24)&0xff;
396         cdb[3] = (lba>>16)&0xff;
397         cdb[4] = (lba>> 8)&0xff;
398         cdb[5] = (lba    )&0xff;
399
400         cdb[8] = (pmi?1:0);
401
402         sd = op->child->private;
403
404         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
405         if(res){
406                 printf("SCSI_IO failed\n");
407                 failed(op->child);
408         }
409         if (!check_sense(sc, op->status)) {
410                 printf("[%d] READCAPACITY10 \"%s\" failed (0x%02x) - expected %s\n", 
411                        op->child->line, op->fname, sc, op->status);
412                 failed(op->child);
413         }
414 }
415
416 static struct backend_op ops[] = {
417         { "READ6",            scsi_read6 },
418         { "READ10",           scsi_read10 },
419         { "READCAPACITY10",   scsi_readcapacity10 },
420         { "TESTUNITREADY",    scsi_testunitready },
421         { "WRITE10",          scsi_write10 },
422         { NULL, NULL}
423 };
424
425 struct nb_operations scsi_ops = {
426         .backend_name = "scsibench",
427         .setup        = scsi_setup,
428         .cleanup      = scsi_cleanup,
429         .ops          = ops
430 };
431
432 #endif /* HAVE_LINUX_SCSI_SG */