static BlockDriverAIOCB * iscsi_aio_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = iscsilun->iscsi; IscsiAIOCB *acb; struct unmap_list list[1]; acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); acb->iscsilun = iscsilun; acb->canceled = 0; acb->bh = NULL; acb->status = -EINPROGRESS; list[0].lba = sector_qemu2lun(sector_num, iscsilun); list[0].num = nb_sectors * BDRV_SECTOR_SIZE / iscsilun->block_size; acb->task = iscsi_unmap_task(iscsi, iscsilun->lun, 0, 0, &list[0], 1, iscsi_unmap_cb, acb); if (acb->task == NULL) { error_report("iSCSI: Failed to send unmap command. %s", iscsi_get_error(iscsi)); qemu_aio_release(acb); return NULL; } iscsi_set_events(iscsilun); return &acb->common; }
static void iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) { IscsiAIOCB *acb = opaque; trace_iscsi_aio_write16_cb(iscsi, status, acb, acb->canceled); g_free(acb->buf); acb->buf = NULL; if (acb->canceled != 0) { return; } acb->status = 0; if (status != 0) { if (status == SCSI_STATUS_CHECK_CONDITION && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION && acb->retries-- > 0) { scsi_free_scsi_task(acb->task); acb->task = NULL; if (iscsi_aio_writev_acb(acb) == 0) { iscsi_set_events(acb->iscsilun); return; } } error_report("Failed to write16 data to iSCSI lun. %s", iscsi_get_error(iscsi)); acb->status = -EIO; } iscsi_schedule_bh(acb); }
static BlockDriverAIOCB * iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { IscsiLun *iscsilun = bs->opaque; IscsiAIOCB *acb; if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { return NULL; } acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb); acb->nb_sectors = nb_sectors; acb->sector_num = sector_num; acb->iscsilun = iscsilun; acb->qiov = qiov; acb->retries = ISCSI_CMD_RETRIES; if (iscsi_aio_readv_acb(acb) != 0) { qemu_aio_release(acb); return NULL; } iscsi_set_events(iscsilun); return &acb->common; }
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_aiocb_info, bs, cb, opaque); acb->iscsilun = iscsilun; acb->canceled = 0; acb->bh = NULL; acb->status = -EINPROGRESS; 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 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, 0, 0, 0, 0, 0, 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 BlockDriverAIOCB * iscsi_aio_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { IscsiLun *iscsilun = bs->opaque; IscsiAIOCB *acb; acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); acb->iscsilun = iscsilun; acb->nb_sectors = nb_sectors; acb->sector_num = sector_num; acb->retries = ISCSI_CMD_RETRIES; if (iscsi_aio_discard_acb(acb) != 0) { if (acb->task) { scsi_free_scsi_task(acb->task); } qemu_aio_release(acb); return NULL; } 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) { return; } acb->status = 0; if (status != 0) { if (status == SCSI_STATUS_CHECK_CONDITION && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION && acb->retries-- > 0) { if (acb->task != NULL) { scsi_free_scsi_task(acb->task); acb->task = NULL; } if (iscsi_aio_flush_acb(acb) == 0) { iscsi_set_events(acb->iscsilun); return; } } error_report("Failed to sync10 data on iSCSI lun. %s", iscsi_get_error(iscsi)); acb->status = -EIO; } iscsi_schedule_bh(acb); }
static BlockDriverAIOCB * iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { IscsiLun *iscsilun = bs->opaque; IscsiAIOCB *acb; acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb); acb->nb_sectors = nb_sectors; acb->sector_num = sector_num; acb->iscsilun = iscsilun; acb->qiov = qiov; acb->read_size = BDRV_SECTOR_SIZE * (size_t)acb->nb_sectors; acb->retries = ISCSI_CMD_RETRIES; if (iscsi_aio_readv_acb(acb) != 0) { if (acb->task) { scsi_free_scsi_task(acb->task); } qemu_aio_release(acb); return NULL; } iscsi_set_events(iscsilun); return &acb->common; }
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 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, sector_qemu2lun(sector_num, iscsilun), acb->buf, size, iscsilun->block_size, 0, 0, fua, 0, 0, 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_nop_timed_event(void *opaque) { IscsiLun *iscsilun = opaque; if (iscsi_get_nops_in_flight(iscsilun->iscsi) > MAX_NOP_FAILURES) { error_report("iSCSI: NOP timeout. Reconnecting..."); iscsi_reconnect(iscsilun->iscsi); } if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) { error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages."); return; } qemu_mod_timer(iscsilun->nop_timer, qemu_get_clock_ms(rt_clock) + NOP_INTERVAL); iscsi_set_events(iscsilun); }
static BlockDriverAIOCB * iscsi_aio_flush(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque) { IscsiLun *iscsilun = bs->opaque; IscsiAIOCB *acb; acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); acb->iscsilun = iscsilun; acb->retries = ISCSI_CMD_RETRIES; if (iscsi_aio_flush_acb(acb) != 0) { qemu_aio_release(acb); return NULL; } iscsi_set_events(iscsilun); return &acb->common; }
static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, unsigned long int req, void *buf, BlockDriverCompletionFunc *cb, void *opaque) { IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = iscsilun->iscsi; struct iscsi_data data; IscsiAIOCB *acb; assert(req == SG_IO); acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); acb->iscsilun = iscsilun; acb->canceled = 0; acb->bh = NULL; acb->status = -EINPROGRESS; acb->buf = NULL; acb->ioh = buf; acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { error_report("iSCSI: Failed to allocate task for scsi command. %s", iscsi_get_error(iscsi)); qemu_aio_release(acb); return NULL; } memset(acb->task, 0, sizeof(struct scsi_task)); switch (acb->ioh->dxfer_direction) { case SG_DXFER_TO_DEV: acb->task->xfer_dir = SCSI_XFER_WRITE; break; case SG_DXFER_FROM_DEV: acb->task->xfer_dir = SCSI_XFER_READ; break; default: acb->task->xfer_dir = SCSI_XFER_NONE; break; } acb->task->cdb_size = acb->ioh->cmd_len; memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len); acb->task->expxferlen = acb->ioh->dxfer_len; data.size = 0; if (acb->task->xfer_dir == SCSI_XFER_WRITE) { if (acb->ioh->iovec_count == 0) { data.data = acb->ioh->dxferp; data.size = acb->ioh->dxfer_len; } else { #if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_out(acb->task, (struct scsi_iovec *) acb->ioh->dxferp, acb->ioh->iovec_count); #else struct iovec *iov = (struct iovec *)acb->ioh->dxferp; acb->buf = g_malloc(acb->ioh->dxfer_len); data.data = acb->buf; data.size = iov_to_buf(iov, acb->ioh->iovec_count, 0, acb->buf, acb->ioh->dxfer_len); #endif } } if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, iscsi_aio_ioctl_cb, (data.size > 0) ? &data : NULL, acb) != 0) { scsi_free_scsi_task(acb->task); qemu_aio_release(acb); return NULL; } /* tell libiscsi to read straight into the buffer we got from ioctl */ if (acb->task->xfer_dir == SCSI_XFER_READ) { if (acb->ioh->iovec_count == 0) { scsi_task_add_data_in_buffer(acb->task, acb->ioh->dxfer_len, acb->ioh->dxferp); } else { #if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_in(acb->task, (struct scsi_iovec *) acb->ioh->dxferp, acb->ioh->iovec_count); #else int i; for (i = 0; i < acb->ioh->iovec_count; i++) { struct iovec *iov = (struct iovec *)acb->ioh->dxferp; scsi_task_add_data_in_buffer(acb->task, iov[i].iov_len, iov[i].iov_base); } #endif } } iscsi_set_events(iscsilun); return &acb->common; }
/* * 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 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; int i; uint64_t lba; uint32_t num_sectors; qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors; acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); trace_iscsi_aio_readv(iscsi, sector_num, nb_sectors, opaque, acb); acb->iscsilun = iscsilun; acb->qiov = qiov; acb->canceled = 0; acb->bh = NULL; acb->status = -EINPROGRESS; 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; } num_sectors = (qemu_read_size + iscsilun->block_size + acb->read_offset - 1) / iscsilun->block_size; acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { error_report("iSCSI: Failed to allocate task for scsi READ16 " "command. %s", iscsi_get_error(iscsi)); qemu_aio_release(acb); return NULL; } memset(acb->task, 0, sizeof(struct scsi_task)); acb->task->xfer_dir = SCSI_XFER_READ; lba = sector_qemu2lun(sector_num, iscsilun); acb->task->expxferlen = qemu_read_size; switch (iscsilun->type) { case TYPE_DISK: acb->task->cdb_size = 16; acb->task->cdb[0] = 0x88; *(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32); *(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff); *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors); break; default: acb->task->cdb_size = 10; acb->task->cdb[0] = 0x28; *(uint32_t *)&acb->task->cdb[2] = htonl(lba); *(uint16_t *)&acb->task->cdb[7] = htons(num_sectors); break; } if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, iscsi_aio_read16_cb, NULL, acb) != 0) { scsi_free_scsi_task(acb->task); 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 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; uint32_t num_sectors; uint64_t lba; struct iscsi_data data; acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb); acb->iscsilun = iscsilun; acb->qiov = qiov; acb->canceled = 0; acb->bh = NULL; acb->status = -EINPROGRESS; /* XXX we should pass the iovec to write16 to avoid the extra copy */ /* this will allow us to get rid of 'buf' completely */ size = nb_sectors * BDRV_SECTOR_SIZE; data.size = MIN(size, acb->qiov->size); /* if the iovec only contains one buffer we can pass it directly */ if (acb->qiov->niov == 1) { acb->buf = NULL; data.data = acb->qiov->iov[0].iov_base; } else { acb->buf = g_malloc(data.size); qemu_iovec_to_buf(acb->qiov, 0, acb->buf, data.size); data.data = acb->buf; } acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { error_report("iSCSI: Failed to allocate task for scsi WRITE16 " "command. %s", iscsi_get_error(iscsi)); qemu_aio_release(acb); return NULL; } memset(acb->task, 0, sizeof(struct scsi_task)); acb->task->xfer_dir = SCSI_XFER_WRITE; acb->task->cdb_size = 16; acb->task->cdb[0] = 0x8a; lba = sector_qemu2lun(sector_num, iscsilun); *(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32); *(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff); num_sectors = size / iscsilun->block_size; *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors); acb->task->expxferlen = size; if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, iscsi_aio_write16_cb, &data, acb) != 0) { scsi_free_scsi_task(acb->task); g_free(acb->buf); qemu_aio_release(acb); return NULL; } iscsi_set_events(iscsilun); return &acb->common; }