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