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