if we have a data-in without the S-bit, then there is more data to read
[tridge/dbench.git] / iscsi.c
1 /* 
2    Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2009
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 <sys/types.h>
23 #include <sys/socket.h>
24 #include <stdint.h>
25 #include "dbench.h"
26
27 #define discard_const(ptr) ((void *)((intptr_t)(ptr)))
28
29 #ifndef SG_DXFER_NONE
30 #define SG_DXFER_NONE -1
31 #endif
32 #ifndef SG_DXFER_TO_DEV
33 #define SG_DXFER_TO_DEV -2
34 #endif
35 #ifndef SG_DXFER_FROM_DEV
36 #define SG_DXFER_FROM_DEV -3
37 #endif
38
39 struct iscsi_device {
40        const char *portal;
41        const char *target;
42        int s;
43        uint64_t isid;
44        uint64_t blocks;
45        uint32_t itt;
46        uint32_t cmd_sn;
47        uint32_t exp_stat_sn;
48 };
49
50
51 void set_nonblocking(int fd)
52 {
53         unsigned v;
54         v = fcntl(fd, F_GETFL, 0);
55         fcntl(fd, F_SETFL, v | O_NONBLOCK);
56 }
57
58
59 struct login_param {
60        struct login_param *next;
61        char *arg;
62        char *value;
63 };
64 struct login_param *login_params;
65
66 static void add_login_param(char *arg, char *value)
67 {
68         struct login_param *new_param;
69
70         new_param = malloc(sizeof(struct login_param));
71         if (new_param == NULL) {
72                 printf("Failed to allocate login param struct\n");
73                 exit(10);
74         }
75         new_param->arg   = strdup(arg);
76         new_param->value = strdup(value);
77         new_param->next  = login_params;
78         login_params     = new_param;
79 }
80
81 static int send_iscsi_pdu(struct iscsi_device *sd, char *ish, char *data, int len)
82 {
83         char *buf, *ptr;
84         ssize_t remaining, count;
85
86         /* itt */
87         ish[16] = (sd->itt>>24)&0xff;
88         ish[17] = (sd->itt>>16)&0xff;
89         ish[18] = (sd->itt>> 8)&0xff;
90         ish[19] = (sd->itt    )&0xff;
91
92         /* command sequence number */
93         ish[24]  = (sd->cmd_sn>>24)&0xff;
94         ish[25]  = (sd->cmd_sn>>16)&0xff;
95         ish[26]  = (sd->cmd_sn>> 8)&0xff;
96         ish[27]  = (sd->cmd_sn    )&0xff;
97
98         /* expected stat sequence number */
99         ish[28]  = (sd->exp_stat_sn>>24)&0xff;
100         ish[29]  = (sd->exp_stat_sn>>16)&0xff;
101         ish[30]  = (sd->exp_stat_sn>> 8)&0xff;
102         ish[31]  = (sd->exp_stat_sn    )&0xff;
103
104         buf=malloc(48+len+4);
105         if (buf == NULL) {
106                 printf("Failed to allocate buffer for PDU of size %d bytes\n", 48+len);
107                 return -1;
108         }
109
110         memcpy(buf, ish, 48);
111         if (len > 0) {
112                 memcpy(buf+48, data, len);
113         }
114         remaining = 48 + len;
115         remaining = (remaining+3)&0xfffffc;
116         ptr = buf;
117         while(remaining > 0) {
118                 count = write(sd->s, ptr, remaining);
119                 if (count == -1) {
120                         printf("Write to socket failed with errno %d(%s)\n", errno, strerror(errno));
121                         free(buf);
122                         return -1;
123                 }
124                 remaining-= count;
125         }
126
127         free(buf);
128         return 0;
129 }
130
131 static int wait_for_pdu(struct iscsi_device *sd, char *ish, char *data, unsigned int *data_size)
132 {
133         char *buf, *ptr;
134         ssize_t total, remaining, count;
135         unsigned int itt, dsl;
136         uint32_t ecsn, ssn;
137
138         remaining = 48;
139         ptr = ish;
140         while (remaining > 0) {
141                 count = read(sd->s, ptr, remaining);
142                 if (count == -1) {
143                         printf("Read from socket failed with errno %d(%s)\n", errno, strerror(errno));
144                         return -1;
145                 }
146                 remaining-= count;
147                 ptr += count;
148         }
149
150         /* verify the itt */
151         itt  = (ish[16]&0xff)<<24;
152         itt |= (ish[17]&0xff)<<16;
153         itt |= (ish[18]&0xff)<< 8;
154         itt |= (ish[19]&0xff);
155         if (itt != sd->itt) {
156                 printf("Wrong ITT in PDU. Expected 0x%08x, got 0x%08x\n", sd->itt, itt);
157                 exit(10);
158         } 
159
160         /* data segment length */
161         dsl  = (ish[5]&0xff)<<16;
162         dsl |= (ish[6]&0xff)<<8;
163         dsl |= (ish[7]&0xff);
164
165         total = (dsl+3)&0xfffffffc;
166         remaining = total;
167         buf = malloc(remaining);
168         if (buf == NULL) {
169                 printf("Failed to alloc buf to read data into\n");
170                 return -1;
171         }
172         ptr = buf;
173         while (remaining > 0) {
174                 count = read(sd->s, ptr, remaining);
175
176                 if (count == -1) {
177                         printf("Read from socket failed with errno %d(%s)\n", errno, strerror(errno));
178                         return -1;
179                 }
180                 remaining-= count;
181                 ptr += count;
182         }
183
184         /* stat sequence number */
185         ssn  = (ish[24]&0xff)<<24;
186         ssn |= (ish[25]&0xff)<<16;
187         ssn |= (ish[26]&0xff)<<8;
188         ssn |= (ish[27]&0xff);
189         sd->exp_stat_sn = ssn+1;
190
191         /* expected command sequence number */
192         ecsn  = (ish[28]&0xff)<<24;
193         ecsn |= (ish[29]&0xff)<<16;
194         ecsn |= (ish[30]&0xff)<<8;
195         ecsn |= (ish[31]&0xff);
196         sd->cmd_sn = ecsn;
197
198         if (ish[0]&0x3f) {
199                 unsigned long int buffer_offset;
200
201                 buffer_offset  = (ish[40]&0xff)<<24;
202                 buffer_offset |= (ish[41]&0xff)<<16;
203                 buffer_offset |= (ish[42]&0xff)<<8;
204                 buffer_offset |= (ish[43]&0xff);
205
206                 if (buffer_offset == 0) {
207                         /* we only return the data from the first data-in pdu */
208                         if (data_size && *data_size > 0) {
209                                 if ((ssize_t)*data_size > total) {
210                                         *data_size = total;
211                                 }
212                                 if (data) {
213                                         memcpy(data, buf, *data_size);
214                                 }
215                         }
216                 }
217         }
218
219         free(buf);
220         return 0;
221 }
222
223 static int iscsi_login(struct child_struct *child, struct iscsi_device *sd)
224 {
225         char name[256];
226         char alias[256];
227         char ish[48];
228         int len;
229         struct login_param *login_param;
230         char *data, *ptr;
231
232         add_login_param("SessionType", "Normal");
233         add_login_param("HeaderDigest", "None");
234         add_login_param("DataDigest", "None");
235         add_login_param("DefaultTime2Wait", "0");
236         add_login_param("DefaultTime2Retain", "0");
237         add_login_param("InitialR2T", "Yes");
238         add_login_param("ImmediateData", "Yes");
239         add_login_param("MaxBurstLength", "16776192");
240         add_login_param("FirstBurstLength", "16776192");
241         add_login_param("MaxOutstandingR2T", "1");
242         add_login_param("MaxRecvDataSegmentLength", "16776192");
243         add_login_param("DataPDUInOrder", "Yes");
244         add_login_param("MaxConnections", "1");
245         add_login_param("TargetName", discard_const(sd->target));
246         sprintf(alias, "dbench:%d", child->id);
247         add_login_param("InitiatorAlias", alias);
248         sprintf(name, "iqn.2009-09.dbench:%d", child->id);
249         add_login_param("InitiatorName", name);
250
251
252         bzero(ish, 48);
253         /* opcode : LOGIN REQUEST (I) */
254         ish[0] = 0x43;
255
256         /* T CSG:op NSG:full feature */
257         ish[1] = 0x87;
258
259         /* data segment length */
260         for(login_param=login_params, len=0; login_param; login_param=login_param->next) {
261                 len += strlen(login_param->arg);
262                 len += 1;
263                 len += strlen(login_param->value);
264                 len += 1;
265         }
266         /* data segment length */
267         ish[5] = (len>>16)&0xff;
268         ish[6] = (len>> 8)&0xff;
269         ish[7] = (len    )&0xff;
270
271
272         /* isid */
273         ish[8] = (sd->isid>>40)&0xff;
274         ish[9] = (sd->isid>>32)&0xff;
275         ish[10] = (sd->isid>>24)&0xff;
276         ish[11] = (sd->isid>>16)&0xff;
277         ish[12] = (sd->isid>> 8)&0xff;
278         ish[13] = (sd->isid    )&0xff;
279         
280         data = malloc(len);
281         for(login_param=login_params, ptr=data; login_param; login_param=login_param->next) {
282                 strcpy(ptr,login_param->arg);
283                 ptr+=strlen(login_param->arg);
284                 *ptr='=';
285                 ptr++;
286                 strcpy(ptr,login_param->value);
287                 ptr+=strlen(login_param->value);
288                 *ptr=0;
289                 ptr++;
290         }
291
292         if (send_iscsi_pdu(sd, ish, data, len) != 0) {
293                 printf("Failed to send iscsi pdu\n");
294                 return -1;
295         }
296
297         if (wait_for_pdu(sd, ish, NULL, NULL) != 0) {
298                 printf("Failed to send iscsi pdu\n");
299                 return -1;
300         }
301
302
303
304         free(data);
305         return 0;
306 }
307
308
309
310
311
312
313
314
315
316
317
318 /* XXX merge with scsi.c */
319 static int check_sense(unsigned char sc, const char *expected)
320 {
321         if (strcmp(expected, "*") == 0){
322                 return 1;
323         }
324         if (strncmp(expected, "0x", 2) == 0) {
325                 return sc == strtol(expected, NULL, 16);
326         }
327         return 0;
328 }
329 static void failed(struct child_struct *child)
330 {
331         child->failed = 1;
332         printf("ERROR: child %d failed at line %d\n", child->id, child->line);
333         exit(1);
334 }
335
336
337
338 static int do_iscsi_io(struct iscsi_device *sd, unsigned char *cdb, unsigned char cdb_size, int xfer_dir, unsigned int *data_size, char *data, unsigned char *sc)
339 {
340         char ish[48];
341         int data_in_len=0, data_out_len=0;
342
343         bzero(ish, 48);
344
345         /* opcode : SCSI command */
346         ish[0] = 0x01;
347
348         /* flags */
349         ish[1] = 0x81; /* F + SIMPLE */
350         if (xfer_dir == SG_DXFER_FROM_DEV) {
351                 ish[1] |= 0x40;
352                 data_in_len= *data_size;
353                 data_out_len=0;
354         }
355         if (xfer_dir == SG_DXFER_TO_DEV) {
356                 ish[1] |= 0x20;
357
358                 /* data segment length */
359                 ish[5] = ((*data_size)>>16)&0xff;
360                 ish[6] = ((*data_size)>> 8)&0xff;
361                 ish[7] = ((*data_size)    )&0xff;
362
363                 data_in_len=0;
364                 data_out_len=*data_size;
365         }
366
367         /* lun */
368         ish[9] = options.iscsi_lun;
369
370         /* expected data xfer len */
371         ish[20]  = ((*data_size)>>24)&0xff;
372         ish[21]  = ((*data_size)>>16)&0xff;
373         ish[22]  = ((*data_size)>> 8)&0xff;
374         ish[23]  = ((*data_size)    )&0xff;
375
376         /* cdb */
377         memcpy(ish+32, cdb, cdb_size);
378
379         *data_size=data_out_len;
380         if (send_iscsi_pdu(sd, ish, data, *data_size) != 0) {
381                 printf("Failed to send iscsi pdu\n");
382                 return -1;
383         }
384
385 need_more_data:
386         *data_size=data_in_len;
387         if (wait_for_pdu(sd, ish, data, data_size) != 0) {
388                 printf("Failed to receive iscsi pdu\n");
389                 return -1;
390         }
391
392         switch (ish[0]&0x3f) {
393         case 0x21: /* SCSI response */
394                 if (ish[2] != 0) {
395                         printf("SCSI Response %d\n", ish[2]);
396                         sd->itt++;
397                         return -1;
398                 }
399                 if (ish[3] == 0) {
400                         *sc = 0;
401                         sd->itt++;
402                         return 0;
403                 }
404                 if (ish[3] == 2) {
405                         *sc = 2;
406                         sd->itt++;
407                         return 0;
408                 }
409                 break;
410         case 0x25: /* SCSI Data-In */
411                 if (ish[1]&0x01) {
412                         *sc = ish[3];
413                         sd->itt++;
414                         return 0;
415                 }
416                 /* no sbit, it means there is more data to read */
417                 goto need_more_data;
418                 break;
419         default:
420                 printf("got unsupported PDU:0x%02x\n", ish[0]&0x3f);
421         }
422
423         *sc = 0;
424         sd->itt++;
425         return 0;
426 }
427
428 static void iscsi_testunitready(struct dbench_op *op)
429 {
430         struct iscsi_device *sd;
431         unsigned char cdb[]={0,0,0,0,0,0};
432         int res;
433         unsigned char sc;
434         unsigned int data_size=0;
435
436         sd = op->child->private;
437
438         res=do_iscsi_io(sd, cdb, sizeof(cdb), SG_DXFER_NONE, &data_size, NULL, &sc);
439         if(res){
440                 printf("SCSI_IO failed\n");
441                 failed(op->child);
442         }
443         if (!check_sense(sc, op->status)) {
444                 printf("[%d] TESTUNITREADY \"%s\" failed (0x%02x) - expected %s\n", 
445                        op->child->line, op->fname, sc, op->status);
446                 failed(op->child);
447         }
448         return;
449 }
450
451 static void iscsi_read10(struct dbench_op *op)
452 {
453         struct iscsi_device *sd=op->child->private;
454         unsigned char cdb[]={0x28,0,0,0,0,0,0,0,0,0};
455         int res;
456         uint32_t lba = op->params[0];
457         uint32_t xferlen = op->params[1];
458         int rd = op->params[2];
459         int grp = op->params[3];
460         unsigned int data_size=1024*1024;
461         char data[data_size];
462         unsigned char sc;
463
464         lba = (lba / xferlen) * xferlen;
465
466         /* make sure we wrap properly instead of failing if the loadfile
467            is bigger than our device
468         */
469         if (sd->blocks <= lba) {
470                 lba = lba%sd->blocks;
471         }
472         if (sd->blocks <= lba+xferlen) {
473                 xferlen=1;
474         }
475
476         cdb[1] = rd;
477
478         cdb[2] = (lba>>24)&0xff;
479         cdb[3] = (lba>>16)&0xff;
480         cdb[4] = (lba>> 8)&0xff;
481         cdb[5] = (lba    )&0xff;
482
483         cdb[6] = grp&0x1f;
484
485         cdb[7] = (xferlen>>8)&0xff;
486         cdb[8] = xferlen&0xff;
487         data_size = xferlen*512;
488
489         res=do_iscsi_io(sd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
490         if(res){
491                 printf("SCSI_IO failed\n");
492                 failed(op->child);
493         }
494         if (!check_sense(sc, op->status)) {
495                 printf("[%d] READ10 \"%s\" failed (0x%02x) - expected %s\n", 
496                        op->child->line, op->fname, sc, op->status);
497                 failed(op->child);
498         }
499
500         op->child->bytes += xferlen*512;
501 }
502
503
504
505 static void local_iscsi_readcapacity10(struct dbench_op *op, uint64_t *blocks)
506 {
507         struct iscsi_device *sd;
508         unsigned char cdb[]={0x25,0,0,0,0,0,0,0,0,0};
509         int res;
510         int lba = op->params[0];
511         int pmi = op->params[1];
512         unsigned int data_size=8;
513         char data[data_size];
514         unsigned char sc;
515
516         cdb[2] = (lba>>24)&0xff;
517         cdb[3] = (lba>>16)&0xff;
518         cdb[4] = (lba>> 8)&0xff;
519         cdb[5] = (lba    )&0xff;
520
521         cdb[8] = (pmi?1:0);
522
523         sd = op->child->private;
524
525         res=do_iscsi_io(sd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
526         if(res){
527                 printf("SCSI_IO failed\n");
528                 failed(op->child);
529         }
530         if (!check_sense(sc, op->status)) {
531                 printf("[%d] READCAPACITY10 \"%s\" failed (0x%02x) - expected %s\n", 
532                        op->child->line, op->fname, sc, op->status);
533                 failed(op->child);
534         }
535
536         if (blocks) {
537                 *blocks  = (data[0]&0xff)<<24;
538                 *blocks |= (data[1]&0xff)<<16;
539                 *blocks |= (data[2]&0xff)<<8;
540                 *blocks |= (data[3]&0xff);
541         }
542 }
543
544 static void iscsi_readcapacity10(struct dbench_op *op)
545 {
546         return local_iscsi_readcapacity10(op, NULL);
547 }
548
549 static void iscsi_setup(struct child_struct *child)
550 {
551         struct iscsi_device *sd;
552         struct sockaddr_in sin;
553         struct dbench_op fake_op;
554
555         sd = malloc(sizeof(struct iscsi_device));
556         if (sd == NULL) {
557                 printf("Failed to allocate iscsi device structure\n");
558                 exit(10);
559         }
560         child->private=sd;
561
562         sd->portal=options.iscsi_portal;
563         sd->target=options.iscsi_target;
564         sd->isid  =0x0000800000000000ULL | child->id; 
565         sd->s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
566         if (sd->s == -1) {
567                 printf("could not open socket() errno:%d(%s)\n", errno, strerror(errno));
568                 exit(10);
569         }
570
571         sin.sin_family      = AF_INET;
572         sin.sin_port        = htons(options.iscsi_port);
573         if (inet_pton(AF_INET, sd->portal, &sin.sin_addr) != 1) {
574                 printf("Failed to convert \"%s\" into an address\n", sd->portal);
575                 exit(10);
576         }
577
578         if (connect(sd->s, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
579                 printf("connect failed with errno:%d(%s)\n", errno, strerror(errno));
580                 exit(10);
581         }
582
583         sd->itt=0x000a0000;
584         sd->cmd_sn=0;
585         sd->exp_stat_sn=0;
586         if (iscsi_login(child, sd) != 0) {
587                 printf("Failed to log in to target.\n");
588                 exit(10);
589         }
590
591         fake_op.child=child;
592         fake_op.status="*";
593         iscsi_testunitready(&fake_op);
594
595         fake_op.params[0]=0;
596         fake_op.params[1]=0;
597         fake_op.status="*";
598         local_iscsi_readcapacity10(&fake_op, &sd->blocks);
599 }
600
601        
602 static void iscsi_cleanup(struct child_struct *child)
603 {
604         struct iscsi_device *sd;
605
606         sd=child->private;
607         close(sd->s);
608         sd->s=-1;
609         free(sd);
610 }
611
612 static int iscsi_init(void)
613 {
614         struct iscsi_device *sd;
615         struct sockaddr_in sin;
616         struct dbench_op fake_op;
617         struct child_struct child;
618
619         sd = malloc(sizeof(struct iscsi_device));
620         if (sd == NULL) {
621                 printf("Failed to allocate iscsi device structure\n");
622                 return 1;
623         }
624         child.private=sd;
625         child.id=99999;
626
627         sd->portal=options.iscsi_portal;
628         sd->target=options.iscsi_target;
629         sd->isid  =0x0000800000000000ULL | child.id; 
630         sd->s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
631         if (sd->s == -1) {
632                 printf("could not open socket() errno:%d(%s)\n", errno, strerror(errno));
633                 return 1;
634         }
635
636         sin.sin_family      = AF_INET;
637         sin.sin_port        = htons(options.iscsi_port);
638         if (inet_pton(AF_INET, sd->portal, &sin.sin_addr) != 1) {
639                 printf("Failed to convert \"%s\" into an address\n", sd->portal);
640                 return 1;
641         }
642
643         if (connect(sd->s, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
644                 printf("connect failed with errno:%d(%s)\n", errno, strerror(errno));
645                 return 1;
646         }
647
648         sd->itt=0x000a0000;
649         sd->cmd_sn=0;
650         sd->exp_stat_sn=0;
651         if (iscsi_login(&child, sd) != 0) {
652                 printf("Failed to log in to target.\n");
653                 return 1;
654         }
655
656         fake_op.child=&child;
657         fake_op.status="*";
658         iscsi_testunitready(&fake_op);
659
660         fake_op.params[0]=0;
661         fake_op.params[1]=0;
662         fake_op.status="*";
663         local_iscsi_readcapacity10(&fake_op, &sd->blocks);
664
665         close(sd->s);
666         free(sd);
667
668         return 0;
669 }
670
671
672 static struct backend_op ops[] = {
673         { "TESTUNITREADY",    iscsi_testunitready },
674         { "READ10",           iscsi_read10 },
675         { "READCAPACITY10",   iscsi_readcapacity10 },
676         { NULL, NULL}
677 };
678
679 struct nb_operations iscsi_ops = {
680         .backend_name = "iscsibench",
681         .init         = iscsi_init,
682         .setup        = iscsi_setup,
683         .cleanup      = iscsi_cleanup,
684         .ops          = ops
685 };
686
687