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