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