 bafbd6a1c6
			
		
	
	
		bafbd6a1c6
		
	
	
	
	
		
			
			Both unused after the previous patch. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
		
			
				
	
	
		
			711 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			711 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU Block driver for iSCSI images
 | |
|  *
 | |
|  * Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to deal
 | |
|  * in the Software without restriction, including without limitation the rights
 | |
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
|  * copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | |
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
|  * THE SOFTWARE.
 | |
|  */
 | |
| 
 | |
| #include "config-host.h"
 | |
| 
 | |
| #include <poll.h>
 | |
| #include "qemu-common.h"
 | |
| #include "qemu-error.h"
 | |
| #include "block_int.h"
 | |
| #include "trace.h"
 | |
| 
 | |
| #include <iscsi/iscsi.h>
 | |
| #include <iscsi/scsi-lowlevel.h>
 | |
| 
 | |
| 
 | |
| typedef struct IscsiLun {
 | |
|     struct iscsi_context *iscsi;
 | |
|     int lun;
 | |
|     int block_size;
 | |
|     unsigned long num_blocks;
 | |
| } IscsiLun;
 | |
| 
 | |
| typedef struct IscsiAIOCB {
 | |
|     BlockDriverAIOCB common;
 | |
|     QEMUIOVector *qiov;
 | |
|     QEMUBH *bh;
 | |
|     IscsiLun *iscsilun;
 | |
|     struct scsi_task *task;
 | |
|     uint8_t *buf;
 | |
|     int status;
 | |
|     int canceled;
 | |
|     size_t read_size;
 | |
|     size_t read_offset;
 | |
| } IscsiAIOCB;
 | |
| 
 | |
| struct IscsiTask {
 | |
|     IscsiLun *iscsilun;
 | |
|     BlockDriverState *bs;
 | |
|     int status;
 | |
|     int complete;
 | |
| };
 | |
| 
 | |
| static void
 | |
| iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
 | |
|                     void *private_data)
 | |
| {
 | |
| }
 | |
| 
 | |
| static void
 | |
| iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
 | |
| {
 | |
|     IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
 | |
|     IscsiLun *iscsilun = acb->iscsilun;
 | |
| 
 | |
|     acb->common.cb(acb->common.opaque, -ECANCELED);
 | |
|     acb->canceled = 1;
 | |
| 
 | |
|     /* send a task mgmt call to the target to cancel the task on the target */
 | |
|     iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
 | |
|                                      iscsi_abort_task_cb, NULL);
 | |
| 
 | |
|     /* then also cancel the task locally in libiscsi */
 | |
|     iscsi_scsi_task_cancel(iscsilun->iscsi, acb->task);
 | |
| }
 | |
| 
 | |
| static AIOPool iscsi_aio_pool = {
 | |
|     .aiocb_size         = sizeof(IscsiAIOCB),
 | |
|     .cancel             = iscsi_aio_cancel,
 | |
| };
 | |
| 
 | |
| 
 | |
| static void iscsi_process_read(void *arg);
 | |
| static void iscsi_process_write(void *arg);
 | |
| 
 | |
| static int iscsi_process_flush(void *arg)
 | |
| {
 | |
|     IscsiLun *iscsilun = arg;
 | |
| 
 | |
|     return iscsi_queue_length(iscsilun->iscsi) > 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| iscsi_set_events(IscsiLun *iscsilun)
 | |
| {
 | |
|     struct iscsi_context *iscsi = iscsilun->iscsi;
 | |
| 
 | |
|     qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read,
 | |
|                            (iscsi_which_events(iscsi) & POLLOUT)
 | |
|                            ? iscsi_process_write : NULL,
 | |
|                            iscsi_process_flush, iscsilun);
 | |
| }
 | |
| 
 | |
| static void
 | |
| iscsi_process_read(void *arg)
 | |
| {
 | |
|     IscsiLun *iscsilun = arg;
 | |
|     struct iscsi_context *iscsi = iscsilun->iscsi;
 | |
| 
 | |
|     iscsi_service(iscsi, POLLIN);
 | |
|     iscsi_set_events(iscsilun);
 | |
| }
 | |
| 
 | |
| static void
 | |
| iscsi_process_write(void *arg)
 | |
