add a "warmup" argument so we can specify explicitely how long the warmup period...
[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
30 #define SCSI_TIMEOUT 5000 /* ms */
31
32 #define discard_const(ptr) ((void *)((intptr_t)(ptr)))
33
34 struct scsi_device {
35        int fd;
36 };
37
38 static void scsi_setup(struct child_struct *child)
39 {
40         int vers;
41         struct scsi_device *sd;
42
43         sd = malloc(sizeof(struct scsi_device));
44         if (sd == NULL) {
45                 printf("Failed to allocate scsi device structure\n");
46                         exit(10);
47         }
48         child->private=sd;
49         if((sd->fd=open(options.scsi_dev, O_RDWR))<0){
50                 printf("Failed to open scsi device node : %s\n", options.scsi_dev);
51                 free(sd);
52                 exit(10);
53         }
54         if ((ioctl(sd->fd, SG_GET_VERSION_NUM, &vers) < 0) || (vers < 30000)) {
55                 printf("%s is not a SCSI device node\n", options.scsi_dev);
56                 close(sd->fd);
57                 free(sd);
58                 exit(10);
59         }
60 }
61
62 static void scsi_cleanup(struct child_struct *child)
63 {
64         struct scsi_device *sd;
65
66         sd=child->private;
67         close(sd->fd);
68         sd->fd=-1;
69         free(sd);
70 }
71
72
73 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)
74 {
75         sg_io_hdr_t io_hdr;
76         unsigned int sense_len=32;
77         unsigned char sense[sense_len];
78
79         *sc = 0;
80
81         memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
82         io_hdr.interface_id = 'S';
83
84         /* CDB */
85         io_hdr.cmdp = cdb;
86         io_hdr.cmd_len = cdb_size;
87
88         /* Where to store the sense_data, if there was an error */
89         io_hdr.sbp = sense;
90         io_hdr.mx_sb_len = sense_len;
91         sense_len=0;
92
93         /* Transfer direction, either in or out. Linux does not yet
94            support bidirectional SCSI transfers ?
95          */
96         io_hdr.dxfer_direction = xfer_dir;
97
98         /* Where to store the DATA IN/OUT from the device and how big the
99            buffer is
100          */
101         io_hdr.dxferp = data;
102         io_hdr.dxfer_len = *data_size;
103
104         /* SCSI timeout in ms */
105         io_hdr.timeout = SCSI_TIMEOUT;
106
107
108         if(ioctl(fd, SG_IO, &io_hdr) < 0){
109                 perror("SG_IO ioctl failed");
110                 return -1;
111         }
112
113         /* now for the error processing */
114         if((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK){
115                 if(io_hdr.sb_len_wr > 0){
116                         sense_len=io_hdr.sb_len_wr;
117                         *sc=sense[2]&0x0f;
118                         return 0;
119                 }
120         }
121         if(io_hdr.masked_status){
122                 printf("SCSI status=0x%x\n", io_hdr.status);
123                 printf("SCSI masked_status=0x%x\n", io_hdr.masked_status);
124                 return -2;
125         }
126         if(io_hdr.host_status){
127                 printf("SCSI host_status=0x%x\n", io_hdr.host_status);
128                 return -3;
129         }
130         if(io_hdr.driver_status){
131                 printf("driver_status=0x%x\n", io_hdr.driver_status);
132                 return -4;
133         }
134
135         return 0;
136 }
137
138
139 static int check_sense(unsigned char sc, const char *expected)
140 {
141         if (strcmp(expected, "*") == 0){
142                 return 1;
143         }
144         if (strncmp(expected, "0x", 2) == 0) {
145                 return sc == strtol(expected, NULL, 16);
146         }
147         return 0;
148 }
149
150 static void failed(struct child_struct *child)
151 {
152         child->failed = 1;
153         printf("ERROR: child %d failed at line %d\n", child->id, child->line);
154         exit(1);
155 }
156
157 static void scsi_testunitready(struct dbench_op *op)
158 {
159         struct scsi_device *sd;
160         unsigned char cdb[]={0,0,0,0,0,0};
161         int res;
162         unsigned char sc;
163         unsigned int data_size=200;
164         char data[data_size];
165
166         sd = op->child->private;
167
168         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
169         if(res){
170                 printf("SCSI_IO failed\n");
171                 failed(op->child);
172         }
173         if (!check_sense(sc, op->status)) {
174                 printf("[%d] TESTUNITREADY \"%s\" failed (0x%02x) - expected %s\n", 
175                        op->child->line, op->fname, sc, op->status);
176                 failed(op->child);
177         }
178
179         return;
180 }
181
182
183 static void scsi_read6(struct dbench_op *op)
184 {
185         struct scsi_device *sd;
186         unsigned char cdb[]={0x08,0,0,0,0,0};
187         int res;
188         int lba = op->params[0];
189         int xferlen = op->params[1];
190         unsigned int data_size=1024*1024;
191         char data[data_size];
192         unsigned char sc;
193
194         cdb[1] = (lba>>16)&0x1f;
195         cdb[2] = (lba>> 8)&0xff;
196         cdb[3] = (lba    )&0xff;
197
198         cdb[4] = xferlen&0xff;
199         data_size = xferlen*512;
200
201         sd = op->child->private;
202
203         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
204         if(res){
205                 printf("SCSI_IO failed\n");
206                 failed(op->child);
207         }
208         if (!check_sense(sc, op->status)) {
209                 printf("[%d] READ6 \"%s\" failed (0x%02x) - expected %s\n", 
210                        op->child->line, op->fname, sc, op->status);
211                 failed(op->child);
212         }
213
214         op->child->bytes += xferlen*512;
215 }
216
217 static void scsi_read10(struct dbench_op *op)
218 {
219         struct scsi_device *sd;
220         unsigned char cdb[]={0x28,0,0,0,0,0,0,0,0,0};
221         int res;
222         int lba = op->params[0];
223         int xferlen = op->params[1];
224         int rd = op->params[2];
225         int grp = op->params[3];
226         unsigned int data_size=1024*1024;
227         char data[data_size];
228         unsigned char sc;
229
230         cdb[1] = rd;
231
232         cdb[2] = (lba>>24)&0xff;
233         cdb[3] = (lba>>16)&0xff;
234         cdb[4] = (lba>> 8)&0xff;
235         cdb[5] = (lba    )&0xff;
236
237         cdb[6] = grp&0x1f;
238
239         cdb[7] = (xferlen>>8)&0xff;
240         cdb[8] = xferlen&0xff;
241         data_size = xferlen*512;
242
243         sd = op->child->private;
244
245         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
246         if(res){
247                 printf("SCSI_IO failed\n");
248                 failed(op->child);
249         }
250         if (!check_sense(sc, op->status)) {
251                 printf("[%d] READ10 \"%s\" failed (0x%02x) - expected %s\n", 
252                        op->child->line, op->fname, sc, op->status);
253                 failed(op->child);
254         }
255
256         op->child->bytes += xferlen*512;
257 }
258
259 static void scsi_readcapacity10(struct dbench_op *op)
260 {
261         struct scsi_device *sd;
262         unsigned char cdb[]={0x25,0,0,0,0,0,0,0,0,0};
263         int res;
264         int lba = op->params[0];
265         int pmi = op->params[1];
266         unsigned int data_size=8;
267         char data[data_size];
268         unsigned char sc;
269
270         cdb[2] = (lba>>24)&0xff;
271         cdb[3] = (lba>>16)&0xff;
272         cdb[4] = (lba>> 8)&0xff;
273         cdb[5] = (lba    )&0xff;
274
275         cdb[8] = (pmi?1:0);
276
277         sd = op->child->private;
278
279         res=scsi_io(sd->fd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
280         if(res){
281                 printf("SCSI_IO failed\n");
282                 failed(op->child);
283         }
284         if (!check_sense(sc, op->status)) {
285                 printf("[%d] READCAPACITY10 \"%s\" failed (0x%02x) - expected %s\n", 
286                        op->child->line, op->fname, sc, op->status);
287                 failed(op->child);
288         }
289 }
290
291 static struct backend_op ops[] = {
292         { "READ6",            scsi_read6 },
293         { "READ10",           scsi_read10 },
294         { "READCAPACITY10",   scsi_readcapacity10 },
295         { "TESTUNITREADY",    scsi_testunitready },
296         { NULL, NULL}
297 };
298
299 struct nb_operations scsi_ops = {
300         .backend_name = "scsibench",
301         .setup        = scsi_setup,
302         .cleanup      = scsi_cleanup,
303         .ops          = ops
304 };
305
306 #endif /* HAVE_LINUX_SCSI_SG */