xref: /qemu/block/iscsi.c (revision 4f6fd349)
1c589b249SRonnie Sahlberg /*
2c589b249SRonnie Sahlberg  * QEMU Block driver for iSCSI images
3c589b249SRonnie Sahlberg  *
4c589b249SRonnie Sahlberg  * Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
5c589b249SRonnie Sahlberg  *
6c589b249SRonnie Sahlberg  * Permission is hereby granted, free of charge, to any person obtaining a copy
7c589b249SRonnie Sahlberg  * of this software and associated documentation files (the "Software"), to deal
8c589b249SRonnie Sahlberg  * in the Software without restriction, including without limitation the rights
9c589b249SRonnie Sahlberg  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10c589b249SRonnie Sahlberg  * copies of the Software, and to permit persons to whom the Software is
11c589b249SRonnie Sahlberg  * furnished to do so, subject to the following conditions:
12c589b249SRonnie Sahlberg  *
13c589b249SRonnie Sahlberg  * The above copyright notice and this permission notice shall be included in
14c589b249SRonnie Sahlberg  * all copies or substantial portions of the Software.
15c589b249SRonnie Sahlberg  *
16c589b249SRonnie Sahlberg  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17c589b249SRonnie Sahlberg  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18c589b249SRonnie Sahlberg  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19c589b249SRonnie Sahlberg  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20c589b249SRonnie Sahlberg  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21c589b249SRonnie Sahlberg  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22c589b249SRonnie Sahlberg  * THE SOFTWARE.
23c589b249SRonnie Sahlberg  */
24c589b249SRonnie Sahlberg 
25c589b249SRonnie Sahlberg #include "config-host.h"
26c589b249SRonnie Sahlberg 
27c589b249SRonnie Sahlberg #include <poll.h>
28f4dfa67fSRonnie Sahlberg #include <arpa/inet.h>
29c589b249SRonnie Sahlberg #include "qemu-common.h"
301de7afc9SPaolo Bonzini #include "qemu/config-file.h"
311de7afc9SPaolo Bonzini #include "qemu/error-report.h"
32737e150eSPaolo Bonzini #include "block/block_int.h"
33c589b249SRonnie Sahlberg #include "trace.h"
340d09e41aSPaolo Bonzini #include "block/scsi.h"
350a53f010SRonnie Sahlberg #include "qemu/iov.h"
36c589b249SRonnie Sahlberg 
37c589b249SRonnie Sahlberg #include <iscsi/iscsi.h>
38c589b249SRonnie Sahlberg #include <iscsi/scsi-lowlevel.h>
39c589b249SRonnie Sahlberg 
4098392453SRonnie Sahlberg #ifdef __linux__
4198392453SRonnie Sahlberg #include <scsi/sg.h>
420d09e41aSPaolo Bonzini #include <block/scsi.h>
4398392453SRonnie Sahlberg #endif
44c589b249SRonnie Sahlberg 
45c589b249SRonnie Sahlberg typedef struct IscsiLun {
46c589b249SRonnie Sahlberg     struct iscsi_context *iscsi;
47c589b249SRonnie Sahlberg     int lun;
48dbfff6d7SRonnie Sahlberg     enum scsi_inquiry_peripheral_device_type type;
49c589b249SRonnie Sahlberg     int block_size;
50c7b4a952SPaolo Bonzini     uint64_t num_blocks;
51c9b9f682SRonnie Sahlberg     int events;
525b5d34ecSPeter Lieven     QEMUTimer *nop_timer;
53c589b249SRonnie Sahlberg } IscsiLun;
54c589b249SRonnie Sahlberg 
55c589b249SRonnie Sahlberg typedef struct IscsiAIOCB {
56c589b249SRonnie Sahlberg     BlockDriverAIOCB common;
57c589b249SRonnie Sahlberg     QEMUIOVector *qiov;
58c589b249SRonnie Sahlberg     QEMUBH *bh;
59c589b249SRonnie Sahlberg     IscsiLun *iscsilun;
60c589b249SRonnie Sahlberg     struct scsi_task *task;
61c589b249SRonnie Sahlberg     uint8_t *buf;
62c589b249SRonnie Sahlberg     int status;
63c589b249SRonnie Sahlberg     int canceled;
641dde716eSPeter Lieven     int retries;
651dde716eSPeter Lieven     int64_t sector_num;
661dde716eSPeter Lieven     int nb_sectors;
6798392453SRonnie Sahlberg #ifdef __linux__
6898392453SRonnie Sahlberg     sg_io_hdr_t *ioh;
6998392453SRonnie Sahlberg #endif
70c589b249SRonnie Sahlberg } IscsiAIOCB;
71c589b249SRonnie Sahlberg 
725b5d34ecSPeter Lieven #define NOP_INTERVAL 5000
735b5d34ecSPeter Lieven #define MAX_NOP_FAILURES 3
741dde716eSPeter Lieven #define ISCSI_CMD_RETRIES 5
755b5d34ecSPeter Lieven 
76c589b249SRonnie Sahlberg static void
77cfb3f506SPaolo Bonzini iscsi_bh_cb(void *p)
7827cbd828SPaolo Bonzini {
7927cbd828SPaolo Bonzini     IscsiAIOCB *acb = p;
8027cbd828SPaolo Bonzini 
8127cbd828SPaolo Bonzini     qemu_bh_delete(acb->bh);
8227cbd828SPaolo Bonzini 
834790b03dSPaolo Bonzini     g_free(acb->buf);
844790b03dSPaolo Bonzini     acb->buf = NULL;
854790b03dSPaolo Bonzini 
8627cbd828SPaolo Bonzini     if (acb->canceled == 0) {
8727cbd828SPaolo Bonzini         acb->common.cb(acb->common.opaque, acb->status);
8827cbd828SPaolo Bonzini     }
8927cbd828SPaolo Bonzini 
901bd075f2SPaolo Bonzini     if (acb->task != NULL) {
911bd075f2SPaolo Bonzini         scsi_free_scsi_task(acb->task);
921bd075f2SPaolo Bonzini         acb->task = NULL;
931bd075f2SPaolo Bonzini     }
941bd075f2SPaolo Bonzini 
9527cbd828SPaolo Bonzini     qemu_aio_release(acb);
9627cbd828SPaolo Bonzini }
9727cbd828SPaolo Bonzini 
98cfb3f506SPaolo Bonzini static void
99cfb3f506SPaolo Bonzini iscsi_schedule_bh(IscsiAIOCB *acb)
10027cbd828SPaolo Bonzini {
1011bd075f2SPaolo Bonzini     if (acb->bh) {
1021bd075f2SPaolo Bonzini         return;
1031bd075f2SPaolo Bonzini     }
104cfb3f506SPaolo Bonzini     acb->bh = qemu_bh_new(iscsi_bh_cb, acb);
10527cbd828SPaolo Bonzini     qemu_bh_schedule(acb->bh);
10627cbd828SPaolo Bonzini }
10727cbd828SPaolo Bonzini 
10827cbd828SPaolo Bonzini 
10927cbd828SPaolo Bonzini static void
110c589b249SRonnie Sahlberg iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
111c589b249SRonnie Sahlberg                     void *private_data)
112c589b249SRonnie Sahlberg {
1131bd075f2SPaolo Bonzini     IscsiAIOCB *acb = private_data;
1141bd075f2SPaolo Bonzini 
1151bd075f2SPaolo Bonzini     acb->status = -ECANCELED;
1161bd075f2SPaolo Bonzini     iscsi_schedule_bh(acb);
117c589b249SRonnie Sahlberg }
118c589b249SRonnie Sahlberg 
119c589b249SRonnie Sahlberg static void
120c589b249SRonnie Sahlberg iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
121c589b249SRonnie Sahlberg {
122c589b249SRonnie Sahlberg     IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
123c589b249SRonnie Sahlberg     IscsiLun *iscsilun = acb->iscsilun;
124c589b249SRonnie Sahlberg 
1251bd075f2SPaolo Bonzini     if (acb->status != -EINPROGRESS) {
1261bd075f2SPaolo Bonzini         return;
1271bd075f2SPaolo Bonzini     }
1281bd075f2SPaolo Bonzini 
129c589b249SRonnie Sahlberg     acb->canceled = 1;
130c589b249SRonnie Sahlberg 
131b2090919SPaolo Bonzini     /* send a task mgmt call to the target to cancel the task on the target */
13264e69e80SStefan Priebe     iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
1331bd075f2SPaolo Bonzini                                      iscsi_abort_task_cb, acb);
134b2090919SPaolo Bonzini 
1351bd075f2SPaolo Bonzini     while (acb->status == -EINPROGRESS) {
1361bd075f2SPaolo Bonzini         qemu_aio_wait();
1371bd075f2SPaolo Bonzini     }
138c589b249SRonnie Sahlberg }
139c589b249SRonnie Sahlberg 
140d7331bedSStefan Hajnoczi static const AIOCBInfo iscsi_aiocb_info = {
141c589b249SRonnie Sahlberg     .aiocb_size         = sizeof(IscsiAIOCB),
142c589b249SRonnie Sahlberg     .cancel             = iscsi_aio_cancel,
143c589b249SRonnie Sahlberg };
144c589b249SRonnie Sahlberg 
145c589b249SRonnie Sahlberg 
146c589b249SRonnie Sahlberg static void iscsi_process_read(void *arg);
147c589b249SRonnie Sahlberg static void iscsi_process_write(void *arg);
148c589b249SRonnie Sahlberg 
149c589b249SRonnie Sahlberg static void
150c589b249SRonnie Sahlberg iscsi_set_events(IscsiLun *iscsilun)
151c589b249SRonnie Sahlberg {
152c589b249SRonnie Sahlberg     struct iscsi_context *iscsi = iscsilun->iscsi;
153c9b9f682SRonnie Sahlberg     int ev;
154c589b249SRonnie Sahlberg 
155c9b9f682SRonnie Sahlberg     /* We always register a read handler.  */
156c9b9f682SRonnie Sahlberg     ev = POLLIN;
157c9b9f682SRonnie Sahlberg     ev |= iscsi_which_events(iscsi);
158c9b9f682SRonnie Sahlberg     if (ev != iscsilun->events) {
159c9b9f682SRonnie Sahlberg         qemu_aio_set_fd_handler(iscsi_get_fd(iscsi),
160c9b9f682SRonnie Sahlberg                       iscsi_process_read,
161c9b9f682SRonnie Sahlberg                       (ev & POLLOUT) ? iscsi_process_write : NULL,
162c9b9f682SRonnie Sahlberg                       iscsilun);
163c9b9f682SRonnie Sahlberg 
164c9b9f682SRonnie Sahlberg     }
165c9b9f682SRonnie Sahlberg 
166c9b9f682SRonnie Sahlberg     iscsilun->events = ev;
167c589b249SRonnie Sahlberg }
168c589b249SRonnie Sahlberg 
169c589b249SRonnie Sahlberg static void
170c589b249SRonnie Sahlberg iscsi_process_read(void *arg)
171c589b249SRonnie Sahlberg {
172c589b249SRonnie Sahlberg     IscsiLun *iscsilun = arg;
173c589b249SRonnie Sahlberg     struct iscsi_context *iscsi = iscsilun->iscsi;
174c589b249SRonnie Sahlberg 
175c589b249SRonnie Sahlberg     iscsi_service(iscsi, POLLIN);
176c589b249SRonnie Sahlberg     iscsi_set_events(iscsilun);
177c589b249SRonnie Sahlberg }
178c589b249SRonnie Sahlberg 
179c589b249SRonnie Sahlberg static void
180c589b249SRonnie Sahlberg iscsi_process_write(void *arg)
181c589b249SRonnie Sahlberg {
182c589b249SRonnie Sahlberg     IscsiLun *iscsilun = arg;
183c589b249SRonnie Sahlberg     struct iscsi_context *iscsi = iscsilun->iscsi;
184c589b249SRonnie Sahlberg 
185c589b249SRonnie Sahlberg     iscsi_service(iscsi, POLLOUT);
186c589b249SRonnie Sahlberg     iscsi_set_events(iscsilun);
187c589b249SRonnie Sahlberg }
188c589b249SRonnie Sahlberg 
1891dde716eSPeter Lieven static int
1901dde716eSPeter Lieven iscsi_aio_writev_acb(IscsiAIOCB *acb);
191c589b249SRonnie Sahlberg 
192c589b249SRonnie Sahlberg static void
193f4dfa67fSRonnie Sahlberg iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status,
194c589b249SRonnie Sahlberg                      void *command_data, void *opaque)
195c589b249SRonnie Sahlberg {
196c589b249SRonnie Sahlberg     IscsiAIOCB *acb = opaque;
197c589b249SRonnie Sahlberg 
198f4dfa67fSRonnie Sahlberg     trace_iscsi_aio_write16_cb(iscsi, status, acb, acb->canceled);
199c589b249SRonnie Sahlberg 
200c589b249SRonnie Sahlberg     g_free(acb->buf);
2014790b03dSPaolo Bonzini     acb->buf = NULL;
202c589b249SRonnie Sahlberg 
203b2090919SPaolo Bonzini     if (acb->canceled != 0) {
204c589b249SRonnie Sahlberg         return;
205c589b249SRonnie Sahlberg     }
206c589b249SRonnie Sahlberg 
207c589b249SRonnie Sahlberg     acb->status = 0;
2081dde716eSPeter Lieven     if (status != 0) {
2091dde716eSPeter Lieven         if (status == SCSI_STATUS_CHECK_CONDITION
2101dde716eSPeter Lieven             && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
2111dde716eSPeter Lieven             && acb->retries-- > 0) {
2121dde716eSPeter Lieven             scsi_free_scsi_task(acb->task);
2131dde716eSPeter Lieven             acb->task = NULL;
2141dde716eSPeter Lieven             if (iscsi_aio_writev_acb(acb) == 0) {
2151dde716eSPeter Lieven                 iscsi_set_events(acb->iscsilun);
2161dde716eSPeter Lieven                 return;
2171dde716eSPeter Lieven             }
2181dde716eSPeter Lieven         }
219f4dfa67fSRonnie Sahlberg         error_report("Failed to write16 data to iSCSI lun. %s",
220c589b249SRonnie Sahlberg                      iscsi_get_error(iscsi));
221c589b249SRonnie Sahlberg         acb->status = -EIO;
222c589b249SRonnie Sahlberg     }
223c589b249SRonnie Sahlberg 
224cfb3f506SPaolo Bonzini     iscsi_schedule_bh(acb);
225c589b249SRonnie Sahlberg }
226c589b249SRonnie Sahlberg 
2270777b5ddSPeter Lieven static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun)
2280777b5ddSPeter Lieven {
2290777b5ddSPeter Lieven     return sector * iscsilun->block_size / BDRV_SECTOR_SIZE;
2300777b5ddSPeter Lieven }
2310777b5ddSPeter Lieven 
232c589b249SRonnie Sahlberg static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
233c589b249SRonnie Sahlberg {
234c589b249SRonnie Sahlberg     return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
235c589b249SRonnie Sahlberg }
236c589b249SRonnie Sahlberg 
23791bea4e2SPeter Lieven static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors,
23891bea4e2SPeter Lieven                                       IscsiLun *iscsilun)
23991bea4e2SPeter Lieven {
24091bea4e2SPeter Lieven     if ((sector_num * BDRV_SECTOR_SIZE) % iscsilun->block_size ||
24191bea4e2SPeter Lieven         (nb_sectors * BDRV_SECTOR_SIZE) % iscsilun->block_size) {
242f5075224SRichard W.M. Jones             error_report("iSCSI misaligned request: "
243f5075224SRichard W.M. Jones                          "iscsilun->block_size %u, sector_num %" PRIi64
244f5075224SRichard W.M. Jones                          ", nb_sectors %d",
24591bea4e2SPeter Lieven                          iscsilun->block_size, sector_num, nb_sectors);
24691bea4e2SPeter Lieven             return 0;
24791bea4e2SPeter Lieven     }
24891bea4e2SPeter Lieven     return 1;
24991bea4e2SPeter Lieven }
25091bea4e2SPeter Lieven 
2511dde716eSPeter Lieven static int
2521dde716eSPeter Lieven iscsi_aio_writev_acb(IscsiAIOCB *acb)
253c589b249SRonnie Sahlberg {
2541dde716eSPeter Lieven     struct iscsi_context *iscsi = acb->iscsilun->iscsi;
255c589b249SRonnie Sahlberg     size_t size;
256f4dfa67fSRonnie Sahlberg     uint32_t num_sectors;
257f4dfa67fSRonnie Sahlberg     uint64_t lba;
2587371d56fSPeter Lieven #if !defined(LIBISCSI_FEATURE_IOVECTOR)
259f4dfa67fSRonnie Sahlberg     struct iscsi_data data;
2607371d56fSPeter Lieven #endif
2617371d56fSPeter Lieven     int ret;
262c589b249SRonnie Sahlberg 
263c589b249SRonnie Sahlberg     acb->canceled   = 0;
2641bd075f2SPaolo Bonzini     acb->bh         = NULL;
2651bd075f2SPaolo Bonzini     acb->status     = -EINPROGRESS;
2664790b03dSPaolo Bonzini     acb->buf        = NULL;
267c589b249SRonnie Sahlberg 
268c589b249SRonnie Sahlberg     /* this will allow us to get rid of 'buf' completely */
2691dde716eSPeter Lieven     size = acb->nb_sectors * BDRV_SECTOR_SIZE;
2707371d56fSPeter Lieven 
2717371d56fSPeter Lieven #if !defined(LIBISCSI_FEATURE_IOVECTOR)
2724cc841b5SPeter Lieven     data.size = MIN(size, acb->qiov->size);
2734cc841b5SPeter Lieven 
2744cc841b5SPeter Lieven     /* if the iovec only contains one buffer we can pass it directly */
2754cc841b5SPeter Lieven     if (acb->qiov->niov == 1) {
2764cc841b5SPeter Lieven         data.data = acb->qiov->iov[0].iov_base;
2774cc841b5SPeter Lieven     } else {
2784cc841b5SPeter Lieven         acb->buf = g_malloc(data.size);
2794cc841b5SPeter Lieven         qemu_iovec_to_buf(acb->qiov, 0, acb->buf, data.size);
2804cc841b5SPeter Lieven         data.data = acb->buf;
2814cc841b5SPeter Lieven     }
2827371d56fSPeter Lieven #endif
283f4dfa67fSRonnie Sahlberg 
284f4dfa67fSRonnie Sahlberg     acb->task = malloc(sizeof(struct scsi_task));
285c589b249SRonnie Sahlberg     if (acb->task == NULL) {
286f4dfa67fSRonnie Sahlberg         error_report("iSCSI: Failed to allocate task for scsi WRITE16 "
287f4dfa67fSRonnie Sahlberg                      "command. %s", iscsi_get_error(iscsi));
2881dde716eSPeter Lieven         return -1;
289f4dfa67fSRonnie Sahlberg     }
290f4dfa67fSRonnie Sahlberg     memset(acb->task, 0, sizeof(struct scsi_task));
291f4dfa67fSRonnie Sahlberg 
292f4dfa67fSRonnie Sahlberg     acb->task->xfer_dir = SCSI_XFER_WRITE;
293f4dfa67fSRonnie Sahlberg     acb->task->cdb_size = 16;
294f4dfa67fSRonnie Sahlberg     acb->task->cdb[0] = 0x8a;
2951dde716eSPeter Lieven     lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
296f4dfa67fSRonnie Sahlberg     *(uint32_t *)&acb->task->cdb[2]  = htonl(lba >> 32);
297f4dfa67fSRonnie Sahlberg     *(uint32_t *)&acb->task->cdb[6]  = htonl(lba & 0xffffffff);
2980777b5ddSPeter Lieven     num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
299f4dfa67fSRonnie Sahlberg     *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
300f4dfa67fSRonnie Sahlberg     acb->task->expxferlen = size;
301f4dfa67fSRonnie Sahlberg 
3027371d56fSPeter Lieven #if defined(LIBISCSI_FEATURE_IOVECTOR)
3031dde716eSPeter Lieven     ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
3047371d56fSPeter Lieven                                    iscsi_aio_write16_cb,
3057371d56fSPeter Lieven                                    NULL,
3067371d56fSPeter Lieven                                    acb);
3077371d56fSPeter Lieven #else
3081dde716eSPeter Lieven     ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
309f4dfa67fSRonnie Sahlberg                                    iscsi_aio_write16_cb,
310f4dfa67fSRonnie Sahlberg                                    &data,
3117371d56fSPeter Lieven                                    acb);
3127371d56fSPeter Lieven #endif
3137371d56fSPeter Lieven     if (ret != 0) {
314f0d2a4d4SPaolo Bonzini         scsi_free_scsi_task(acb->task);
315c589b249SRonnie Sahlberg         g_free(acb->buf);
3161dde716eSPeter Lieven         return -1;
317c589b249SRonnie Sahlberg     }
318c589b249SRonnie Sahlberg 
3197371d56fSPeter Lieven #if defined(LIBISCSI_FEATURE_IOVECTOR)
3207371d56fSPeter Lieven     scsi_task_set_iov_out(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov);
3217371d56fSPeter Lieven #endif
3227371d56fSPeter Lieven 
3231dde716eSPeter Lieven     return 0;
3241dde716eSPeter Lieven }
325c589b249SRonnie Sahlberg 
3261dde716eSPeter Lieven static BlockDriverAIOCB *
3271dde716eSPeter Lieven iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
3281dde716eSPeter Lieven                  QEMUIOVector *qiov, int nb_sectors,
3291dde716eSPeter Lieven                  BlockDriverCompletionFunc *cb,
3301dde716eSPeter Lieven                  void *opaque)
3311dde716eSPeter Lieven {
3321dde716eSPeter Lieven     IscsiLun *iscsilun = bs->opaque;
3331dde716eSPeter Lieven     IscsiAIOCB *acb;
3341dde716eSPeter Lieven 
33591bea4e2SPeter Lieven     if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
33691bea4e2SPeter Lieven         return NULL;
33791bea4e2SPeter Lieven     }
33891bea4e2SPeter Lieven 
3391dde716eSPeter Lieven     acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
3401dde716eSPeter Lieven     trace_iscsi_aio_writev(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
3411dde716eSPeter Lieven 
3421dde716eSPeter Lieven     acb->iscsilun    = iscsilun;
3431dde716eSPeter Lieven     acb->qiov        = qiov;
3441dde716eSPeter Lieven     acb->nb_sectors  = nb_sectors;
3451dde716eSPeter Lieven     acb->sector_num  = sector_num;
3461dde716eSPeter Lieven     acb->retries     = ISCSI_CMD_RETRIES;
3471dde716eSPeter Lieven 
3481dde716eSPeter Lieven     if (iscsi_aio_writev_acb(acb) != 0) {
3491dde716eSPeter Lieven         qemu_aio_release(acb);
3501dde716eSPeter Lieven         return NULL;
3511dde716eSPeter Lieven     }
3521dde716eSPeter Lieven 
3531dde716eSPeter Lieven     iscsi_set_events(iscsilun);
354c589b249SRonnie Sahlberg     return &acb->common;
355c589b249SRonnie Sahlberg }
356c589b249SRonnie Sahlberg 
3571dde716eSPeter Lieven static int
3581dde716eSPeter Lieven iscsi_aio_readv_acb(IscsiAIOCB *acb);
3591dde716eSPeter Lieven 
360c589b249SRonnie Sahlberg static void
361f4dfa67fSRonnie Sahlberg iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status,
362c589b249SRonnie Sahlberg                     void *command_data, void *opaque)
363c589b249SRonnie Sahlberg {
364c589b249SRonnie Sahlberg     IscsiAIOCB *acb = opaque;
365c589b249SRonnie Sahlberg 
366f4dfa67fSRonnie Sahlberg     trace_iscsi_aio_read16_cb(iscsi, status, acb, acb->canceled);
367c589b249SRonnie Sahlberg 
368b2090919SPaolo Bonzini     if (acb->canceled != 0) {
369c589b249SRonnie Sahlberg         return;
370c589b249SRonnie Sahlberg     }
371c589b249SRonnie Sahlberg 
372c589b249SRonnie Sahlberg     acb->status = 0;
373c589b249SRonnie Sahlberg     if (status != 0) {
3741dde716eSPeter Lieven         if (status == SCSI_STATUS_CHECK_CONDITION
3751dde716eSPeter Lieven             && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
3761dde716eSPeter Lieven             && acb->retries-- > 0) {
3771dde716eSPeter Lieven             scsi_free_scsi_task(acb->task);
3781dde716eSPeter Lieven             acb->task = NULL;
3791dde716eSPeter Lieven             if (iscsi_aio_readv_acb(acb) == 0) {
3801dde716eSPeter Lieven                 iscsi_set_events(acb->iscsilun);
3811dde716eSPeter Lieven                 return;
3821dde716eSPeter Lieven             }
3831dde716eSPeter Lieven         }
384f4dfa67fSRonnie Sahlberg         error_report("Failed to read16 data from iSCSI lun. %s",
385c589b249SRonnie Sahlberg                      iscsi_get_error(iscsi));
386c589b249SRonnie Sahlberg         acb->status = -EIO;
387c589b249SRonnie Sahlberg     }
388c589b249SRonnie Sahlberg 
389cfb3f506SPaolo Bonzini     iscsi_schedule_bh(acb);
390c589b249SRonnie Sahlberg }
391c589b249SRonnie Sahlberg 
3921dde716eSPeter Lieven static int
3931dde716eSPeter Lieven iscsi_aio_readv_acb(IscsiAIOCB *acb)
394c589b249SRonnie Sahlberg {
3951dde716eSPeter Lieven     struct iscsi_context *iscsi = acb->iscsilun->iscsi;
3967e4d5a9fSPeter Lieven     size_t size;
3971dde716eSPeter Lieven     uint64_t lba;
3981dde716eSPeter Lieven     uint32_t num_sectors;
3991dde716eSPeter Lieven     int ret;
4007371d56fSPeter Lieven #if !defined(LIBISCSI_FEATURE_IOVECTOR)
401c589b249SRonnie Sahlberg     int i;
4027371d56fSPeter Lieven #endif
403c589b249SRonnie Sahlberg 
404c589b249SRonnie Sahlberg     acb->canceled    = 0;
4051bd075f2SPaolo Bonzini     acb->bh          = NULL;
4061bd075f2SPaolo Bonzini     acb->status      = -EINPROGRESS;
407c589b249SRonnie Sahlberg     acb->buf         = NULL;
408c589b249SRonnie Sahlberg 
4097e4d5a9fSPeter Lieven     size = acb->nb_sectors * BDRV_SECTOR_SIZE;
410f4dfa67fSRonnie Sahlberg 
411f4dfa67fSRonnie Sahlberg     acb->task = malloc(sizeof(struct scsi_task));
412c589b249SRonnie Sahlberg     if (acb->task == NULL) {
413f4dfa67fSRonnie Sahlberg         error_report("iSCSI: Failed to allocate task for scsi READ16 "
414f4dfa67fSRonnie Sahlberg                      "command. %s", iscsi_get_error(iscsi));
4151dde716eSPeter Lieven         return -1;
416f4dfa67fSRonnie Sahlberg     }
417f4dfa67fSRonnie Sahlberg     memset(acb->task, 0, sizeof(struct scsi_task));
418f4dfa67fSRonnie Sahlberg 
419f4dfa67fSRonnie Sahlberg     acb->task->xfer_dir = SCSI_XFER_READ;
4207e4d5a9fSPeter Lieven     acb->task->expxferlen = size;
4211dde716eSPeter Lieven     lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
4227e4d5a9fSPeter Lieven     num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
423f4dfa67fSRonnie Sahlberg 
4241dde716eSPeter Lieven     switch (acb->iscsilun->type) {
425f4dfa67fSRonnie Sahlberg     case TYPE_DISK:
426f4dfa67fSRonnie Sahlberg         acb->task->cdb_size = 16;
427f4dfa67fSRonnie Sahlberg         acb->task->cdb[0]  = 0x88;
428f4dfa67fSRonnie Sahlberg         *(uint32_t *)&acb->task->cdb[2]  = htonl(lba >> 32);
429f4dfa67fSRonnie Sahlberg         *(uint32_t *)&acb->task->cdb[6]  = htonl(lba & 0xffffffff);
430f4dfa67fSRonnie Sahlberg         *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
431f4dfa67fSRonnie Sahlberg         break;
432f4dfa67fSRonnie Sahlberg     default:
433f4dfa67fSRonnie Sahlberg         acb->task->cdb_size = 10;
434f4dfa67fSRonnie Sahlberg         acb->task->cdb[0]  = 0x28;
435f4dfa67fSRonnie Sahlberg         *(uint32_t *)&acb->task->cdb[2] = htonl(lba);
436f4dfa67fSRonnie Sahlberg         *(uint16_t *)&acb->task->cdb[7] = htons(num_sectors);
437f4dfa67fSRonnie Sahlberg         break;
438f4dfa67fSRonnie Sahlberg     }
439f4dfa67fSRonnie Sahlberg 
4401dde716eSPeter Lieven     ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
441f4dfa67fSRonnie Sahlberg                                    iscsi_aio_read16_cb,
442f4dfa67fSRonnie Sahlberg                                    NULL,
4437371d56fSPeter Lieven                                    acb);
4447371d56fSPeter Lieven     if (ret != 0) {
445f0d2a4d4SPaolo Bonzini         scsi_free_scsi_task(acb->task);
4461dde716eSPeter Lieven         return -1;
447c589b249SRonnie Sahlberg     }
448c589b249SRonnie Sahlberg 
4497371d56fSPeter Lieven #if defined(LIBISCSI_FEATURE_IOVECTOR)
4507371d56fSPeter Lieven     scsi_task_set_iov_in(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov);
4517371d56fSPeter Lieven #else
452c589b249SRonnie Sahlberg     for (i = 0; i < acb->qiov->niov; i++) {
453c589b249SRonnie Sahlberg         scsi_task_add_data_in_buffer(acb->task,
454c589b249SRonnie Sahlberg                 acb->qiov->iov[i].iov_len,
455c589b249SRonnie Sahlberg                 acb->qiov->iov[i].iov_base);
456c589b249SRonnie Sahlberg     }
4577371d56fSPeter Lieven #endif
4581dde716eSPeter Lieven     return 0;
4591dde716eSPeter Lieven }
4601dde716eSPeter Lieven 
4611dde716eSPeter Lieven static BlockDriverAIOCB *
4621dde716eSPeter Lieven iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
4631dde716eSPeter Lieven                 QEMUIOVector *qiov, int nb_sectors,
4641dde716eSPeter Lieven                 BlockDriverCompletionFunc *cb,
4651dde716eSPeter Lieven                 void *opaque)
4661dde716eSPeter Lieven {
4671dde716eSPeter Lieven     IscsiLun *iscsilun = bs->opaque;
4681dde716eSPeter Lieven     IscsiAIOCB *acb;
4691dde716eSPeter Lieven 
47091bea4e2SPeter Lieven     if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
47191bea4e2SPeter Lieven         return NULL;
47291bea4e2SPeter Lieven     }
47391bea4e2SPeter Lieven 
4741dde716eSPeter Lieven     acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
4751dde716eSPeter Lieven     trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
4761dde716eSPeter Lieven 
4771dde716eSPeter Lieven     acb->nb_sectors  = nb_sectors;
4781dde716eSPeter Lieven     acb->sector_num  = sector_num;
4791dde716eSPeter Lieven     acb->iscsilun    = iscsilun;
4801dde716eSPeter Lieven     acb->qiov        = qiov;
4811dde716eSPeter Lieven     acb->retries     = ISCSI_CMD_RETRIES;
4821dde716eSPeter Lieven 
4831dde716eSPeter Lieven     if (iscsi_aio_readv_acb(acb) != 0) {
4841dde716eSPeter Lieven         qemu_aio_release(acb);
4851dde716eSPeter Lieven         return NULL;
4861dde716eSPeter Lieven     }
487c589b249SRonnie Sahlberg 
488c589b249SRonnie Sahlberg     iscsi_set_events(iscsilun);
489c589b249SRonnie Sahlberg     return &acb->common;
490c589b249SRonnie Sahlberg }
491c589b249SRonnie Sahlberg 
4921dde716eSPeter Lieven static int
4931dde716eSPeter Lieven iscsi_aio_flush_acb(IscsiAIOCB *acb);
494c589b249SRonnie Sahlberg 
495c589b249SRonnie Sahlberg static void
496c589b249SRonnie Sahlberg iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
497c589b249SRonnie Sahlberg                      void *command_data, void *opaque)
498c589b249SRonnie Sahlberg {
499c589b249SRonnie Sahlberg     IscsiAIOCB *acb = opaque;
500c589b249SRonnie Sahlberg 
501b2090919SPaolo Bonzini     if (acb->canceled != 0) {
502c589b249SRonnie Sahlberg         return;
503c589b249SRonnie Sahlberg     }
504c589b249SRonnie Sahlberg 
505c589b249SRonnie Sahlberg     acb->status = 0;
5061dde716eSPeter Lieven     if (status != 0) {
5071dde716eSPeter Lieven         if (status == SCSI_STATUS_CHECK_CONDITION
5081dde716eSPeter Lieven             && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
5091dde716eSPeter Lieven             && acb->retries-- > 0) {
5101dde716eSPeter Lieven             scsi_free_scsi_task(acb->task);
5111dde716eSPeter Lieven             acb->task = NULL;
5121dde716eSPeter Lieven             if (iscsi_aio_flush_acb(acb) == 0) {
5131dde716eSPeter Lieven                 iscsi_set_events(acb->iscsilun);
5141dde716eSPeter Lieven                 return;
5151dde716eSPeter Lieven             }
5161dde716eSPeter Lieven         }
517c589b249SRonnie Sahlberg         error_report("Failed to sync10 data on iSCSI lun. %s",
518c589b249SRonnie Sahlberg                      iscsi_get_error(iscsi));
519c589b249SRonnie Sahlberg         acb->status = -EIO;
520c589b249SRonnie Sahlberg     }
521c589b249SRonnie Sahlberg 
522cfb3f506SPaolo Bonzini     iscsi_schedule_bh(acb);
523c589b249SRonnie Sahlberg }
524c589b249SRonnie Sahlberg 
5251dde716eSPeter Lieven static int
5261dde716eSPeter Lieven iscsi_aio_flush_acb(IscsiAIOCB *acb)
527c589b249SRonnie Sahlberg {
5281dde716eSPeter Lieven     struct iscsi_context *iscsi = acb->iscsilun->iscsi;
529c589b249SRonnie Sahlberg 
530c589b249SRonnie Sahlberg     acb->canceled   = 0;
5311bd075f2SPaolo Bonzini     acb->bh         = NULL;
5321bd075f2SPaolo Bonzini     acb->status     = -EINPROGRESS;
5334790b03dSPaolo Bonzini     acb->buf        = NULL;
534c589b249SRonnie Sahlberg 
5351dde716eSPeter Lieven     acb->task = iscsi_synchronizecache10_task(iscsi, acb->iscsilun->lun,
536c589b249SRonnie Sahlberg                                          0, 0, 0, 0,
537c589b249SRonnie Sahlberg                                          iscsi_synccache10_cb,
538c589b249SRonnie Sahlberg                                          acb);
539c589b249SRonnie Sahlberg     if (acb->task == NULL) {
540c589b249SRonnie Sahlberg         error_report("iSCSI: Failed to send synchronizecache10 command. %s",
541c589b249SRonnie Sahlberg                      iscsi_get_error(iscsi));
5421dde716eSPeter Lieven         return -1;
5431dde716eSPeter Lieven     }
5441dde716eSPeter Lieven 
5451dde716eSPeter Lieven     return 0;
5461dde716eSPeter Lieven }
5471dde716eSPeter Lieven 
5481dde716eSPeter Lieven static BlockDriverAIOCB *
5491dde716eSPeter Lieven iscsi_aio_flush(BlockDriverState *bs,
5501dde716eSPeter Lieven                 BlockDriverCompletionFunc *cb, void *opaque)
5511dde716eSPeter Lieven {
5521dde716eSPeter Lieven     IscsiLun *iscsilun = bs->opaque;
5531dde716eSPeter Lieven 
5541dde716eSPeter Lieven     IscsiAIOCB *acb;
5551dde716eSPeter Lieven 
5561dde716eSPeter Lieven     acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
5571dde716eSPeter Lieven 
5581dde716eSPeter Lieven     acb->iscsilun    = iscsilun;
5591dde716eSPeter Lieven     acb->retries     = ISCSI_CMD_RETRIES;
5601dde716eSPeter Lieven 
5611dde716eSPeter Lieven     if (iscsi_aio_flush_acb(acb) != 0) {
562c589b249SRonnie Sahlberg         qemu_aio_release(acb);
563c589b249SRonnie Sahlberg         return NULL;
564c589b249SRonnie Sahlberg     }
565c589b249SRonnie Sahlberg 
566c589b249SRonnie Sahlberg     iscsi_set_events(iscsilun);
567c589b249SRonnie Sahlberg 
568c589b249SRonnie Sahlberg     return &acb->common;
569c589b249SRonnie Sahlberg }
570c589b249SRonnie Sahlberg 
5711dde716eSPeter Lieven static int iscsi_aio_discard_acb(IscsiAIOCB *acb);
5721dde716eSPeter Lieven 
573fa6acb0cSRonnie Sahlberg static void
574fa6acb0cSRonnie Sahlberg iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
575fa6acb0cSRonnie Sahlberg                      void *command_data, void *opaque)
576fa6acb0cSRonnie Sahlberg {
577fa6acb0cSRonnie Sahlberg     IscsiAIOCB *acb = opaque;
578fa6acb0cSRonnie Sahlberg 
579b2090919SPaolo Bonzini     if (acb->canceled != 0) {
580fa6acb0cSRonnie Sahlberg         return;
581fa6acb0cSRonnie Sahlberg     }
582fa6acb0cSRonnie Sahlberg 
583fa6acb0cSRonnie Sahlberg     acb->status = 0;
5841dde716eSPeter Lieven     if (status != 0) {
5851dde716eSPeter Lieven         if (status == SCSI_STATUS_CHECK_CONDITION
5861dde716eSPeter Lieven             && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
5871dde716eSPeter Lieven             && acb->retries-- > 0) {
5881dde716eSPeter Lieven             scsi_free_scsi_task(acb->task);
5891dde716eSPeter Lieven             acb->task = NULL;
5901dde716eSPeter Lieven             if (iscsi_aio_discard_acb(acb) == 0) {
5911dde716eSPeter Lieven                 iscsi_set_events(acb->iscsilun);
5921dde716eSPeter Lieven                 return;
5931dde716eSPeter Lieven             }
5941dde716eSPeter Lieven         }
595fa6acb0cSRonnie Sahlberg         error_report("Failed to unmap data on iSCSI lun. %s",
596fa6acb0cSRonnie Sahlberg                      iscsi_get_error(iscsi));
597fa6acb0cSRonnie Sahlberg         acb->status = -EIO;
598fa6acb0cSRonnie Sahlberg     }
599fa6acb0cSRonnie Sahlberg 
600cfb3f506SPaolo Bonzini     iscsi_schedule_bh(acb);
601fa6acb0cSRonnie Sahlberg }
602fa6acb0cSRonnie Sahlberg 
6031dde716eSPeter Lieven static int iscsi_aio_discard_acb(IscsiAIOCB *acb) {
6041dde716eSPeter Lieven     struct iscsi_context *iscsi = acb->iscsilun->iscsi;
605fa6acb0cSRonnie Sahlberg     struct unmap_list list[1];
606fa6acb0cSRonnie Sahlberg 
607fa6acb0cSRonnie Sahlberg     acb->canceled   = 0;
6081bd075f2SPaolo Bonzini     acb->bh         = NULL;
6091bd075f2SPaolo Bonzini     acb->status     = -EINPROGRESS;
6104790b03dSPaolo Bonzini     acb->buf        = NULL;
611fa6acb0cSRonnie Sahlberg 
6121dde716eSPeter Lieven     list[0].lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
6131dde716eSPeter Lieven     list[0].num = acb->nb_sectors * BDRV_SECTOR_SIZE / acb->iscsilun->block_size;
614fa6acb0cSRonnie Sahlberg 
6151dde716eSPeter Lieven     acb->task = iscsi_unmap_task(iscsi, acb->iscsilun->lun,
616fa6acb0cSRonnie Sahlberg                                  0, 0, &list[0], 1,
617fa6acb0cSRonnie Sahlberg                                  iscsi_unmap_cb,
618fa6acb0cSRonnie Sahlberg                                  acb);
619fa6acb0cSRonnie Sahlberg     if (acb->task == NULL) {
620fa6acb0cSRonnie Sahlberg         error_report("iSCSI: Failed to send unmap command. %s",
621fa6acb0cSRonnie Sahlberg                      iscsi_get_error(iscsi));
6221dde716eSPeter Lieven         return -1;
6231dde716eSPeter Lieven     }
6241dde716eSPeter Lieven 
6251dde716eSPeter Lieven     return 0;
6261dde716eSPeter Lieven }
6271dde716eSPeter Lieven 
6281dde716eSPeter Lieven static BlockDriverAIOCB *
6291dde716eSPeter Lieven iscsi_aio_discard(BlockDriverState *bs,
6301dde716eSPeter Lieven                   int64_t sector_num, int nb_sectors,
6311dde716eSPeter Lieven                   BlockDriverCompletionFunc *cb, void *opaque)
6321dde716eSPeter Lieven {
6331dde716eSPeter Lieven     IscsiLun *iscsilun = bs->opaque;
6341dde716eSPeter Lieven     IscsiAIOCB *acb;
6351dde716eSPeter Lieven 
6361dde716eSPeter Lieven     acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
6371dde716eSPeter Lieven 
6381dde716eSPeter Lieven     acb->iscsilun    = iscsilun;
6391dde716eSPeter Lieven     acb->nb_sectors  = nb_sectors;
6401dde716eSPeter Lieven     acb->sector_num  = sector_num;
6411dde716eSPeter Lieven     acb->retries     = ISCSI_CMD_RETRIES;
6421dde716eSPeter Lieven 
6431dde716eSPeter Lieven     if (iscsi_aio_discard_acb(acb) != 0) {
644fa6acb0cSRonnie Sahlberg         qemu_aio_release(acb);
645fa6acb0cSRonnie Sahlberg         return NULL;
646fa6acb0cSRonnie Sahlberg     }
647fa6acb0cSRonnie Sahlberg 
648fa6acb0cSRonnie Sahlberg     iscsi_set_events(iscsilun);
649fa6acb0cSRonnie Sahlberg 
650fa6acb0cSRonnie Sahlberg     return &acb->common;
651fa6acb0cSRonnie Sahlberg }
652fa6acb0cSRonnie Sahlberg 
65398392453SRonnie Sahlberg #ifdef __linux__
65498392453SRonnie Sahlberg static void
65598392453SRonnie Sahlberg iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
65698392453SRonnie Sahlberg                      void *command_data, void *opaque)
65798392453SRonnie Sahlberg {
65898392453SRonnie Sahlberg     IscsiAIOCB *acb = opaque;
65998392453SRonnie Sahlberg 
6600a53f010SRonnie Sahlberg     g_free(acb->buf);
6610a53f010SRonnie Sahlberg     acb->buf = NULL;
6620a53f010SRonnie Sahlberg 
663b2090919SPaolo Bonzini     if (acb->canceled != 0) {
66498392453SRonnie Sahlberg         return;
66598392453SRonnie Sahlberg     }
66698392453SRonnie Sahlberg 
66798392453SRonnie Sahlberg     acb->status = 0;
66898392453SRonnie Sahlberg     if (status < 0) {
66998392453SRonnie Sahlberg         error_report("Failed to ioctl(SG_IO) to iSCSI lun. %s",
67098392453SRonnie Sahlberg                      iscsi_get_error(iscsi));
67198392453SRonnie Sahlberg         acb->status = -EIO;
67298392453SRonnie Sahlberg     }
67398392453SRonnie Sahlberg 
67498392453SRonnie Sahlberg     acb->ioh->driver_status = 0;
67598392453SRonnie Sahlberg     acb->ioh->host_status   = 0;
67698392453SRonnie Sahlberg     acb->ioh->resid         = 0;
67798392453SRonnie Sahlberg 
67898392453SRonnie Sahlberg #define SG_ERR_DRIVER_SENSE    0x08
67998392453SRonnie Sahlberg 
68098392453SRonnie Sahlberg     if (status == SCSI_STATUS_CHECK_CONDITION && acb->task->datain.size >= 2) {
68198392453SRonnie Sahlberg         int ss;
68298392453SRonnie Sahlberg 
68398392453SRonnie Sahlberg         acb->ioh->driver_status |= SG_ERR_DRIVER_SENSE;
68498392453SRonnie Sahlberg 
68598392453SRonnie Sahlberg         acb->ioh->sb_len_wr = acb->task->datain.size - 2;
68698392453SRonnie Sahlberg         ss = (acb->ioh->mx_sb_len >= acb->ioh->sb_len_wr) ?
68798392453SRonnie Sahlberg              acb->ioh->mx_sb_len : acb->ioh->sb_len_wr;
68898392453SRonnie Sahlberg         memcpy(acb->ioh->sbp, &acb->task->datain.data[2], ss);
68998392453SRonnie Sahlberg     }
69098392453SRonnie Sahlberg 
691cfb3f506SPaolo Bonzini     iscsi_schedule_bh(acb);
69298392453SRonnie Sahlberg }
69398392453SRonnie Sahlberg 
69498392453SRonnie Sahlberg static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
69598392453SRonnie Sahlberg         unsigned long int req, void *buf,
69698392453SRonnie Sahlberg         BlockDriverCompletionFunc *cb, void *opaque)
69798392453SRonnie Sahlberg {
69898392453SRonnie Sahlberg     IscsiLun *iscsilun = bs->opaque;
69998392453SRonnie Sahlberg     struct iscsi_context *iscsi = iscsilun->iscsi;
70098392453SRonnie Sahlberg     struct iscsi_data data;
70198392453SRonnie Sahlberg     IscsiAIOCB *acb;
70298392453SRonnie Sahlberg 
70398392453SRonnie Sahlberg     assert(req == SG_IO);
70498392453SRonnie Sahlberg 
705d7331bedSStefan Hajnoczi     acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
70698392453SRonnie Sahlberg 
70798392453SRonnie Sahlberg     acb->iscsilun = iscsilun;
70898392453SRonnie Sahlberg     acb->canceled    = 0;
7091bd075f2SPaolo Bonzini     acb->bh          = NULL;
7101bd075f2SPaolo Bonzini     acb->status      = -EINPROGRESS;
71198392453SRonnie Sahlberg     acb->buf         = NULL;
71298392453SRonnie Sahlberg     acb->ioh         = buf;
71398392453SRonnie Sahlberg 
71498392453SRonnie Sahlberg     acb->task = malloc(sizeof(struct scsi_task));
71598392453SRonnie Sahlberg     if (acb->task == NULL) {
71698392453SRonnie Sahlberg         error_report("iSCSI: Failed to allocate task for scsi command. %s",
71798392453SRonnie Sahlberg                      iscsi_get_error(iscsi));
71898392453SRonnie Sahlberg         qemu_aio_release(acb);
71998392453SRonnie Sahlberg         return NULL;
72098392453SRonnie Sahlberg     }
72198392453SRonnie Sahlberg     memset(acb->task, 0, sizeof(struct scsi_task));
72298392453SRonnie Sahlberg 
72398392453SRonnie Sahlberg     switch (acb->ioh->dxfer_direction) {
72498392453SRonnie Sahlberg     case SG_DXFER_TO_DEV:
72598392453SRonnie Sahlberg         acb->task->xfer_dir = SCSI_XFER_WRITE;
72698392453SRonnie Sahlberg         break;
72798392453SRonnie Sahlberg     case SG_DXFER_FROM_DEV:
72898392453SRonnie Sahlberg         acb->task->xfer_dir = SCSI_XFER_READ;
72998392453SRonnie Sahlberg         break;
73098392453SRonnie Sahlberg     default:
73198392453SRonnie Sahlberg         acb->task->xfer_dir = SCSI_XFER_NONE;
73298392453SRonnie Sahlberg         break;
73398392453SRonnie Sahlberg     }
73498392453SRonnie Sahlberg 
73598392453SRonnie Sahlberg     acb->task->cdb_size = acb->ioh->cmd_len;
73698392453SRonnie Sahlberg     memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len);
73798392453SRonnie Sahlberg     acb->task->expxferlen = acb->ioh->dxfer_len;
73898392453SRonnie Sahlberg 
7390a53f010SRonnie Sahlberg     data.size = 0;
74098392453SRonnie Sahlberg     if (acb->task->xfer_dir == SCSI_XFER_WRITE) {
7410a53f010SRonnie Sahlberg         if (acb->ioh->iovec_count == 0) {
74298392453SRonnie Sahlberg             data.data = acb->ioh->dxferp;
74398392453SRonnie Sahlberg             data.size = acb->ioh->dxfer_len;
7440a53f010SRonnie Sahlberg         } else {
7450a53f010SRonnie Sahlberg #if defined(LIBISCSI_FEATURE_IOVECTOR)
7460a53f010SRonnie Sahlberg             scsi_task_set_iov_out(acb->task,
7470a53f010SRonnie Sahlberg                                  (struct scsi_iovec *) acb->ioh->dxferp,
7480a53f010SRonnie Sahlberg                                  acb->ioh->iovec_count);
7490a53f010SRonnie Sahlberg #else
7500a53f010SRonnie Sahlberg             struct iovec *iov = (struct iovec *)acb->ioh->dxferp;
7510a53f010SRonnie Sahlberg 
7520a53f010SRonnie Sahlberg             acb->buf = g_malloc(acb->ioh->dxfer_len);
7530a53f010SRonnie Sahlberg             data.data = acb->buf;
7540a53f010SRonnie Sahlberg             data.size = iov_to_buf(iov, acb->ioh->iovec_count, 0,
7550a53f010SRonnie Sahlberg                                    acb->buf, acb->ioh->dxfer_len);
7560a53f010SRonnie Sahlberg #endif
75798392453SRonnie Sahlberg         }
7580a53f010SRonnie Sahlberg     }
7590a53f010SRonnie Sahlberg 
76098392453SRonnie Sahlberg     if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
76198392453SRonnie Sahlberg                                  iscsi_aio_ioctl_cb,
7620a53f010SRonnie Sahlberg                                  (data.size > 0) ? &data : NULL,
76398392453SRonnie Sahlberg                                  acb) != 0) {
76498392453SRonnie Sahlberg         scsi_free_scsi_task(acb->task);
76598392453SRonnie Sahlberg         qemu_aio_release(acb);
76698392453SRonnie Sahlberg         return NULL;
76798392453SRonnie Sahlberg     }
76898392453SRonnie Sahlberg 
76998392453SRonnie Sahlberg     /* tell libiscsi to read straight into the buffer we got from ioctl */
77098392453SRonnie Sahlberg     if (acb->task->xfer_dir == SCSI_XFER_READ) {
7710a53f010SRonnie Sahlberg         if (acb->ioh->iovec_count == 0) {
77298392453SRonnie Sahlberg             scsi_task_add_data_in_buffer(acb->task,
77398392453SRonnie Sahlberg                                          acb->ioh->dxfer_len,
77498392453SRonnie Sahlberg                                          acb->ioh->dxferp);
7750a53f010SRonnie Sahlberg         } else {
7760a53f010SRonnie Sahlberg #if defined(LIBISCSI_FEATURE_IOVECTOR)
7770a53f010SRonnie Sahlberg             scsi_task_set_iov_in(acb->task,
7780a53f010SRonnie Sahlberg                                  (struct scsi_iovec *) acb->ioh->dxferp,
7790a53f010SRonnie Sahlberg                                  acb->ioh->iovec_count);
7800a53f010SRonnie Sahlberg #else
7810a53f010SRonnie Sahlberg             int i;
7820a53f010SRonnie Sahlberg             for (i = 0; i < acb->ioh->iovec_count; i++) {
7830a53f010SRonnie Sahlberg                 struct iovec *iov = (struct iovec *)acb->ioh->dxferp;
7840a53f010SRonnie Sahlberg 
7850a53f010SRonnie Sahlberg                 scsi_task_add_data_in_buffer(acb->task,
7860a53f010SRonnie Sahlberg                     iov[i].iov_len,
7870a53f010SRonnie Sahlberg                     iov[i].iov_base);
7880a53f010SRonnie Sahlberg             }
7890a53f010SRonnie Sahlberg #endif
7900a53f010SRonnie Sahlberg         }
79198392453SRonnie Sahlberg     }
79298392453SRonnie Sahlberg 
79398392453SRonnie Sahlberg     iscsi_set_events(iscsilun);
79498392453SRonnie Sahlberg 
79598392453SRonnie Sahlberg     return &acb->common;
79698392453SRonnie Sahlberg }
79798392453SRonnie Sahlberg 
798f1a12821SRonnie Sahlberg 
799f1a12821SRonnie Sahlberg static void ioctl_cb(void *opaque, int status)
800f1a12821SRonnie Sahlberg {
801f1a12821SRonnie Sahlberg     int *p_status = opaque;
802f1a12821SRonnie Sahlberg     *p_status = status;
803f1a12821SRonnie Sahlberg }
804f1a12821SRonnie Sahlberg 
80598392453SRonnie Sahlberg static int iscsi_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
80698392453SRonnie Sahlberg {
80798392453SRonnie Sahlberg     IscsiLun *iscsilun = bs->opaque;
808f1a12821SRonnie Sahlberg     int status;
80998392453SRonnie Sahlberg 
81098392453SRonnie Sahlberg     switch (req) {
81198392453SRonnie Sahlberg     case SG_GET_VERSION_NUM:
81298392453SRonnie Sahlberg         *(int *)buf = 30000;
81398392453SRonnie Sahlberg         break;
81498392453SRonnie Sahlberg     case SG_GET_SCSI_ID:
81598392453SRonnie Sahlberg         ((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type;
81698392453SRonnie Sahlberg         break;
817f1a12821SRonnie Sahlberg     case SG_IO:
818f1a12821SRonnie Sahlberg         status = -EINPROGRESS;
819f1a12821SRonnie Sahlberg         iscsi_aio_ioctl(bs, req, buf, ioctl_cb, &status);
820f1a12821SRonnie Sahlberg 
821f1a12821SRonnie Sahlberg         while (status == -EINPROGRESS) {
822f1a12821SRonnie Sahlberg             qemu_aio_wait();
823f1a12821SRonnie Sahlberg         }
824f1a12821SRonnie Sahlberg 
825f1a12821SRonnie Sahlberg         return 0;
82698392453SRonnie Sahlberg     default:
82798392453SRonnie Sahlberg         return -1;
82898392453SRonnie Sahlberg     }
82998392453SRonnie Sahlberg     return 0;
83098392453SRonnie Sahlberg }
83198392453SRonnie Sahlberg #endif
83298392453SRonnie Sahlberg 
833c589b249SRonnie Sahlberg static int64_t
834c589b249SRonnie Sahlberg iscsi_getlength(BlockDriverState *bs)
835c589b249SRonnie Sahlberg {
836c589b249SRonnie Sahlberg     IscsiLun *iscsilun = bs->opaque;
837c589b249SRonnie Sahlberg     int64_t len;
838c589b249SRonnie Sahlberg 
839c589b249SRonnie Sahlberg     len  = iscsilun->num_blocks;
840c589b249SRonnie Sahlberg     len *= iscsilun->block_size;
841c589b249SRonnie Sahlberg 
842c589b249SRonnie Sahlberg     return len;
843c589b249SRonnie Sahlberg }
844c589b249SRonnie Sahlberg 
845f9dadc98SRonnie Sahlberg static int parse_chap(struct iscsi_context *iscsi, const char *target)
846f9dadc98SRonnie Sahlberg {
847f9dadc98SRonnie Sahlberg     QemuOptsList *list;
848f9dadc98SRonnie Sahlberg     QemuOpts *opts;
849f9dadc98SRonnie Sahlberg     const char *user = NULL;
850f9dadc98SRonnie Sahlberg     const char *password = NULL;
851f9dadc98SRonnie Sahlberg 
852f9dadc98SRonnie Sahlberg     list = qemu_find_opts("iscsi");
853f9dadc98SRonnie Sahlberg     if (!list) {
854f9dadc98SRonnie Sahlberg         return 0;
855f9dadc98SRonnie Sahlberg     }
856f9dadc98SRonnie Sahlberg 
857f9dadc98SRonnie Sahlberg     opts = qemu_opts_find(list, target);
858f9dadc98SRonnie Sahlberg     if (opts == NULL) {
859f9dadc98SRonnie Sahlberg         opts = QTAILQ_FIRST(&list->head);
860f9dadc98SRonnie Sahlberg         if (!opts) {
861f9dadc98SRonnie Sahlberg             return 0;
862f9dadc98SRonnie Sahlberg         }
863f9dadc98SRonnie Sahlberg     }
864f9dadc98SRonnie Sahlberg 
865f9dadc98SRonnie Sahlberg     user = qemu_opt_get(opts, "user");
866f9dadc98SRonnie Sahlberg     if (!user) {
867f9dadc98SRonnie Sahlberg         return 0;
868f9dadc98SRonnie Sahlberg     }
869f9dadc98SRonnie Sahlberg 
870f9dadc98SRonnie Sahlberg     password = qemu_opt_get(opts, "password");
871f9dadc98SRonnie Sahlberg     if (!password) {
872f9dadc98SRonnie Sahlberg         error_report("CHAP username specified but no password was given");
873f9dadc98SRonnie Sahlberg         return -1;
874f9dadc98SRonnie Sahlberg     }
875f9dadc98SRonnie Sahlberg 
876f9dadc98SRonnie Sahlberg     if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
877f9dadc98SRonnie Sahlberg         error_report("Failed to set initiator username and password");
878f9dadc98SRonnie Sahlberg         return -1;
879f9dadc98SRonnie Sahlberg     }
880f9dadc98SRonnie Sahlberg 
881f9dadc98SRonnie Sahlberg     return 0;
882f9dadc98SRonnie Sahlberg }
883f9dadc98SRonnie Sahlberg 
884f9dadc98SRonnie Sahlberg static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
885f9dadc98SRonnie Sahlberg {
886f9dadc98SRonnie Sahlberg     QemuOptsList *list;
887f9dadc98SRonnie Sahlberg     QemuOpts *opts;
888f9dadc98SRonnie Sahlberg     const char *digest = NULL;
889f9dadc98SRonnie Sahlberg 
890f9dadc98SRonnie Sahlberg     list = qemu_find_opts("iscsi");
891f9dadc98SRonnie Sahlberg     if (!list) {
892f9dadc98SRonnie Sahlberg         return;
893f9dadc98SRonnie Sahlberg     }
894f9dadc98SRonnie Sahlberg 
895f9dadc98SRonnie Sahlberg     opts = qemu_opts_find(list, target);
896f9dadc98SRonnie Sahlberg     if (opts == NULL) {
897f9dadc98SRonnie Sahlberg         opts = QTAILQ_FIRST(&list->head);
898f9dadc98SRonnie Sahlberg         if (!opts) {
899f9dadc98SRonnie Sahlberg             return;
900f9dadc98SRonnie Sahlberg         }
901f9dadc98SRonnie Sahlberg     }
902f9dadc98SRonnie Sahlberg 
903f9dadc98SRonnie Sahlberg     digest = qemu_opt_get(opts, "header-digest");
904f9dadc98SRonnie Sahlberg     if (!digest) {
905f9dadc98SRonnie Sahlberg         return;
906f9dadc98SRonnie Sahlberg     }
907f9dadc98SRonnie Sahlberg 
908f9dadc98SRonnie Sahlberg     if (!strcmp(digest, "CRC32C")) {
909f9dadc98SRonnie Sahlberg         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C);
910f9dadc98SRonnie Sahlberg     } else if (!strcmp(digest, "NONE")) {
911f9dadc98SRonnie Sahlberg         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE);
912f9dadc98SRonnie Sahlberg     } else if (!strcmp(digest, "CRC32C-NONE")) {
913f9dadc98SRonnie Sahlberg         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE);
914f9dadc98SRonnie Sahlberg     } else if (!strcmp(digest, "NONE-CRC32C")) {
915f9dadc98SRonnie Sahlberg         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
916f9dadc98SRonnie Sahlberg     } else {
917f9dadc98SRonnie Sahlberg         error_report("Invalid header-digest setting : %s", digest);
918f9dadc98SRonnie Sahlberg     }
919f9dadc98SRonnie Sahlberg }
920f9dadc98SRonnie Sahlberg 
921f9dadc98SRonnie Sahlberg static char *parse_initiator_name(const char *target)
922f9dadc98SRonnie Sahlberg {
923f9dadc98SRonnie Sahlberg     QemuOptsList *list;
924f9dadc98SRonnie Sahlberg     QemuOpts *opts;
925f9dadc98SRonnie Sahlberg     const char *name = NULL;
92631459f46SRonnie Sahlberg     const char *iscsi_name = qemu_get_vm_name();
927f9dadc98SRonnie Sahlberg 
928f9dadc98SRonnie Sahlberg     list = qemu_find_opts("iscsi");
929f2ef4a6dSPaolo Bonzini     if (list) {
930f9dadc98SRonnie Sahlberg         opts = qemu_opts_find(list, target);
931f9dadc98SRonnie Sahlberg         if (!opts) {
932f2ef4a6dSPaolo Bonzini             opts = QTAILQ_FIRST(&list->head);
933f9dadc98SRonnie Sahlberg         }
934f2ef4a6dSPaolo Bonzini         if (opts) {
935f9dadc98SRonnie Sahlberg             name = qemu_opt_get(opts, "initiator-name");
936f2ef4a6dSPaolo Bonzini         }
937f9dadc98SRonnie Sahlberg     }
938f9dadc98SRonnie Sahlberg 
939f2ef4a6dSPaolo Bonzini     if (name) {
940f9dadc98SRonnie Sahlberg         return g_strdup(name);
941f2ef4a6dSPaolo Bonzini     } else {
94231459f46SRonnie Sahlberg         return g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s",
94331459f46SRonnie Sahlberg                                iscsi_name ? ":" : "",
94431459f46SRonnie Sahlberg                                iscsi_name ? iscsi_name : "");
945f2ef4a6dSPaolo Bonzini     }
946f9dadc98SRonnie Sahlberg }
947f9dadc98SRonnie Sahlberg 
9485b5d34ecSPeter Lieven #if defined(LIBISCSI_FEATURE_NOP_COUNTER)
9495b5d34ecSPeter Lieven static void iscsi_nop_timed_event(void *opaque)
9505b5d34ecSPeter Lieven {
9515b5d34ecSPeter Lieven     IscsiLun *iscsilun = opaque;
9525b5d34ecSPeter Lieven 
9535b5d34ecSPeter Lieven     if (iscsi_get_nops_in_flight(iscsilun->iscsi) > MAX_NOP_FAILURES) {
9545b5d34ecSPeter Lieven         error_report("iSCSI: NOP timeout. Reconnecting...");
9555b5d34ecSPeter Lieven         iscsi_reconnect(iscsilun->iscsi);
9565b5d34ecSPeter Lieven     }
9575b5d34ecSPeter Lieven 
9585b5d34ecSPeter Lieven     if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) {
9595b5d34ecSPeter Lieven         error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages.");
9605b5d34ecSPeter Lieven         return;
9615b5d34ecSPeter Lieven     }
9625b5d34ecSPeter Lieven 
963bc72ad67SAlex Bligh     timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
9645b5d34ecSPeter Lieven     iscsi_set_events(iscsilun);
9655b5d34ecSPeter Lieven }
9665b5d34ecSPeter Lieven #endif
9675b5d34ecSPeter Lieven 
968cb1b83e7SPeter Lieven static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
969cb1b83e7SPeter Lieven {
970cb1b83e7SPeter Lieven     struct scsi_task *task = NULL;
971cb1b83e7SPeter Lieven     struct scsi_readcapacity10 *rc10 = NULL;
972cb1b83e7SPeter Lieven     struct scsi_readcapacity16 *rc16 = NULL;
973cb1b83e7SPeter Lieven     int ret = 0;
974cb1b83e7SPeter Lieven     int retries = ISCSI_CMD_RETRIES;
975cb1b83e7SPeter Lieven 
9761288844eSPaolo Bonzini     do {
9771288844eSPaolo Bonzini         if (task != NULL) {
9781288844eSPaolo Bonzini             scsi_free_scsi_task(task);
9791288844eSPaolo Bonzini             task = NULL;
9801288844eSPaolo Bonzini         }
9811288844eSPaolo Bonzini 
982cb1b83e7SPeter Lieven         switch (iscsilun->type) {
983cb1b83e7SPeter Lieven         case TYPE_DISK:
984cb1b83e7SPeter Lieven             task = iscsi_readcapacity16_sync(iscsilun->iscsi, iscsilun->lun);
9851288844eSPaolo Bonzini             if (task != NULL && task->status == SCSI_STATUS_GOOD) {
986cb1b83e7SPeter Lieven                 rc16 = scsi_datain_unmarshall(task);
987cb1b83e7SPeter Lieven                 if (rc16 == NULL) {
988cb1b83e7SPeter Lieven                     error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
989cb1b83e7SPeter Lieven                     ret = -EINVAL;
9901288844eSPaolo Bonzini                 } else {
991cb1b83e7SPeter Lieven                     iscsilun->block_size = rc16->block_length;
992cb1b83e7SPeter Lieven                     iscsilun->num_blocks = rc16->returned_lba + 1;
9931288844eSPaolo Bonzini                 }
9941288844eSPaolo Bonzini             }
995cb1b83e7SPeter Lieven             break;
996cb1b83e7SPeter Lieven         case TYPE_ROM:
997cb1b83e7SPeter Lieven             task = iscsi_readcapacity10_sync(iscsilun->iscsi, iscsilun->lun, 0, 0);
9981288844eSPaolo Bonzini             if (task != NULL && task->status == SCSI_STATUS_GOOD) {
999cb1b83e7SPeter Lieven                 rc10 = scsi_datain_unmarshall(task);
1000cb1b83e7SPeter Lieven                 if (rc10 == NULL) {
1001cb1b83e7SPeter Lieven                     error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
1002cb1b83e7SPeter Lieven                     ret = -EINVAL;
10031288844eSPaolo Bonzini                 } else {
1004cb1b83e7SPeter Lieven                     iscsilun->block_size = rc10->block_size;
1005cb1b83e7SPeter Lieven                     if (rc10->lba == 0) {
1006cb1b83e7SPeter Lieven                         /* blank disk loaded */
1007cb1b83e7SPeter Lieven                         iscsilun->num_blocks = 0;
1008cb1b83e7SPeter Lieven                     } else {
1009cb1b83e7SPeter Lieven                         iscsilun->num_blocks = rc10->lba + 1;
1010cb1b83e7SPeter Lieven                     }
10111288844eSPaolo Bonzini                 }
10121288844eSPaolo Bonzini             }
1013cb1b83e7SPeter Lieven             break;
1014cb1b83e7SPeter Lieven         default:
10151288844eSPaolo Bonzini             return 0;
1016cb1b83e7SPeter Lieven         }
10171288844eSPaolo Bonzini     } while (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
10181288844eSPaolo Bonzini              && task->sense.key == SCSI_SENSE_UNIT_ATTENTION
10191288844eSPaolo Bonzini              && retries-- > 0);
1020cb1b83e7SPeter Lieven 
10211288844eSPaolo Bonzini     if (task == NULL || task->status != SCSI_STATUS_GOOD) {
10221288844eSPaolo Bonzini         error_report("iSCSI: failed to send readcapacity10 command.");
10231288844eSPaolo Bonzini         ret = -EINVAL;
10241288844eSPaolo Bonzini     }
1025cb1b83e7SPeter Lieven     if (task) {
1026cb1b83e7SPeter Lieven         scsi_free_scsi_task(task);
1027cb1b83e7SPeter Lieven     }
1028cb1b83e7SPeter Lieven     return ret;
1029cb1b83e7SPeter Lieven }
1030cb1b83e7SPeter Lieven 
103160beb341SKevin Wolf /* TODO Convert to fine grained options */
103260beb341SKevin Wolf static QemuOptsList runtime_opts = {
103360beb341SKevin Wolf     .name = "iscsi",
103460beb341SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
103560beb341SKevin Wolf     .desc = {
103660beb341SKevin Wolf         {
103760beb341SKevin Wolf             .name = "filename",
103860beb341SKevin Wolf             .type = QEMU_OPT_STRING,
103960beb341SKevin Wolf             .help = "URL to the iscsi image",
104060beb341SKevin Wolf         },
104160beb341SKevin Wolf         { /* end of list */ }
104260beb341SKevin Wolf     },
104360beb341SKevin Wolf };
104460beb341SKevin Wolf 
1045c589b249SRonnie Sahlberg /*
1046c589b249SRonnie Sahlberg  * We support iscsi url's on the form
1047c589b249SRonnie Sahlberg  * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
1048c589b249SRonnie Sahlberg  */
104956d1b4d2SKevin Wolf static int iscsi_open(BlockDriverState *bs, QDict *options, int flags)
1050c589b249SRonnie Sahlberg {
1051c589b249SRonnie Sahlberg     IscsiLun *iscsilun = bs->opaque;
1052c589b249SRonnie Sahlberg     struct iscsi_context *iscsi = NULL;
1053c589b249SRonnie Sahlberg     struct iscsi_url *iscsi_url = NULL;
1054e829b0bbSPeter Lieven     struct scsi_task *task = NULL;
1055e829b0bbSPeter Lieven     struct scsi_inquiry_standard *inq = NULL;
1056f9dadc98SRonnie Sahlberg     char *initiator_name = NULL;
105760beb341SKevin Wolf     QemuOpts *opts;
105860beb341SKevin Wolf     Error *local_err = NULL;
105960beb341SKevin Wolf     const char *filename;
1060c589b249SRonnie Sahlberg     int ret;
1061c589b249SRonnie Sahlberg 
1062c589b249SRonnie Sahlberg     if ((BDRV_SECTOR_SIZE % 512) != 0) {
1063c589b249SRonnie Sahlberg         error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. "
1064c589b249SRonnie Sahlberg                      "BDRV_SECTOR_SIZE(%lld) is not a multiple "
1065c589b249SRonnie Sahlberg                      "of 512", BDRV_SECTOR_SIZE);
1066c589b249SRonnie Sahlberg         return -EINVAL;
1067c589b249SRonnie Sahlberg     }
1068c589b249SRonnie Sahlberg 
106960beb341SKevin Wolf     opts = qemu_opts_create_nofail(&runtime_opts);
107060beb341SKevin Wolf     qemu_opts_absorb_qdict(opts, options, &local_err);
107160beb341SKevin Wolf     if (error_is_set(&local_err)) {
107260beb341SKevin Wolf         qerror_report_err(local_err);
107360beb341SKevin Wolf         error_free(local_err);
107460beb341SKevin Wolf         ret = -EINVAL;
107560beb341SKevin Wolf         goto out;
107660beb341SKevin Wolf     }
107760beb341SKevin Wolf 
107860beb341SKevin Wolf     filename = qemu_opt_get(opts, "filename");
107960beb341SKevin Wolf 
108060beb341SKevin Wolf 
1081c589b249SRonnie Sahlberg     iscsi_url = iscsi_parse_full_url(iscsi, filename);
1082c589b249SRonnie Sahlberg     if (iscsi_url == NULL) {
10838da1e18bSPeter Lieven         error_report("Failed to parse URL : %s", filename);
1084c589b249SRonnie Sahlberg         ret = -EINVAL;
1085b93c94f7SPaolo Bonzini         goto out;
1086c589b249SRonnie Sahlberg     }
1087c589b249SRonnie Sahlberg 
1088f9dadc98SRonnie Sahlberg     memset(iscsilun, 0, sizeof(IscsiLun));
1089f9dadc98SRonnie Sahlberg 
1090f9dadc98SRonnie Sahlberg     initiator_name = parse_initiator_name(iscsi_url->target);
1091f9dadc98SRonnie Sahlberg 
1092f9dadc98SRonnie Sahlberg     iscsi = iscsi_create_context(initiator_name);
1093f9dadc98SRonnie Sahlberg     if (iscsi == NULL) {
1094f9dadc98SRonnie Sahlberg         error_report("iSCSI: Failed to create iSCSI context.");
1095f9dadc98SRonnie Sahlberg         ret = -ENOMEM;
1096b93c94f7SPaolo Bonzini         goto out;
1097f9dadc98SRonnie Sahlberg     }
1098f9dadc98SRonnie Sahlberg 
1099c589b249SRonnie Sahlberg     if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
1100c589b249SRonnie Sahlberg         error_report("iSCSI: Failed to set target name.");
1101c589b249SRonnie Sahlberg         ret = -EINVAL;
1102b93c94f7SPaolo Bonzini         goto out;
1103c589b249SRonnie Sahlberg     }
1104c589b249SRonnie Sahlberg 
1105c589b249SRonnie Sahlberg     if (iscsi_url->user != NULL) {
1106c589b249SRonnie Sahlberg         ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
1107c589b249SRonnie Sahlberg                                               iscsi_url->passwd);
1108c589b249SRonnie Sahlberg         if (ret != 0) {
1109c589b249SRonnie Sahlberg             error_report("Failed to set initiator username and password");
1110c589b249SRonnie Sahlberg             ret = -EINVAL;
1111b93c94f7SPaolo Bonzini             goto out;
1112c589b249SRonnie Sahlberg         }
1113c589b249SRonnie Sahlberg     }
1114f9dadc98SRonnie Sahlberg 
1115f9dadc98SRonnie Sahlberg     /* check if we got CHAP username/password via the options */
1116f9dadc98SRonnie Sahlberg     if (parse_chap(iscsi, iscsi_url->target) != 0) {
1117f9dadc98SRonnie Sahlberg         error_report("iSCSI: Failed to set CHAP user/password");
1118f9dadc98SRonnie Sahlberg         ret = -EINVAL;
1119b93c94f7SPaolo Bonzini         goto out;
1120f9dadc98SRonnie Sahlberg     }
1121f9dadc98SRonnie Sahlberg 
1122c589b249SRonnie Sahlberg     if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
1123c589b249SRonnie Sahlberg         error_report("iSCSI: Failed to set session type to normal.");
1124c589b249SRonnie Sahlberg         ret = -EINVAL;
1125b93c94f7SPaolo Bonzini         goto out;
1126c589b249SRonnie Sahlberg     }
1127c589b249SRonnie Sahlberg 
1128c589b249SRonnie Sahlberg     iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
1129c589b249SRonnie Sahlberg 
1130f9dadc98SRonnie Sahlberg     /* check if we got HEADER_DIGEST via the options */
1131f9dadc98SRonnie Sahlberg     parse_header_digest(iscsi, iscsi_url->target);
1132f9dadc98SRonnie Sahlberg 
1133e829b0bbSPeter Lieven     if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
1134c589b249SRonnie Sahlberg         error_report("iSCSI: Failed to connect to LUN : %s",
1135c589b249SRonnie Sahlberg             iscsi_get_error(iscsi));
1136c589b249SRonnie Sahlberg         ret = -EINVAL;
1137b93c94f7SPaolo Bonzini         goto out;
1138c589b249SRonnie Sahlberg     }
1139622695a4SRonnie Sahlberg 
1140e829b0bbSPeter Lieven     iscsilun->iscsi = iscsi;
1141e829b0bbSPeter Lieven     iscsilun->lun   = iscsi_url->lun;
1142e829b0bbSPeter Lieven 
1143e829b0bbSPeter Lieven     task = iscsi_inquiry_sync(iscsi, iscsilun->lun, 0, 0, 36);
1144e829b0bbSPeter Lieven 
1145e829b0bbSPeter Lieven     if (task == NULL || task->status != SCSI_STATUS_GOOD) {
1146e829b0bbSPeter Lieven         error_report("iSCSI: failed to send inquiry command.");
1147e829b0bbSPeter Lieven         ret = -EINVAL;
1148e829b0bbSPeter Lieven         goto out;
1149e829b0bbSPeter Lieven     }
1150e829b0bbSPeter Lieven 
1151e829b0bbSPeter Lieven     inq = scsi_datain_unmarshall(task);
1152e829b0bbSPeter Lieven     if (inq == NULL) {
1153e829b0bbSPeter Lieven         error_report("iSCSI: Failed to unmarshall inquiry data.");
1154e829b0bbSPeter Lieven         ret = -EINVAL;
1155e829b0bbSPeter Lieven         goto out;
1156e829b0bbSPeter Lieven     }
1157e829b0bbSPeter Lieven 
1158e829b0bbSPeter Lieven     iscsilun->type = inq->periperal_device_type;
1159e829b0bbSPeter Lieven 
1160cb1b83e7SPeter Lieven     if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
1161e829b0bbSPeter Lieven         goto out;
1162e829b0bbSPeter Lieven     }
11630777b5ddSPeter Lieven     bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
1164e829b0bbSPeter Lieven 
1165622695a4SRonnie Sahlberg     /* Medium changer or tape. We dont have any emulation for this so this must
1166622695a4SRonnie Sahlberg      * be sg ioctl compatible. We force it to be sg, otherwise qemu will try
1167622695a4SRonnie Sahlberg      * to read from the device to guess the image format.
1168622695a4SRonnie Sahlberg      */
1169622695a4SRonnie Sahlberg     if (iscsilun->type == TYPE_MEDIUM_CHANGER ||
1170622695a4SRonnie Sahlberg         iscsilun->type == TYPE_TAPE) {
1171622695a4SRonnie Sahlberg         bs->sg = 1;
1172622695a4SRonnie Sahlberg     }
1173622695a4SRonnie Sahlberg 
11745b5d34ecSPeter Lieven #if defined(LIBISCSI_FEATURE_NOP_COUNTER)
11755b5d34ecSPeter Lieven     /* Set up a timer for sending out iSCSI NOPs */
1176bc72ad67SAlex Bligh     iscsilun->nop_timer = timer_new_ms(QEMU_CLOCK_REALTIME, iscsi_nop_timed_event, iscsilun);
1177bc72ad67SAlex Bligh     timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
11785b5d34ecSPeter Lieven #endif
11795b5d34ecSPeter Lieven 
1180b93c94f7SPaolo Bonzini out:
118160beb341SKevin Wolf     qemu_opts_del(opts);
1182f9dadc98SRonnie Sahlberg     if (initiator_name != NULL) {
1183f9dadc98SRonnie Sahlberg         g_free(initiator_name);
1184f9dadc98SRonnie Sahlberg     }
1185c589b249SRonnie Sahlberg     if (iscsi_url != NULL) {
1186c589b249SRonnie Sahlberg         iscsi_destroy_url(iscsi_url);
1187c589b249SRonnie Sahlberg     }
1188e829b0bbSPeter Lieven     if (task != NULL) {
1189e829b0bbSPeter Lieven         scsi_free_scsi_task(task);
1190e829b0bbSPeter Lieven     }
1191b93c94f7SPaolo Bonzini 
1192b93c94f7SPaolo Bonzini     if (ret) {
1193c589b249SRonnie Sahlberg         if (iscsi != NULL) {
1194c589b249SRonnie Sahlberg             iscsi_destroy_context(iscsi);
1195c589b249SRonnie Sahlberg         }
1196c589b249SRonnie Sahlberg         memset(iscsilun, 0, sizeof(IscsiLun));
1197b93c94f7SPaolo Bonzini     }
1198c589b249SRonnie Sahlberg     return ret;
1199c589b249SRonnie Sahlberg }
1200c589b249SRonnie Sahlberg 
1201c589b249SRonnie Sahlberg static void iscsi_close(BlockDriverState *bs)
1202c589b249SRonnie Sahlberg {
1203c589b249SRonnie Sahlberg     IscsiLun *iscsilun = bs->opaque;
1204c589b249SRonnie Sahlberg     struct iscsi_context *iscsi = iscsilun->iscsi;
1205c589b249SRonnie Sahlberg 
12065b5d34ecSPeter Lieven     if (iscsilun->nop_timer) {
1207bc72ad67SAlex Bligh         timer_del(iscsilun->nop_timer);
1208bc72ad67SAlex Bligh         timer_free(iscsilun->nop_timer);
12095b5d34ecSPeter Lieven     }
1210f2e5dca4SStefan Hajnoczi     qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL);
1211c589b249SRonnie Sahlberg     iscsi_destroy_context(iscsi);
1212c589b249SRonnie Sahlberg     memset(iscsilun, 0, sizeof(IscsiLun));
1213c589b249SRonnie Sahlberg }
1214c589b249SRonnie Sahlberg 
1215cb1b83e7SPeter Lieven static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
1216cb1b83e7SPeter Lieven {
1217cb1b83e7SPeter Lieven     IscsiLun *iscsilun = bs->opaque;
1218cb1b83e7SPeter Lieven     int ret = 0;
1219cb1b83e7SPeter Lieven 
1220cb1b83e7SPeter Lieven     if (iscsilun->type != TYPE_DISK) {
1221cb1b83e7SPeter Lieven         return -ENOTSUP;
1222cb1b83e7SPeter Lieven     }
1223cb1b83e7SPeter Lieven 
1224cb1b83e7SPeter Lieven     if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
1225cb1b83e7SPeter Lieven         return ret;
1226cb1b83e7SPeter Lieven     }
1227cb1b83e7SPeter Lieven 
1228cb1b83e7SPeter Lieven     if (offset > iscsi_getlength(bs)) {
1229cb1b83e7SPeter Lieven         return -EINVAL;
1230cb1b83e7SPeter Lieven     }
1231cb1b83e7SPeter Lieven 
1232cb1b83e7SPeter Lieven     return 0;
1233cb1b83e7SPeter Lieven }
1234cb1b83e7SPeter Lieven 
1235f807ecd5SPeter Lieven static int iscsi_has_zero_init(BlockDriverState *bs)
1236f807ecd5SPeter Lieven {
1237f807ecd5SPeter Lieven     return 0;
1238f807ecd5SPeter Lieven }
1239f807ecd5SPeter Lieven 
1240de8864e5SPeter Lieven static int iscsi_create(const char *filename, QEMUOptionParameter *options)
1241de8864e5SPeter Lieven {
1242de8864e5SPeter Lieven     int ret = 0;
1243de8864e5SPeter Lieven     int64_t total_size = 0;
124413c91cb7SFam Zheng     BlockDriverState *bs;
1245de8864e5SPeter Lieven     IscsiLun *iscsilun = NULL;
124660beb341SKevin Wolf     QDict *bs_options;
1247de8864e5SPeter Lieven 
124813c91cb7SFam Zheng     bs = bdrv_new("");
1249de8864e5SPeter Lieven 
1250de8864e5SPeter Lieven     /* Read out options */
1251de8864e5SPeter Lieven     while (options && options->name) {
1252de8864e5SPeter Lieven         if (!strcmp(options->name, "size")) {
1253de8864e5SPeter Lieven             total_size = options->value.n / BDRV_SECTOR_SIZE;
1254de8864e5SPeter Lieven         }
1255de8864e5SPeter Lieven         options++;
1256de8864e5SPeter Lieven     }
1257de8864e5SPeter Lieven 
125813c91cb7SFam Zheng     bs->opaque = g_malloc0(sizeof(struct IscsiLun));
125913c91cb7SFam Zheng     iscsilun = bs->opaque;
1260de8864e5SPeter Lieven 
126160beb341SKevin Wolf     bs_options = qdict_new();
126260beb341SKevin Wolf     qdict_put(bs_options, "filename", qstring_from_str(filename));
126313c91cb7SFam Zheng     ret = iscsi_open(bs, bs_options, 0);
126460beb341SKevin Wolf     QDECREF(bs_options);
126560beb341SKevin Wolf 
1266de8864e5SPeter Lieven     if (ret != 0) {
1267de8864e5SPeter Lieven         goto out;
1268de8864e5SPeter Lieven     }
12695b5d34ecSPeter Lieven     if (iscsilun->nop_timer) {
1270bc72ad67SAlex Bligh         timer_del(iscsilun->nop_timer);
1271bc72ad67SAlex Bligh         timer_free(iscsilun->nop_timer);
12725b5d34ecSPeter Lieven     }
1273de8864e5SPeter Lieven     if (iscsilun->type != TYPE_DISK) {
1274de8864e5SPeter Lieven         ret = -ENODEV;
1275de8864e5SPeter Lieven         goto out;
1276de8864e5SPeter Lieven     }
127713c91cb7SFam Zheng     if (bs->total_sectors < total_size) {
1278de8864e5SPeter Lieven         ret = -ENOSPC;
1279d3bda7bcSPeter Lieven         goto out;
1280de8864e5SPeter Lieven     }
1281de8864e5SPeter Lieven 
1282de8864e5SPeter Lieven     ret = 0;
1283de8864e5SPeter Lieven out:
1284de8864e5SPeter Lieven     if (iscsilun->iscsi != NULL) {
1285de8864e5SPeter Lieven         iscsi_destroy_context(iscsilun->iscsi);
1286de8864e5SPeter Lieven     }
128713c91cb7SFam Zheng     g_free(bs->opaque);
128813c91cb7SFam Zheng     bs->opaque = NULL;
1289*4f6fd349SFam Zheng     bdrv_unref(bs);
1290de8864e5SPeter Lieven     return ret;
1291de8864e5SPeter Lieven }
1292de8864e5SPeter Lieven 
1293de8864e5SPeter Lieven static QEMUOptionParameter iscsi_create_options[] = {
1294de8864e5SPeter Lieven     {
1295de8864e5SPeter Lieven         .name = BLOCK_OPT_SIZE,
1296de8864e5SPeter Lieven         .type = OPT_SIZE,
1297de8864e5SPeter Lieven         .help = "Virtual disk size"
1298de8864e5SPeter Lieven     },
1299de8864e5SPeter Lieven     { NULL }
1300de8864e5SPeter Lieven };
1301de8864e5SPeter Lieven 
1302c589b249SRonnie Sahlberg static BlockDriver bdrv_iscsi = {
1303c589b249SRonnie Sahlberg     .format_name     = "iscsi",
1304c589b249SRonnie Sahlberg     .protocol_name   = "iscsi",
1305c589b249SRonnie Sahlberg 
1306c589b249SRonnie Sahlberg     .instance_size   = sizeof(IscsiLun),
1307c589b249SRonnie Sahlberg     .bdrv_file_open  = iscsi_open,
1308c589b249SRonnie Sahlberg     .bdrv_close      = iscsi_close,
1309de8864e5SPeter Lieven     .bdrv_create     = iscsi_create,
1310de8864e5SPeter Lieven     .create_options  = iscsi_create_options,
1311c589b249SRonnie Sahlberg 
1312c589b249SRonnie Sahlberg     .bdrv_getlength  = iscsi_getlength,
1313cb1b83e7SPeter Lieven     .bdrv_truncate   = iscsi_truncate,
1314c589b249SRonnie Sahlberg 
1315c589b249SRonnie Sahlberg     .bdrv_aio_readv  = iscsi_aio_readv,
1316c589b249SRonnie Sahlberg     .bdrv_aio_writev = iscsi_aio_writev,
1317c589b249SRonnie Sahlberg     .bdrv_aio_flush  = iscsi_aio_flush,
1318fa6acb0cSRonnie Sahlberg 
1319fa6acb0cSRonnie Sahlberg     .bdrv_aio_discard = iscsi_aio_discard,
1320f807ecd5SPeter Lieven     .bdrv_has_zero_init = iscsi_has_zero_init,
132198392453SRonnie Sahlberg 
132298392453SRonnie Sahlberg #ifdef __linux__
132398392453SRonnie Sahlberg     .bdrv_ioctl       = iscsi_ioctl,
132498392453SRonnie Sahlberg     .bdrv_aio_ioctl   = iscsi_aio_ioctl,
132598392453SRonnie Sahlberg #endif
1326c589b249SRonnie Sahlberg };
1327c589b249SRonnie Sahlberg 
13284d454574SPaolo Bonzini static QemuOptsList qemu_iscsi_opts = {
13294d454574SPaolo Bonzini     .name = "iscsi",
13304d454574SPaolo Bonzini     .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
13314d454574SPaolo Bonzini     .desc = {
13324d454574SPaolo Bonzini         {
13334d454574SPaolo Bonzini             .name = "user",
13344d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
13354d454574SPaolo Bonzini             .help = "username for CHAP authentication to target",
13364d454574SPaolo Bonzini         },{
13374d454574SPaolo Bonzini             .name = "password",
13384d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
13394d454574SPaolo Bonzini             .help = "password for CHAP authentication to target",
13404d454574SPaolo Bonzini         },{
13414d454574SPaolo Bonzini             .name = "header-digest",
13424d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
13434d454574SPaolo Bonzini             .help = "HeaderDigest setting. "
13444d454574SPaolo Bonzini                     "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
13454d454574SPaolo Bonzini         },{
13464d454574SPaolo Bonzini             .name = "initiator-name",
13474d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
13484d454574SPaolo Bonzini             .help = "Initiator iqn name to use when connecting",
13494d454574SPaolo Bonzini         },
13504d454574SPaolo Bonzini         { /* end of list */ }
13514d454574SPaolo Bonzini     },
13524d454574SPaolo Bonzini };
13534d454574SPaolo Bonzini 
1354c589b249SRonnie Sahlberg static void iscsi_block_init(void)
1355c589b249SRonnie Sahlberg {
1356c589b249SRonnie Sahlberg     bdrv_register(&bdrv_iscsi);
13574d454574SPaolo Bonzini     qemu_add_opts(&qemu_iscsi_opts);
1358c589b249SRonnie Sahlberg }
1359c589b249SRonnie Sahlberg 
1360c589b249SRonnie Sahlberg block_init(iscsi_block_init);
1361