| {
 | |
|     IscsiLun *iscsilun = arg;
 | |
|     struct iscsi_context *iscsi = iscsilun->iscsi;
 | |
| 
 | |
|     iscsi_service(iscsi, POLLOUT);
 | |
|     iscsi_set_events(iscsilun);
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| iscsi_schedule_bh(QEMUBHFunc *cb, IscsiAIOCB *acb)
 | |
| {
 | |
|     acb->bh = qemu_bh_new(cb, acb);
 | |
|     if (!acb->bh) {
 | |
|         error_report("oom: could not create iscsi bh");
 | |
|         return -EIO;
 | |
|     }
 | |
| 
 | |
|     qemu_bh_schedule(acb->bh);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| iscsi_readv_writev_bh_cb(void *p)
 | |
| {
 | |
|     IscsiAIOCB *acb = p;
 | |
| 
 | |
|     qemu_bh_delete(acb->bh);
 | |
| 
 | |
|     if (acb->canceled == 0) {
 | |
|         acb->common.cb(acb->common.opaque, acb->status);
 | |
|     }
 | |
| 
 | |
|     qemu_aio_release(acb);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status,
 | |
|                      void *command_data, void *opaque)
 | |
| {
 | |
|     IscsiAIOCB *acb = opaque;
 | |
| 
 | |
|     trace_iscsi_aio_write10_cb(iscsi, status, acb, acb->canceled);
 | |
| 
 | |
|     g_free(acb->buf);
 | |
| 
 | |
|     if (acb->canceled != 0) {
 | |
|         qemu_aio_release(acb);
 | |
|         scsi_free_scsi_task(acb->task);
 | |
|         acb->task = NULL;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     acb->status = 0;
 | |
|     if (status < 0) {
 | |
|         error_report("Failed to write10 data to iSCSI lun. %s",
 | |
|                      iscsi_get_error(iscsi));
 | |
|         acb->status = -EIO;
 | |
|     }
 | |
| 
 | |
|     iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
 | |
|     scsi_free_scsi_task(acb->task);
 | |
|     acb->task = NULL;
 | |
| }
 | |
| 
 | |
| static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
 | |
| {
 | |
|     return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
 | |
| }
 | |
| 
 | |
| static BlockDriverAIOCB *
 | |
| iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
 | |
|                  QEMUIOVector *qiov, int nb_sectors,
 | |
|                  BlockDriverCompletionFunc *cb,
 | |
|                  void *opaque)
 | |
| {
 | |
|     IscsiLun *iscsilun = bs->opaque;
 | |
|     struct iscsi_context *iscsi = iscsilun->iscsi;
 | |
|     IscsiAIOCB *acb;
 | |
|     size_t size;
 | |
|     int fua = 0;
 | |
| 
 | |
|     /* set FUA on writes when cache mode is write through */
 | |
|     if (!(bs->open_flags & BDRV_O_CACHE_WB)) {
 | |
|         fua = 1;
 | |
|     }
 | |
| 
 | |
|     acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
 | |
|     trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb);
 | |
| 
 | |
|     acb->iscsilun = iscsilun;
 | |
|     acb->qiov     = qiov;
 | |
| 
 | |
|     acb->canceled   = 0;
 | |
| 
 | |
|     /* XXX we should pass the iovec to write10 to avoid the extra copy */
 | |
|     /* this will allow us to get rid of 'buf' completely */
 | |
|     size = nb_sectors * BDRV_SECTOR_SIZE;
 | |
|     acb->buf = g_malloc(size);
 | |
|     qemu_iovec_to_buffer(acb->qiov, acb->buf);
 | |
|     acb->task = iscsi_write10_task(iscsi, iscsilun->lun, acb->buf, size,
 | |
|                               sector_qemu2lun(sector_num, iscsilun),
 | |
|                               fua, 0, iscsilun->block_size,
 | |
|                               iscsi_aio_write10_cb, acb);
 | |
|     if (acb->task == NULL) {
 | |
|         error_report("iSCSI: Failed to send write10 command. %s",
 | |
|                      iscsi_get_error(iscsi));
 | |
|         g_free(acb->buf);
 | |
|         qemu_aio_release(acb);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     iscsi_set_events(iscsilun);
 | |
| 
 | |
|     return &acb->common;
 | |
| }
 | |
| 
 | |
| static void
 | |
| iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status,
 | |
|                     void *command_data, void *opaque)
 | |
| {
 | |
|     IscsiAIOCB *acb = opaque;
 | |
| 
 | |
|     trace_iscsi_aio_read10_cb(iscsi, status, acb, acb->canceled);
 | |
| 
 | |
|     if (acb->canceled != 0) {
 | |
|         qemu_aio_release(acb);
 | |
|         scsi_free_scsi_task(acb->task);
 | |
|         acb->task = NULL;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     acb->status = 0;
 | |
|     if (status != 0) {
 | |
|         error_report("Failed to read10 data from iSCSI lun. %s",
 | |
|                      iscsi_get_error(iscsi));
 | |
|         acb->status = -EIO;
 | |
|     }
 | |
| 
 | |
|     iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
 | |
|     scsi_free_scsi_task(acb->task);
 | |
|     acb->task = NULL;
 | |
| }
 | |
| 
 | |
| static BlockDriverAIOCB *
 | |
| iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
 | |
|                 QEMUIOVector *qiov, int nb_sectors,
 | |
|                 BlockDriverCompletionFunc *cb,
 | |
|                 void *opaque)
 | |
| {
 | |
|     IscsiLun *iscsilun = bs->opaque;
 | |
|     struct iscsi_context *iscsi = iscsilun->iscsi;
 | |
|     IscsiAIOCB *acb;
 | |
|     size_t qemu_read_size, lun_read_size;
 | |
|     int i;
 | |
| 
 | |
|     qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
 | |
| 
 | |
|     acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
 | |
|     trace_iscsi_aio_readv(iscsi, sector_num, nb_sectors, opaque, acb);
 | |
| 
 | |
|     acb->iscsilun = iscsilun;
 | |
|     acb->qiov     = qiov;
 | |
| 
 | |
|     acb->canceled    = 0;
 | |
|     acb->read_size   = qemu_read_size;
 | |
|     acb->buf         = NULL;
 | |
| 
 | |
|     /* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU
 | |
|      * may be misaligned to the LUN, so we may need to read some extra
 | |
|      * data.
 | |
|      */
 | |
|     acb->read_offset = 0;
 | |
|     if (iscsilun->block_size > BDRV_SECTOR_SIZE) {
 | |
|         uint64_t bdrv_offset = BDRV_SECTOR_SIZE * sector_num;
 | |
| 
 | |
|         acb->read_offset  = bdrv_offset % iscsilun->block_size;
 | |
|     }
 | |
| 
 | |
|     lun_read_size  = (qemu_read_size + iscsilun->block_size
 | |
|                      + acb->read_offset - 1)
 | |
|                      / iscsilun->block_size * iscsilun->block_size;
 | |
|     acb->task = iscsi_read10_task(iscsi, iscsilun->lun,
 | |
|                              sector_qemu2lun(sector_num, iscsilun),
 | |
|                              lun_read_size, iscsilun->block_size,
 | |
|                              iscsi_aio_read10_cb, acb);
 | |
|     if (acb->task == NULL) {
 | |
|         error_report("iSCSI: Failed to send read10 command. %s",
 | |
|                      iscsi_get_error(iscsi));
 | |
|         qemu_aio_release(acb);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < acb->qiov->niov; i++) {
 | |
|         scsi_task_add_data_in_buffer(acb->task,
 | |
|                 acb->qiov->iov[i].iov_len,
 | |
|                 acb->qiov->iov[i].iov_base);
 | |
|     }
 | |
| 
 | |
|     iscsi_set_events(iscsilun);
 | |
| 
 | |
|     return &acb->common;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
 | |
|                      void *command_data, void *opaque)
 | |
| {
 | |
|     IscsiAIOCB *acb = opaque;
 | |
| 
 | |
|     if (acb->canceled != 0) {
 | |
|         qemu_aio_release(acb);
 | |
|         scsi_free_scsi_task(acb->task);
 | |
|         acb->task = NULL;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     acb->status = 0;
 | |
|     if (status < 0) {
 | |
|         error_report("Failed to sync10 data on iSCSI lun. %s",
 | |
|                      iscsi_get_error(iscsi));
 | |
|         acb->status = -EIO;
 | |
|     }
 | |
| 
 | |
|     iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
 | |
|     scsi_free_scsi_task(acb->task);
 | |
|     acb->task = NULL;
 | |
| }
 | |
| 
 | |
| static BlockDriverAIOCB *
 | |
| iscsi_aio_flush(BlockDriverState *bs,
 | |
|                 BlockDriverCompletionFunc *cb, void *opaque)
 | |
| {
 | |
|     IscsiLun *iscsilun = bs->opaque;
 | |
|     struct iscsi_context *iscsi = iscsilun->iscsi;
 | |
|     IscsiAIOCB *acb;
 | |
| 
 | |
|     acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
 | |
| 
 | |
|     acb->iscsilun = iscsilun;
 | |
|     acb->canceled   = 0;
 | |
| 
 | |
|     acb->task = iscsi_synchronizecache10_task(iscsi, iscsilun->lun,
 | |
|                                          0, 0, 0, 0,
 | |
|                                          iscsi_synccache10_cb,
 | |
|                                          acb);
 | |
|     if (acb->task == NULL) {
 | |
|         error_report("iSCSI: Failed to send synchronizecache10 command. %s",
 | |
|                      iscsi_get_error(iscsi));
 | |
|         qemu_aio_release(acb);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     iscsi_set_events(iscsilun);
 | |
| 
 | |
|     return &acb->common;
 | |
| }
 | |
| 
 | |
| static int64_t
 | |
| iscsi_getlength(BlockDriverState *bs)
 | |
| {
 | |
|     IscsiLun *iscsilun = bs->opaque;
 | |
|     int64_t len;
 | |
| 
 | |
|     len  = iscsilun->num_blocks;
 | |
|     len *= iscsilun->block_size;
 | |
| 
 | |
|     return len;
 | |
| }
 | |
| 
 | |
| static void
 | |
| iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
 | |
|                         void *command_data, void *opaque)
 | |
| {
 | |
|     struct IscsiTask *itask = opaque;
 | |
|     struct scsi_readcapacity10 *rc10;
 | |
|     struct scsi_task *task = command_data;
 | |
| 
 | |
|     if (status != 0) {
 | |
|         error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
 | |
|                      iscsi_get_error(iscsi));
 | |
|         itask->status   = 1;
 | |
|         itask->complete = 1;
 | |
|         scsi_free_scsi_task(task);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     rc10 = scsi_datain_unmarshall(task);
 | |
|     if (rc10 == NULL) {
 | |
|         error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
 | |
|         itask->status   = 1;
 | |
|         itask->complete = 1;
 | |
|         scsi_free_scsi_task(task);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     itask->iscsilun->block_size = rc10->block_size;
 | |
|     itask->iscsilun->num_blocks = rc10->lba;
 | |
|     itask->bs->total_sectors = (uint64_t)rc10->lba *
 | |
|                                rc10->block_size / BDRV_SECTOR_SIZE ;
 | |
| 
 | |
|     itask->status   = 0;
 | |
|     itask->complete = 1;
 | |
|     scsi_free_scsi_task(task);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
 | |
|                  void *opaque)
 | |
| {
 | |
|     struct IscsiTask *itask = opaque;
 | |
|     struct scsi_task *task;
 | |
| 
 | |
|     if (status != 0) {
 | |
|         itask->status   = 1;
 | |
|         itask->complete = 1;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, 0, 0,
 | |
|                                    iscsi_readcapacity10_cb, opaque);
 | |
|     if (task == NULL) {
 | |
|         error_report("iSCSI: failed to send readcapacity command.");
 | |
|         itask->status   = 1;
 | |
|         itask->complete = 1;
 | |
|         return;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int parse_chap(struct iscsi_context *iscsi, const char *target)
 | |
| {
 | |
|     QemuOptsList *list;
 | |
|     QemuOpts *opts;
 | |
|     const char *user = NULL;
 | |
|     const char *password = NULL;
 | |
| 
 | |
|     list = qemu_find_opts("iscsi");
 | |
|     if (!list) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     opts = qemu_opts_find(list, target);
 | |
|     if (opts == NULL) {
 | |
|         opts = QTAILQ_FIRST(&list->head);
 | |
|         if (!opts) {
 | |
|             return 0;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     user = qemu_opt_get(opts, "user");
 | |
|     if (!user) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     password = qemu_opt_get(opts, "password");
 | |
|     if (!password) {
 | |
|         error_report("CHAP username specified but no password was given");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
 | |
|         error_report("Failed to set initiator username and password");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
 | |
| {
 | |
|     QemuOptsList *list;
 | |
|     QemuOpts *opts;
 | |
|     const char *digest = NULL;
 | |
| 
 | |
|     list = qemu_find_opts("iscsi");
 | |
|     if (!list) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     opts = qemu_opts_find(list, target);
 | |
|     if (opts == NULL) {
 | |
|         opts = QTAILQ_FIRST(&list->head);
 | |
|         if (!opts) {
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     digest = qemu_opt_get(opts, "header-digest");
 | |
|     if (!digest) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!strcmp(digest, "CRC32C")) {
 | |
|         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C);
 | |
|     } else if (!strcmp(digest, "NONE")) {
 | |
|         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE);
 | |
|     } else if (!strcmp(digest, "CRC32C-NONE")) {
 | |
|         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE);
 | |
|     } else if (!strcmp(digest, "NONE-CRC32C")) {
 | |
|         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
 | |
|     } else {
 | |
|         error_report("Invalid header-digest setting : %s", digest);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static char *parse_initiator_name(const char *target)
 | |
| {
 | |
|     QemuOptsList *list;
 | |
|     QemuOpts *opts;
 | |
|     const char *name = NULL;
 | |
| 
 | |
|     list = qemu_find_opts("iscsi");
 | |
|     if (!list) {
 | |
|         return g_strdup("iqn.2008-11.org.linux-kvm");
 | |
|     }
 | |
| 
 | |
|     opts = qemu_opts_find(list, target);
 | |
|     if (opts == NULL) {
 | |
|         opts = QTAILQ_FIRST(&list->head);
 | |
|         if (!opts) {
 | |
|             return g_strdup("iqn.2008-11.org.linux-kvm");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     name = qemu_opt_get(opts, "initiator-name");
 | |
|     if (!name) {
 | |
|         return g_strdup("iqn.2008-11.org.linux-kvm");
 | |
|     }
 | |
| 
 | |
|     return g_strdup(name);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * We support iscsi url's on the form
 | |
|  * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
 | |
|  */
 | |
| static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
 | |
| {
 | |
|     IscsiLun *iscsilun = bs->opaque;
 | |
|     struct iscsi_context *iscsi = NULL;
 | |
|     struct iscsi_url *iscsi_url = NULL;
 | |
|     struct IscsiTask task;
 | |
|     char *initiator_name = NULL;
 | |
|     int ret;
 | |
| 
 | |
|     if ((BDRV_SECTOR_SIZE % 512) != 0) {
 | |
|         error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. "
 | |
|                      "BDRV_SECTOR_SIZE(%lld) is not a multiple "
 | |
|                      "of 512", BDRV_SECTOR_SIZE);
 | |
|         return -EINVAL;
 | |
|     }
 | |
| 
 | |
|     iscsi_url = iscsi_parse_full_url(iscsi, filename);
 | |
|     if (iscsi_url == NULL) {
 | |
|         error_report("Failed to parse URL : %s %s", filename,
 | |
|                      iscsi_get_error(iscsi));
 | |
|         ret = -EINVAL;
 | |
|         goto failed;
 | |
|     }
 | |
| 
 | |
|     memset(iscsilun, 0, sizeof(IscsiLun));
 | |
| 
 | |
|     initiator_name = parse_initiator_name(iscsi_url->target);
 | |
| 
 | |
|     iscsi = iscsi_create_context(initiator_name);
 | |
|     if (iscsi == NULL) {
 | |
|         error_report("iSCSI: Failed to create iSCSI context.");
 | |
|         ret = -ENOMEM;
 | |
|         goto failed;
 | |
|     }
 | |
| 
 | |
|     if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
 | |
|         error_report("iSCSI: Failed to set target name.");
 | |
|         ret = -EINVAL;
 | |
|         goto failed;
 | |
|     }
 | |
| 
 | |
|     if (iscsi_url->user != NULL) {
 | |
|         ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
 | |
|                                               iscsi_url->passwd);
 | |
|         if (ret != 0) {
 | |
|             error_report("Failed to set initiator username and password");
 | |
|             ret = -EINVAL;
 | |
|             goto failed;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* check if we got CHAP username/password via the options */
 | |
|     if (parse_chap(iscsi, iscsi_url->target) != 0) {
 | |
|         error_report("iSCSI: Failed to set CHAP user/password");
 | |
|         ret = -EINVAL;
 | |
|         goto failed;
 | |
|     }
 | |
| 
 | |
|     if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
 | |
|         error_report("iSCSI: Failed to set session type to normal.");
 | |
|         ret = -EINVAL;
 | |
|         goto failed;
 | |
|     }
 | |
| 
 | |
|     iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
 | |
| 
 | |
|     /* check if we got HEADER_DIGEST via the options */
 | |
|     parse_header_digest(iscsi, iscsi_url->target);
 | |
| 
 | |
|     task.iscsilun = iscsilun;
 | |
|     task.status = 0;
 | |
|     task.complete = 0;
 | |
|     task.bs = bs;
 | |
| 
 | |
|     iscsilun->iscsi = iscsi;
 | |
|     iscsilun->lun   = iscsi_url->lun;
 | |
| 
 | |
|     if (iscsi_full_connect_async(iscsi, iscsi_url->portal, iscsi_url->lun,
 | |
|                                  iscsi_connect_cb, &task)
 | |
|         != 0) {
 | |
|         error_report("iSCSI: Failed to start async connect.");
 | |
|         ret = -EINVAL;
 | |
|         goto failed;
 | |
|     }
 | |
| 
 | |
|     while (!task.complete) {
 | |
|         iscsi_set_events(iscsilun);
 | |
|         qemu_aio_wait();
 | |
|     }
 | |
|     if (task.status != 0) {
 | |
|         error_report("iSCSI: Failed to connect to LUN : %s",
 | |
|                      iscsi_get_error(iscsi));
 | |
|         ret = -EINVAL;
 | |
|         goto failed;
 | |
|     }
 | |
| 
 | |
|     if (iscsi_url != NULL) {
 | |
|         iscsi_destroy_url(iscsi_url);
 | |
|     }
 | |
|     return 0;
 | |
| 
 | |
| failed:
 | |
|     if (initiator_name != NULL) {
 | |
|         g_free(initiator_name);
 | |
|     }
 | |
|     if (iscsi_url != NULL) {
 | |
|         iscsi_destroy_url(iscsi_url);
 | |
|     }
 | |
|     if (iscsi != NULL) {
 | |
|         iscsi_destroy_context(iscsi);
 | |
|     }
 | |
|     memset(iscsilun, 0, sizeof(IscsiLun));
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void iscsi_close(BlockDriverState *bs)
 | |
| {
 | |
|     IscsiLun *iscsilun = bs->opaque;
 | |
|     struct iscsi_context *iscsi = iscsilun->iscsi;
 | |
| 
 | |
|     qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL);
 | |
|     iscsi_destroy_context(iscsi);
 | |
|     memset(iscsilun, 0, sizeof(IscsiLun));
 | |
| }
 | |
| 
 | |
| static BlockDriver bdrv_iscsi = {
 | |
|     .format_name     = "iscsi",
 | |
|     .protocol_name   = "iscsi",
 | |
| 
 | |
|     .instance_size   = sizeof(IscsiLun),
 | |
|     .bdrv_file_open  = iscsi_open,
 | |
|     .bdrv_close      = iscsi_close,
 | |
| 
 | |
|     .bdrv_getlength  = iscsi_getlength,
 | |
| 
 | |
|     .bdrv_aio_readv  = iscsi_aio_readv,
 | |
|     .bdrv_aio_writev = iscsi_aio_writev,
 | |
|     .bdrv_aio_flush  = iscsi_aio_flush,
 | |
| };
 | |
| 
 | |
| static void iscsi_block_init(void)
 | |
| {
 | |
|     bdrv_register(&bdrv_iscsi);
 | |
| }
 | |
| 
 | |
| block_init(iscsi_block_init);
 |