]> git.ozlabs.org Git - ccan/blob - ccan/iscsi/scsi-command.c
iscsi: new module from Ronnie.
[ccan] / ccan / iscsi / scsi-command.c
1 /* 
2    Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
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 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <arpa/inet.h>
22 #include "iscsi.h"
23 #include "iscsi-private.h"
24 #include "scsi-lowlevel.h"
25 #include "dlinklist.h"
26
27 struct iscsi_scsi_cbdata {
28        struct iscsi_scsi_cbdata *prev, *next;
29        iscsi_command_cb  callback;
30        void             *private_data;
31        struct scsi_task *task;
32 };
33
34 void iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata)
35 {
36         if (scsi_cbdata == NULL) {
37                 return;
38         }
39         if (scsi_cbdata->task == NULL) {
40                 scsi_free_scsi_task(scsi_cbdata->task);
41                 scsi_cbdata->task = NULL;
42         }
43         free(scsi_cbdata);
44 }
45
46 static void iscsi_scsi_response_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
47 {
48         struct iscsi_scsi_cbdata *scsi_cbdata = (struct iscsi_scsi_cbdata *)private_data;
49         struct scsi_task *task = command_data;
50
51         switch (status) {
52         case ISCSI_STATUS_CHECK_CONDITION:
53                 scsi_cbdata->callback(iscsi, ISCSI_STATUS_CHECK_CONDITION, task, scsi_cbdata->private_data);    
54                 return;
55         
56
57         case ISCSI_STATUS_GOOD:
58                 scsi_cbdata->callback(iscsi, ISCSI_STATUS_GOOD, task, scsi_cbdata->private_data);       
59                 return;
60         default:
61                 printf("Cant handle  scsi status %d yet\n", status);
62                 scsi_cbdata->callback(iscsi, ISCSI_STATUS_ERROR, task, scsi_cbdata->private_data);      
63         }
64 }
65
66
67 static int iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, struct scsi_task *task, iscsi_command_cb cb, struct iscsi_data *data, void *private_data)
68 {
69         struct iscsi_pdu *pdu;
70         struct iscsi_scsi_cbdata *scsi_cbdata;
71         int flags;
72
73         if (iscsi == NULL) {
74                 printf("trying to send command on NULL context\n");
75                 scsi_free_scsi_task(task);
76                 return -1;
77         }
78
79         if (iscsi->session_type != ISCSI_SESSION_NORMAL) {
80                 printf("Trying to send command on discovery session\n");
81                 scsi_free_scsi_task(task);
82                 return -2;
83         }
84
85         if (iscsi->is_loggedin == 0) {
86                 printf("Trying to send command while not logged in\n");
87                 scsi_free_scsi_task(task);
88                 return -3;
89         }
90
91         scsi_cbdata = malloc(sizeof(struct iscsi_scsi_cbdata));
92         if (scsi_cbdata == NULL) {
93                 printf("failed to allocate scsi cbdata\n");
94                 scsi_free_scsi_task(task);
95                 return -4;
96         }
97         bzero(scsi_cbdata, sizeof(struct iscsi_scsi_cbdata));
98         scsi_cbdata->task         = task;
99         scsi_cbdata->callback     = cb;
100         scsi_cbdata->private_data = private_data;
101
102         pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_SCSI_REQUEST, ISCSI_PDU_SCSI_RESPONSE);
103         if (pdu == NULL) {
104                 printf("Failed to allocate text pdu\n");
105                 iscsi_free_scsi_cbdata(scsi_cbdata);
106                 return -5;
107         }
108         pdu->scsi_cbdata = scsi_cbdata;
109
110         /* flags */
111         flags = ISCSI_PDU_SCSI_FINAL|ISCSI_PDU_SCSI_ATTR_SIMPLE;
112         switch (task->xfer_dir) {
113         case SCSI_XFER_NONE:
114                 break;
115         case SCSI_XFER_READ:
116                 flags |= ISCSI_PDU_SCSI_READ;
117                 break;
118         case SCSI_XFER_WRITE:
119                 flags |= ISCSI_PDU_SCSI_WRITE;
120                 if (data == NULL) {
121                         printf("DATA-OUT command but data == NULL\n");
122                         iscsi_free_pdu(iscsi, pdu);
123                         return -5;
124                 }
125                 if (data->size != task->expxferlen) {
126                         printf("data size:%d is not same as expected data transfer length:%d\n", data->size, task->expxferlen);
127                         iscsi_free_pdu(iscsi, pdu);
128                         return -7;
129                 }
130                 if (iscsi_pdu_add_data(iscsi, pdu, data->data, data->size) != 0) {
131                         printf("Failed to add outdata to the pdu\n");
132                         iscsi_free_pdu(iscsi, pdu);
133                         return -6;
134                 }
135
136                 break;
137         }
138         iscsi_pdu_set_pduflags(pdu, flags);
139
140         /* lun */
141         iscsi_pdu_set_lun(pdu, lun);
142
143         /* expxferlen */
144         iscsi_pdu_set_expxferlen(pdu, task->expxferlen);
145
146         /* cmdsn */
147         iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
148         pdu->cmdsn = iscsi->cmdsn;
149         iscsi->cmdsn++;
150
151         /* exp statsn */
152         iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1);
153                 
154         /* cdb */
155         iscsi_pdu_set_cdb(pdu, task);
156
157         pdu->callback     = iscsi_scsi_response_cb;
158         pdu->private_data = scsi_cbdata;
159
160         if (iscsi_queue_pdu(iscsi, pdu) != 0) {
161                 printf("failed to queue iscsi scsi pdu\n");
162                 iscsi_free_pdu(iscsi, pdu);
163                 return -6;
164         }
165
166         return 0;
167 }
168
169
170 int iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
171 {
172         int statsn, flags, response, status;
173         struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
174         struct scsi_task *task = scsi_cbdata->task;
175
176         statsn = ntohl(*(uint32_t *)&hdr[24]);
177         if (statsn > (int)iscsi->statsn) {
178                 iscsi->statsn = statsn;
179         }
180
181         flags = hdr[1];
182         if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
183                 printf("scsi response pdu but Final bit is not set: 0x%02x\n", flags);
184                 pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
185                 return -1;
186         }
187         if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
188                 printf("scsi response asked for ACK 0x%02x\n", flags);
189                 pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
190                 return -1;
191         }
192         /* for now, we ignore all overflow/underflow flags. We just print/log them so we can tweak
193          * libiscsi to not generate under/over flows
194          */
195         if ((flags&ISCSI_PDU_DATA_BIDIR_OVERFLOW) != 0) {
196                 printf("scsi response contains bidir overflow 0x%02x\n", flags);
197         }
198         if ((flags&ISCSI_PDU_DATA_BIDIR_UNDERFLOW) != 0) {
199                 printf("scsi response contains bidir underflow 0x%02x\n", flags);
200         }
201         if ((flags&ISCSI_PDU_DATA_RESIDUAL_OVERFLOW) != 0) {
202                 printf("scsi response contains residual overflow 0x%02x\n", flags);
203         }
204         if ((flags&ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW) != 0) {
205                 printf("scsi response contains residual underflow 0x%02x\n", flags);
206         }
207
208
209         response = hdr[2];
210         if (response != 0x00) {
211                 printf("scsi reply response field:%d\n", response);
212         }
213
214         status = hdr[3];
215
216         switch (status) {
217         case ISCSI_STATUS_GOOD:
218                 task->datain.data = pdu->indata.data;
219                 task->datain.size = pdu->indata.size;
220
221                 pdu->callback(iscsi, ISCSI_STATUS_GOOD, &pdu->indata, pdu->private_data);
222                 break;
223         case ISCSI_STATUS_CHECK_CONDITION:
224                 task->datain.data = discard_const(hdr + ISCSI_HEADER_SIZE);
225                 task->datain.size = size - ISCSI_HEADER_SIZE;
226
227                 task->sense.error_type = task->datain.data[2] & 0x7f;
228                 task->sense.key        = task->datain.data[4] & 0x0f;
229                 task->sense.ascq       = ntohs(*(uint16_t *)&(task->datain.data[14]));
230
231                 pdu->callback(iscsi, ISCSI_STATUS_CHECK_CONDITION, task, pdu->private_data);    
232                 break;
233         default:
234                 printf("Unknown status :%d\n", status);
235
236                 pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);      
237                 return -1;
238         }
239
240         return 0;
241 }
242
243 int iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size, int *is_finished)
244 {
245         int statsn, flags, status;
246         struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
247         struct scsi_task *task = scsi_cbdata->task;
248         int dsl;
249
250         statsn = ntohl(*(uint32_t *)&hdr[24]);
251         if (statsn > (int)iscsi->statsn) {
252                 iscsi->statsn = statsn;
253         }
254
255         flags = hdr[1];
256         if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
257                 printf("scsi response asked for ACK 0x%02x\n", flags);
258                 pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
259                 return -1;
260         }
261         /* for now, we ignore all overflow/underflow flags. We just print/log them so we can tweak
262          * libiscsi to not generate under/over flows
263          */
264         if ((flags&ISCSI_PDU_DATA_RESIDUAL_OVERFLOW) != 0) {
265                 printf("scsi response contains residual overflow 0x%02x\n", flags);
266         }
267         if ((flags&ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW) != 0) {
268                 printf("scsi response contains residual underflow 0x%02x\n", flags);
269         }
270
271         dsl = ntohl(*(uint32_t *)&hdr[4])&0x00ffffff;
272
273         if (dsl > size - ISCSI_HEADER_SIZE) {
274                 printf ("dsl is :%d, while buffser size if %d\n", dsl, size - ISCSI_HEADER_SIZE);
275         }
276
277         if (iscsi_add_data(&pdu->indata, discard_const(hdr + ISCSI_HEADER_SIZE), dsl, 0) != 0) {
278                 printf("failed to add data to pdu in buffer\n");
279                 return -3;
280         }
281
282
283         if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
284                 printf("scsi data-in without Final bit: 0x%02x\n", flags);
285                 *is_finished = 0;
286         }
287         if ((flags&ISCSI_PDU_DATA_CONTAINS_STATUS) == 0) {
288                 printf("scsi data-in without Status bit: 0x%02x\n", flags);
289                 *is_finished = 0;
290         }
291
292         if (*is_finished == 0) {
293                 return 0;
294         }
295
296
297         /* this was the final data-in packet in the sequence and it has the s-bit set, so invoke the
298          * callback.
299          */
300         status = hdr[3];
301         task->datain.data = pdu->indata.data;
302         task->datain.size = pdu->indata.size;
303
304         pdu->callback(iscsi, status, task, pdu->private_data);
305
306         return 0;
307 }
308
309
310
311
312 /*
313  * SCSI commands
314  */
315
316 int iscsi_testunitready_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data)
317 {
318         struct scsi_task *task;
319         int ret;
320
321         if ((task = scsi_cdb_testunitready()) == NULL) {
322                 printf("Failed to create testunitready cdb\n");
323                 return -1;
324         }
325         ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
326
327         return ret;
328 }
329
330
331 int iscsi_reportluns_async(struct iscsi_context *iscsi, iscsi_command_cb cb, int report_type, int alloc_len, void *private_data)
332 {
333         struct scsi_task *task;
334         int ret;
335
336         if (alloc_len < 16) {
337                 printf("Minimum allowed alloc len for reportluns is 16. You specified %d\n", alloc_len);
338                 return -1;
339         }
340
341         if ((task = scsi_reportluns_cdb(report_type, alloc_len)) == NULL) {
342                 printf("Failed to create reportluns cdb\n");
343                 return -2;
344         }
345         /* report luns are always sent to lun 0 */
346         ret = iscsi_scsi_command_async(iscsi, 0, task, cb, NULL, private_data);
347
348         return ret;
349 }
350
351 int iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int evpd, int page_code, int maxsize, void *private_data)
352 {
353         struct scsi_task *task;
354         int ret;
355
356         if ((task = scsi_cdb_inquiry(evpd, page_code, maxsize)) == NULL) {
357                 printf("Failed to create inquiry cdb\n");
358                 return -1;
359         }
360         ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
361
362         return ret;
363 }
364
365 int iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int pmi, void *private_data)
366 {
367         struct scsi_task *task;
368         int ret;
369
370         if ((task = scsi_cdb_readcapacity10(lba, pmi)) == NULL) {
371                 printf("Failed to create readcapacity10 cdb\n");
372                 return -1;
373         }
374         ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
375
376         return ret;
377 }
378
379 int iscsi_read10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int datalen, int blocksize, void *private_data)
380 {
381         struct scsi_task *task;
382         int ret;
383
384         if (datalen % blocksize != 0) {
385                 printf("datalen:%d is not a multiple of the blocksize:%d\n", datalen, blocksize);
386                 return -1;
387         }
388
389         if ((task = scsi_cdb_read10(lba, datalen, blocksize)) == NULL) {
390                 printf("Failed to create read10 cdb\n");
391                 return -2;
392         }
393         ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
394
395         return ret;
396 }
397
398
399 int iscsi_write10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, unsigned char *data, int datalen, int lba, int fua, int fuanv, int blocksize, void *private_data)
400 {
401         struct scsi_task *task;
402         struct iscsi_data outdata;
403         int ret;
404
405         if (datalen % blocksize != 0) {
406                 printf("datalen:%d is not a multiple of the blocksize:%d\n", datalen, blocksize);
407                 return -1;
408         }
409
410         if ((task = scsi_cdb_write10(lba, datalen, fua, fuanv, blocksize)) == NULL) {
411                 printf("Failed to create read10 cdb\n");
412                 return -2;
413         }
414
415         outdata.data = data;
416         outdata.size = datalen;
417
418         ret = iscsi_scsi_command_async(iscsi, lun, task, cb, &outdata, private_data);
419
420         return ret;
421 }
422
423 int iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, void *private_data)
424 {
425         struct scsi_task *task;
426         int ret;
427
428         if ((task = scsi_cdb_modesense6(dbd, pc, page_code, sub_page_code, alloc_len)) == NULL) {
429                 printf("Failed to create modesense6 cdb\n");
430                 return -2;
431         }
432         ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
433
434         return ret;
435 }
436