]> git.proxmox.com Git - qemu.git/blob - block/iscsi.c
ISCSI: Add support for thin-provisioning via discard/UNMAP and bigger LUNs
[qemu.git] / block / iscsi.c
1 /*
2 * QEMU Block driver for iSCSI images
3 *
4 * Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
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, 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 void
387 iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
388 void *command_data, void *opaque)
389 {
390 IscsiAIOCB *acb = opaque;
391
392 if (acb->canceled != 0) {
393 qemu_aio_release(acb);
394 scsi_free_scsi_task(acb->task);
395 acb->task = NULL;
396 return;
397 }
398
399 acb->status = 0;
400 if (status < 0) {
401 error_report("Failed to unmap data on iSCSI lun. %s",
402 iscsi_get_error(iscsi));
403 acb->status = -EIO;
404 }
405
406 iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
407 scsi_free_scsi_task(acb->task);
408 acb->task = NULL;
409 }
410
411 static BlockDriverAIOCB *
412 iscsi_aio_discard(BlockDriverState *bs,
413 int64_t sector_num, int nb_sectors,
414 BlockDriverCompletionFunc *cb, void *opaque)
415 {
416 IscsiLun *iscsilun = bs->opaque;
417 struct iscsi_context *iscsi = iscsilun->iscsi;
418 IscsiAIOCB *acb;
419 struct unmap_list list[1];
420
421 acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
422
423 acb->iscsilun = iscsilun;
424 acb->canceled = 0;
425
426 list[0].lba = sector_qemu2lun(sector_num, iscsilun);
427 list[0].num = nb_sectors * BDRV_SECTOR_SIZE / iscsilun->block_size;
428
429 acb->task = iscsi_unmap_task(iscsi, iscsilun->lun,
430 0, 0, &list[0], 1,
431 iscsi_unmap_cb,
432 acb);
433 if (acb->task == NULL) {
434 error_report("iSCSI: Failed to send unmap command. %s",
435 iscsi_get_error(iscsi));
436 qemu_aio_release(acb);
437 return NULL;
438 }
439
440 iscsi_set_events(iscsilun);
441
442 return &acb->common;
443 }
444
445 static int64_t
446 iscsi_getlength(BlockDriverState *bs)
447 {
448 IscsiLun *iscsilun = bs->opaque;
449 int64_t len;
450
451 len = iscsilun->num_blocks;
452 len *= iscsilun->block_size;
453
454 return len;
455 }
456
457 static void
458 iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
459 void *command_data, void *opaque)
460 {
461 struct IscsiTask *itask = opaque;
462 struct scsi_readcapacity16 *rc16;
463 struct scsi_task *task = command_data;
464
465 if (status != 0) {
466 error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
467 iscsi_get_error(iscsi));
468 itask->status = 1;
469 itask->complete = 1;
470 scsi_free_scsi_task(task);
471 return;
472 }
473
474 rc16 = scsi_datain_unmarshall(task);
475 if (rc16 == NULL) {
476 error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
477 itask->status = 1;
478 itask->complete = 1;
479 scsi_free_scsi_task(task);
480 return;
481 }
482
483 itask->iscsilun->block_size = rc16->block_length;
484 itask->iscsilun->num_blocks = rc16->returned_lba + 1;
485 itask->bs->total_sectors = itask->iscsilun->num_blocks *
486 itask->iscsilun->block_size / BDRV_SECTOR_SIZE ;
487
488 itask->status = 0;
489 itask->complete = 1;
490 scsi_free_scsi_task(task);
491 }
492
493 static void
494 iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
495 void *opaque)
496 {
497 struct IscsiTask *itask = opaque;
498 struct scsi_task *task;
499
500 if (status != 0) {
501 itask->status = 1;
502 itask->complete = 1;
503 return;
504 }
505
506 task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun,
507 iscsi_readcapacity16_cb, opaque);
508 if (task == NULL) {
509 error_report("iSCSI: failed to send readcapacity16 command.");
510 itask->status = 1;
511 itask->complete = 1;
512 return;
513 }
514 }
515
516 static int parse_chap(struct iscsi_context *iscsi, const char *target)
517 {
518 QemuOptsList *list;
519 QemuOpts *opts;
520 const char *user = NULL;
521 const char *password = NULL;
522
523 list = qemu_find_opts("iscsi");
524 if (!list) {
525 return 0;
526 }
527
528 opts = qemu_opts_find(list, target);
529 if (opts == NULL) {
530 opts = QTAILQ_FIRST(&list->head);
531 if (!opts) {
532 return 0;
533 }
534 }
535
536 user = qemu_opt_get(opts, "user");
537 if (!user) {
538 return 0;
539 }
540
541 password = qemu_opt_get(opts, "password");
542 if (!password) {
543 error_report("CHAP username specified but no password was given");
544 return -1;
545 }
546
547 if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
548 error_report("Failed to set initiator username and password");
549 return -1;
550 }
551
552 return 0;
553 }
554
555 static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
556 {
557 QemuOptsList *list;
558 QemuOpts *opts;
559 const char *digest = NULL;
560
561 list = qemu_find_opts("iscsi");
562 if (!list) {
563 return;
564 }
565
566 opts = qemu_opts_find(list, target);
567 if (opts == NULL) {
568 opts = QTAILQ_FIRST(&list->head);
569 if (!opts) {
570 return;
571 }
572 }
573
574 digest = qemu_opt_get(opts, "header-digest");
575 if (!digest) {
576 return;
577 }
578
579 if (!strcmp(digest, "CRC32C")) {
580 iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C);
581 } else if (!strcmp(digest, "NONE")) {
582 iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE);
583 } else if (!strcmp(digest, "CRC32C-NONE")) {
584 iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE);
585 } else if (!strcmp(digest, "NONE-CRC32C")) {
586 iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
587 } else {
588 error_report("Invalid header-digest setting : %s", digest);
589 }
590 }
591
592 static char *parse_initiator_name(const char *target)
593 {
594 QemuOptsList *list;
595 QemuOpts *opts;
596 const char *name = NULL;
597
598 list = qemu_find_opts("iscsi");
599 if (!list) {
600 return g_strdup("iqn.2008-11.org.linux-kvm");
601 }
602
603 opts = qemu_opts_find(list, target);
604 if (opts == NULL) {
605 opts = QTAILQ_FIRST(&list->head);
606 if (!opts) {
607 return g_strdup("iqn.2008-11.org.linux-kvm");
608 }
609 }
610
611 name = qemu_opt_get(opts, "initiator-name");
612 if (!name) {
613 return g_strdup("iqn.2008-11.org.linux-kvm");
614 }
615
616 return g_strdup(name);
617 }
618
619 /*
620 * We support iscsi url's on the form
621 * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
622 */
623 static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
624 {
625 IscsiLun *iscsilun = bs->opaque;
626 struct iscsi_context *iscsi = NULL;
627 struct iscsi_url *iscsi_url = NULL;
628 struct IscsiTask task;
629 char *initiator_name = NULL;
630 int ret;
631
632 if ((BDRV_SECTOR_SIZE % 512) != 0) {
633 error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. "
634 "BDRV_SECTOR_SIZE(%lld) is not a multiple "
635 "of 512", BDRV_SECTOR_SIZE);
636 return -EINVAL;
637 }
638
639 iscsi_url = iscsi_parse_full_url(iscsi, filename);
640 if (iscsi_url == NULL) {
641 error_report("Failed to parse URL : %s %s", filename,
642 iscsi_get_error(iscsi));
643 ret = -EINVAL;
644 goto failed;
645 }
646
647 memset(iscsilun, 0, sizeof(IscsiLun));
648
649 initiator_name = parse_initiator_name(iscsi_url->target);
650
651 iscsi = iscsi_create_context(initiator_name);
652 if (iscsi == NULL) {
653 error_report("iSCSI: Failed to create iSCSI context.");
654 ret = -ENOMEM;
655 goto failed;
656 }
657
658 if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
659 error_report("iSCSI: Failed to set target name.");
660 ret = -EINVAL;
661 goto failed;
662 }
663
664 if (iscsi_url->user != NULL) {
665 ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
666 iscsi_url->passwd);
667 if (ret != 0) {
668 error_report("Failed to set initiator username and password");
669 ret = -EINVAL;
670 goto failed;
671 }
672 }
673
674 /* check if we got CHAP username/password via the options */
675 if (parse_chap(iscsi, iscsi_url->target) != 0) {
676 error_report("iSCSI: Failed to set CHAP user/password");
677 ret = -EINVAL;
678 goto failed;
679 }
680
681 if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
682 error_report("iSCSI: Failed to set session type to normal.");
683 ret = -EINVAL;
684 goto failed;
685 }
686
687 iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
688
689 /* check if we got HEADER_DIGEST via the options */
690 parse_header_digest(iscsi, iscsi_url->target);
691
692 task.iscsilun = iscsilun;
693 task.status = 0;
694 task.complete = 0;
695 task.bs = bs;
696
697 iscsilun->iscsi = iscsi;
698 iscsilun->lun = iscsi_url->lun;
699
700 if (iscsi_full_connect_async(iscsi, iscsi_url->portal, iscsi_url->lun,
701 iscsi_connect_cb, &task)
702 != 0) {
703 error_report("iSCSI: Failed to start async connect.");
704 ret = -EINVAL;
705 goto failed;
706 }
707
708 while (!task.complete) {
709 iscsi_set_events(iscsilun);
710 qemu_aio_wait();
711 }
712 if (task.status != 0) {
713 error_report("iSCSI: Failed to connect to LUN : %s",
714 iscsi_get_error(iscsi));
715 ret = -EINVAL;
716 goto failed;
717 }
718
719 if (iscsi_url != NULL) {
720 iscsi_destroy_url(iscsi_url);
721 }
722 return 0;
723
724 failed:
725 if (initiator_name != NULL) {
726 g_free(initiator_name);
727 }
728 if (iscsi_url != NULL) {
729 iscsi_destroy_url(iscsi_url);
730 }
731 if (iscsi != NULL) {
732 iscsi_destroy_context(iscsi);
733 }
734 memset(iscsilun, 0, sizeof(IscsiLun));
735 return ret;
736 }
737
738 static void iscsi_close(BlockDriverState *bs)
739 {
740 IscsiLun *iscsilun = bs->opaque;
741 struct iscsi_context *iscsi = iscsilun->iscsi;
742
743 qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL);
744 iscsi_destroy_context(iscsi);
745 memset(iscsilun, 0, sizeof(IscsiLun));
746 }
747
748 static BlockDriver bdrv_iscsi = {
749 .format_name = "iscsi",
750 .protocol_name = "iscsi",
751
752 .instance_size = sizeof(IscsiLun),
753 .bdrv_file_open = iscsi_open,
754 .bdrv_close = iscsi_close,
755
756 .bdrv_getlength = iscsi_getlength,
757
758 .bdrv_aio_readv = iscsi_aio_readv,
759 .bdrv_aio_writev = iscsi_aio_writev,
760 .bdrv_aio_flush = iscsi_aio_flush,
761
762 .bdrv_aio_discard = iscsi_aio_discard,
763 };
764
765 static void iscsi_block_init(void)
766 {
767 bdrv_register(&bdrv_iscsi);
768 }
769
770 block_init(iscsi_block_init);