|
|
1.1 ! root 1: /* ! 2: * QEMU Block driver for iSCSI images ! 3: * ! 4: * Copyright (c) 2010-2011 Ronnie Sahlberg <[email protected]> ! 5: * ! 6: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 7: * of this software and associated documentation files (the "Software"), to deal ! 8: * in the Software without restriction, including without limitation the rights ! 9: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 10: * copies of the Software, and to permit persons to whom the Software is ! 11: * furnished to do so, subject to the following conditions: ! 12: * ! 13: * The above copyright notice and this permission notice shall be included in ! 14: * all copies or substantial portions of the Software. ! 15: * ! 16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 17: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 19: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 20: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 21: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 22: * THE SOFTWARE. ! 23: */ ! 24: ! 25: #include "config-host.h" ! 26: ! 27: #include <poll.h> ! 28: #include "qemu-common.h" ! 29: #include "qemu-error.h" ! 30: #include "block_int.h" ! 31: #include "trace.h" ! 32: ! 33: #include <iscsi/iscsi.h> ! 34: #include <iscsi/scsi-lowlevel.h> ! 35: ! 36: ! 37: typedef struct IscsiLun { ! 38: struct iscsi_context *iscsi; ! 39: int lun; ! 40: int block_size; ! 41: unsigned long num_blocks; ! 42: } IscsiLun; ! 43: ! 44: typedef struct IscsiAIOCB { ! 45: BlockDriverAIOCB common; ! 46: QEMUIOVector *qiov; ! 47: QEMUBH *bh; ! 48: IscsiLun *iscsilun; ! 49: struct scsi_task *task; ! 50: uint8_t *buf; ! 51: int status; ! 52: int canceled; ! 53: size_t read_size; ! 54: size_t read_offset; ! 55: } IscsiAIOCB; ! 56: ! 57: struct IscsiTask { ! 58: IscsiLun *iscsilun; ! 59: BlockDriverState *bs; ! 60: int status; ! 61: int complete; ! 62: }; ! 63: ! 64: static void ! 65: iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, ! 66: void *private_data) ! 67: { ! 68: } ! 69: ! 70: static void ! 71: iscsi_aio_cancel(BlockDriverAIOCB *blockacb) ! 72: { ! 73: IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; ! 74: IscsiLun *iscsilun = acb->iscsilun; ! 75: ! 76: acb->common.cb(acb->common.opaque, -ECANCELED); ! 77: acb->canceled = 1; ! 78: ! 79: /* send a task mgmt call to the target to cancel the task on the target */ ! 80: iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, ! 81: iscsi_abort_task_cb, NULL); ! 82: ! 83: /* then also cancel the task locally in libiscsi */ ! 84: iscsi_scsi_task_cancel(iscsilun->iscsi, acb->task); ! 85: } ! 86: ! 87: static AIOPool iscsi_aio_pool = { ! 88: .aiocb_size = sizeof(IscsiAIOCB), ! 89: .cancel = iscsi_aio_cancel, ! 90: }; ! 91: ! 92: ! 93: static void iscsi_process_read(void *arg); ! 94: static void iscsi_process_write(void *arg); ! 95: ! 96: static int iscsi_process_flush(void *arg) ! 97: { ! 98: IscsiLun *iscsilun = arg; ! 99: ! 100: return iscsi_queue_length(iscsilun->iscsi) > 0; ! 101: } ! 102: ! 103: static void ! 104: iscsi_set_events(IscsiLun *iscsilun) ! 105: { ! 106: struct iscsi_context *iscsi = iscsilun->iscsi; ! 107: ! 108: qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read, ! 109: (iscsi_which_events(iscsi) & POLLOUT) ! 110: ? iscsi_process_write : NULL, ! 111: iscsi_process_flush, NULL, iscsilun); ! 112: } ! 113: ! 114: static void ! 115: iscsi_process_read(void *arg) ! 116: { ! 117: IscsiLun *iscsilun = arg; ! 118: struct iscsi_context *iscsi = iscsilun->iscsi; ! 119: ! 120: iscsi_service(iscsi, POLLIN); ! 121: iscsi_set_events(iscsilun); ! 122: } ! 123: ! 124: static void ! 125: iscsi_process_write(void *arg) ! 126: { ! 127: IscsiLun *iscsilun = arg; ! 128: struct iscsi_context *iscsi = iscsilun->iscsi; ! 129: ! 130: iscsi_service(iscsi, POLLOUT); ! 131: iscsi_set_events(iscsilun); ! 132: } ! 133: ! 134: ! 135: static int ! 136: iscsi_schedule_bh(QEMUBHFunc *cb, IscsiAIOCB *acb) ! 137: { ! 138: acb->bh = qemu_bh_new(cb, acb); ! 139: if (!acb->bh) { ! 140: error_report("oom: could not create iscsi bh"); ! 141: return -EIO; ! 142: } ! 143: ! 144: qemu_bh_schedule(acb->bh); ! 145: return 0; ! 146: } ! 147: ! 148: static void ! 149: iscsi_readv_writev_bh_cb(void *p) ! 150: { ! 151: IscsiAIOCB *acb = p; ! 152: ! 153: qemu_bh_delete(acb->bh); ! 154: ! 155: if (acb->canceled == 0) { ! 156: acb->common.cb(acb->common.opaque, acb->status); ! 157: } ! 158: ! 159: qemu_aio_release(acb); ! 160: } ! 161: ! 162: ! 163: static void ! 164: iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status, ! 165: void *command_data, void *opaque) ! 166: { ! 167: IscsiAIOCB *acb = opaque; ! 168: ! 169: trace_iscsi_aio_write10_cb(iscsi, status, acb, acb->canceled); ! 170: ! 171: g_free(acb->buf); ! 172: ! 173: if (acb->canceled != 0) { ! 174: qemu_aio_release(acb); ! 175: scsi_free_scsi_task(acb->task); ! 176: acb->task = NULL; ! 177: return; ! 178: } ! 179: ! 180: acb->status = 0; ! 181: if (status < 0) { ! 182: error_report("Failed to write10 data to iSCSI lun. %s", ! 183: iscsi_get_error(iscsi)); ! 184: acb->status = -EIO; ! 185: } ! 186: ! 187: iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb); ! 188: scsi_free_scsi_task(acb->task); ! 189: acb->task = NULL; ! 190: } ! 191: ! 192: static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun) ! 193: { ! 194: return sector * BDRV_SECTOR_SIZE / iscsilun->block_size; ! 195: } ! 196: ! 197: static BlockDriverAIOCB * ! 198: iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num, ! 199: QEMUIOVector *qiov, int nb_sectors, ! 200: BlockDriverCompletionFunc *cb, ! 201: void *opaque) ! 202: { ! 203: IscsiLun *iscsilun = bs->opaque; ! 204: struct iscsi_context *iscsi = iscsilun->iscsi; ! 205: IscsiAIOCB *acb; ! 206: size_t size; ! 207: int fua = 0; ! 208: ! 209: /* set FUA on writes when cache mode is write through */ ! 210: if (!(bs->open_flags & BDRV_O_CACHE_WB)) { ! 211: fua = 1; ! 212: } ! 213: ! 214: acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque); ! 215: trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb); ! 216: ! 217: acb->iscsilun = iscsilun; ! 218: acb->qiov = qiov; ! 219: ! 220: acb->canceled = 0; ! 221: ! 222: /* XXX we should pass the iovec to write10 to avoid the extra copy */ ! 223: /* this will allow us to get rid of 'buf' completely */ ! 224: size = nb_sectors * BDRV_SECTOR_SIZE; ! 225: acb->buf = g_malloc(size); ! 226: qemu_iovec_to_buffer(acb->qiov, acb->buf); ! 227: acb->task = iscsi_write10_task(iscsi, iscsilun->lun, acb->buf, size, ! 228: sector_qemu2lun(sector_num, iscsilun), ! 229: fua, 0, iscsilun->block_size, ! 230: iscsi_aio_write10_cb, acb); ! 231: if (acb->task == NULL) { ! 232: error_report("iSCSI: Failed to send write10 command. %s", ! 233: iscsi_get_error(iscsi)); ! 234: g_free(acb->buf); ! 235: qemu_aio_release(acb); ! 236: return NULL; ! 237: } ! 238: ! 239: iscsi_set_events(iscsilun); ! 240: ! 241: return &acb->common; ! 242: } ! 243: ! 244: static void ! 245: iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status, ! 246: void *command_data, void *opaque) ! 247: { ! 248: IscsiAIOCB *acb = opaque; ! 249: ! 250: trace_iscsi_aio_read10_cb(iscsi, status, acb, acb->canceled); ! 251: ! 252: if (acb->canceled != 0) { ! 253: qemu_aio_release(acb); ! 254: scsi_free_scsi_task(acb->task); ! 255: acb->task = NULL; ! 256: return; ! 257: } ! 258: ! 259: acb->status = 0; ! 260: if (status != 0) { ! 261: error_report("Failed to read10 data from iSCSI lun. %s", ! 262: iscsi_get_error(iscsi)); ! 263: acb->status = -EIO; ! 264: } ! 265: ! 266: iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb); ! 267: scsi_free_scsi_task(acb->task); ! 268: acb->task = NULL; ! 269: } ! 270: ! 271: static BlockDriverAIOCB * ! 272: iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, ! 273: QEMUIOVector *qiov, int nb_sectors, ! 274: BlockDriverCompletionFunc *cb, ! 275: void *opaque) ! 276: { ! 277: IscsiLun *iscsilun = bs->opaque; ! 278: struct iscsi_context *iscsi = iscsilun->iscsi; ! 279: IscsiAIOCB *acb; ! 280: size_t qemu_read_size, lun_read_size; ! 281: int i; ! 282: ! 283: qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors; ! 284: ! 285: acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque); ! 286: trace_iscsi_aio_readv(iscsi, sector_num, nb_sectors, opaque, acb); ! 287: ! 288: acb->iscsilun = iscsilun; ! 289: acb->qiov = qiov; ! 290: ! 291: acb->canceled = 0; ! 292: acb->read_size = qemu_read_size; ! 293: acb->buf = NULL; ! 294: ! 295: /* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU ! 296: * may be misaligned to the LUN, so we may need to read some extra ! 297: * data. ! 298: */ ! 299: acb->read_offset = 0; ! 300: if (iscsilun->block_size > BDRV_SECTOR_SIZE) { ! 301: uint64_t bdrv_offset = BDRV_SECTOR_SIZE * sector_num; ! 302: ! 303: acb->read_offset = bdrv_offset % iscsilun->block_size; ! 304: } ! 305: ! 306: lun_read_size = (qemu_read_size + iscsilun->block_size ! 307: + acb->read_offset - 1) ! 308: / iscsilun->block_size * iscsilun->block_size; ! 309: acb->task = iscsi_read10_task(iscsi, iscsilun->lun, ! 310: sector_qemu2lun(sector_num, iscsilun), ! 311: lun_read_size, iscsilun->block_size, ! 312: iscsi_aio_read10_cb, acb); ! 313: if (acb->task == NULL) { ! 314: error_report("iSCSI: Failed to send read10 command. %s", ! 315: iscsi_get_error(iscsi)); ! 316: qemu_aio_release(acb); ! 317: return NULL; ! 318: } ! 319: ! 320: for (i = 0; i < acb->qiov->niov; i++) { ! 321: scsi_task_add_data_in_buffer(acb->task, ! 322: acb->qiov->iov[i].iov_len, ! 323: acb->qiov->iov[i].iov_base); ! 324: } ! 325: ! 326: iscsi_set_events(iscsilun); ! 327: ! 328: return &acb->common; ! 329: } ! 330: ! 331: ! 332: static void ! 333: iscsi_synccache10_cb(struct iscsi_context *iscsi, int status, ! 334: void *command_data, void *opaque) ! 335: { ! 336: IscsiAIOCB *acb = opaque; ! 337: ! 338: if (acb->canceled != 0) { ! 339: qemu_aio_release(acb); ! 340: scsi_free_scsi_task(acb->task); ! 341: acb->task = NULL; ! 342: return; ! 343: } ! 344: ! 345: acb->status = 0; ! 346: if (status < 0) { ! 347: error_report("Failed to sync10 data on iSCSI lun. %s", ! 348: iscsi_get_error(iscsi)); ! 349: acb->status = -EIO; ! 350: } ! 351: ! 352: iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb); ! 353: scsi_free_scsi_task(acb->task); ! 354: acb->task = NULL; ! 355: } ! 356: ! 357: static BlockDriverAIOCB * ! 358: iscsi_aio_flush(BlockDriverState *bs, ! 359: BlockDriverCompletionFunc *cb, void *opaque) ! 360: { ! 361: IscsiLun *iscsilun = bs->opaque; ! 362: struct iscsi_context *iscsi = iscsilun->iscsi; ! 363: IscsiAIOCB *acb; ! 364: ! 365: acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque); ! 366: ! 367: acb->iscsilun = iscsilun; ! 368: acb->canceled = 0; ! 369: ! 370: acb->task = iscsi_synchronizecache10_task(iscsi, iscsilun->lun, ! 371: 0, 0, 0, 0, ! 372: iscsi_synccache10_cb, ! 373: acb); ! 374: if (acb->task == NULL) { ! 375: error_report("iSCSI: Failed to send synchronizecache10 command. %s", ! 376: iscsi_get_error(iscsi)); ! 377: qemu_aio_release(acb); ! 378: return NULL; ! 379: } ! 380: ! 381: iscsi_set_events(iscsilun); ! 382: ! 383: return &acb->common; ! 384: } ! 385: ! 386: static int64_t ! 387: iscsi_getlength(BlockDriverState *bs) ! 388: { ! 389: IscsiLun *iscsilun = bs->opaque; ! 390: int64_t len; ! 391: ! 392: len = iscsilun->num_blocks; ! 393: len *= iscsilun->block_size; ! 394: ! 395: return len; ! 396: } ! 397: ! 398: static void ! 399: iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status, ! 400: void *command_data, void *opaque) ! 401: { ! 402: struct IscsiTask *itask = opaque; ! 403: struct scsi_readcapacity10 *rc10; ! 404: struct scsi_task *task = command_data; ! 405: ! 406: if (status != 0) { ! 407: error_report("iSCSI: Failed to read capacity of iSCSI lun. %s", ! 408: iscsi_get_error(iscsi)); ! 409: itask->status = 1; ! 410: itask->complete = 1; ! 411: scsi_free_scsi_task(task); ! 412: return; ! 413: } ! 414: ! 415: rc10 = scsi_datain_unmarshall(task); ! 416: if (rc10 == NULL) { ! 417: error_report("iSCSI: Failed to unmarshall readcapacity10 data."); ! 418: itask->status = 1; ! 419: itask->complete = 1; ! 420: scsi_free_scsi_task(task); ! 421: return; ! 422: } ! 423: ! 424: itask->iscsilun->block_size = rc10->block_size; ! 425: itask->iscsilun->num_blocks = rc10->lba; ! 426: itask->bs->total_sectors = (uint64_t)rc10->lba * ! 427: rc10->block_size / BDRV_SECTOR_SIZE ; ! 428: ! 429: itask->status = 0; ! 430: itask->complete = 1; ! 431: scsi_free_scsi_task(task); ! 432: } ! 433: ! 434: ! 435: static void ! 436: iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data, ! 437: void *opaque) ! 438: { ! 439: struct IscsiTask *itask = opaque; ! 440: struct scsi_task *task; ! 441: ! 442: if (status != 0) { ! 443: itask->status = 1; ! 444: itask->complete = 1; ! 445: return; ! 446: } ! 447: ! 448: task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, 0, 0, ! 449: iscsi_readcapacity10_cb, opaque); ! 450: if (task == NULL) { ! 451: error_report("iSCSI: failed to send readcapacity command."); ! 452: itask->status = 1; ! 453: itask->complete = 1; ! 454: return; ! 455: } ! 456: } ! 457: ! 458: /* ! 459: * We support iscsi url's on the form ! 460: * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun> ! 461: */ ! 462: static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) ! 463: { ! 464: IscsiLun *iscsilun = bs->opaque; ! 465: struct iscsi_context *iscsi = NULL; ! 466: struct iscsi_url *iscsi_url = NULL; ! 467: struct IscsiTask task; ! 468: int ret; ! 469: ! 470: if ((BDRV_SECTOR_SIZE % 512) != 0) { ! 471: error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. " ! 472: "BDRV_SECTOR_SIZE(%lld) is not a multiple " ! 473: "of 512", BDRV_SECTOR_SIZE); ! 474: return -EINVAL; ! 475: } ! 476: ! 477: memset(iscsilun, 0, sizeof(IscsiLun)); ! 478: ! 479: /* Should really append the KVM name after the ':' here */ ! 480: iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:"); ! 481: if (iscsi == NULL) { ! 482: error_report("iSCSI: Failed to create iSCSI context."); ! 483: ret = -ENOMEM; ! 484: goto failed; ! 485: } ! 486: ! 487: iscsi_url = iscsi_parse_full_url(iscsi, filename); ! 488: if (iscsi_url == NULL) { ! 489: error_report("Failed to parse URL : %s %s", filename, ! 490: iscsi_get_error(iscsi)); ! 491: ret = -EINVAL; ! 492: goto failed; ! 493: } ! 494: ! 495: if (iscsi_set_targetname(iscsi, iscsi_url->target)) { ! 496: error_report("iSCSI: Failed to set target name."); ! 497: ret = -EINVAL; ! 498: goto failed; ! 499: } ! 500: ! 501: if (iscsi_url->user != NULL) { ! 502: ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, ! 503: iscsi_url->passwd); ! 504: if (ret != 0) { ! 505: error_report("Failed to set initiator username and password"); ! 506: ret = -EINVAL; ! 507: goto failed; ! 508: } ! 509: } ! 510: if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) { ! 511: error_report("iSCSI: Failed to set session type to normal."); ! 512: ret = -EINVAL; ! 513: goto failed; ! 514: } ! 515: ! 516: iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); ! 517: ! 518: task.iscsilun = iscsilun; ! 519: task.status = 0; ! 520: task.complete = 0; ! 521: task.bs = bs; ! 522: ! 523: iscsilun->iscsi = iscsi; ! 524: iscsilun->lun = iscsi_url->lun; ! 525: ! 526: if (iscsi_full_connect_async(iscsi, iscsi_url->portal, iscsi_url->lun, ! 527: iscsi_connect_cb, &task) ! 528: != 0) { ! 529: error_report("iSCSI: Failed to start async connect."); ! 530: ret = -EINVAL; ! 531: goto failed; ! 532: } ! 533: ! 534: while (!task.complete) { ! 535: iscsi_set_events(iscsilun); ! 536: qemu_aio_wait(); ! 537: } ! 538: if (task.status != 0) { ! 539: error_report("iSCSI: Failed to connect to LUN : %s", ! 540: iscsi_get_error(iscsi)); ! 541: ret = -EINVAL; ! 542: goto failed; ! 543: } ! 544: ! 545: if (iscsi_url != NULL) { ! 546: iscsi_destroy_url(iscsi_url); ! 547: } ! 548: return 0; ! 549: ! 550: failed: ! 551: if (iscsi_url != NULL) { ! 552: iscsi_destroy_url(iscsi_url); ! 553: } ! 554: if (iscsi != NULL) { ! 555: iscsi_destroy_context(iscsi); ! 556: } ! 557: memset(iscsilun, 0, sizeof(IscsiLun)); ! 558: return ret; ! 559: } ! 560: ! 561: static void iscsi_close(BlockDriverState *bs) ! 562: { ! 563: IscsiLun *iscsilun = bs->opaque; ! 564: struct iscsi_context *iscsi = iscsilun->iscsi; ! 565: ! 566: qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL); ! 567: iscsi_destroy_context(iscsi); ! 568: memset(iscsilun, 0, sizeof(IscsiLun)); ! 569: } ! 570: ! 571: static BlockDriver bdrv_iscsi = { ! 572: .format_name = "iscsi", ! 573: .protocol_name = "iscsi", ! 574: ! 575: .instance_size = sizeof(IscsiLun), ! 576: .bdrv_file_open = iscsi_open, ! 577: .bdrv_close = iscsi_close, ! 578: ! 579: .bdrv_getlength = iscsi_getlength, ! 580: ! 581: .bdrv_aio_readv = iscsi_aio_readv, ! 582: .bdrv_aio_writev = iscsi_aio_writev, ! 583: .bdrv_aio_flush = iscsi_aio_flush, ! 584: }; ! 585: ! 586: static void iscsi_block_init(void) ! 587: { ! 588: bdrv_register(&bdrv_iscsi); ! 589: } ! 590: ! 591: block_init(iscsi_block_init);
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.