From f9adb8428857737d17d4be3048eaa0632a920fad Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 19 Oct 2011 15:42:47 +0200 Subject: [PATCH 19/19] scsi-disk: bump SCSIRequest reference count until aio completion runs RH-Author: Paolo Bonzini Message-id: <1319038967-26901-3-git-send-email-pbonzini@redhat.com> Patchwork-id: 34421 O-Subject: [RHEL 6.2 KVM PATCH v2 2/2] scsi-disk: bump SCSIRequest reference count until aio completion runs Bugzilla: 744780 RH-Acked-by: Markus Armbruster RH-Acked-by: Kevin Wolf In some cases a request may be canceled before the completion callback runs. Keep a reference to the request between starting an AIO operation and the corresponding scsi_*_complete. Similarly, when a request has to be retried, keep a reference between scsi_handle_rw_error and scsi_dma_restart_bh. Signed-off-by: Paolo Bonzini --- hw/scsi-disk.c | 36 ++++++++++++++++++++++++++++++++++-- 1 files changed, 34 insertions(+), 2 deletions(-) Signed-off-by: Michal Novotny --- hw/scsi-disk.c | 36 ++++++++++++++++++++++++++++++++++-- 1 files changed, 34 insertions(+), 2 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 87e9724..73c6750 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -118,6 +118,12 @@ static void scsi_cancel_io(SCSIRequest *req) bdrv_aio_cancel(r->req.aiocb); } r->req.aiocb = NULL; + if (r->status & SCSI_REQ_STATUS_RETRY) { + r->status &= ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK); + + /* This reference was left in by scsi_handle_rw_error. */ + scsi_req_unref(&r->req); + } } static void scsi_read_complete(void * opaque, int ret) @@ -133,7 +139,7 @@ static void scsi_read_complete(void * opaque, int ret) if (ret) { if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) { - return; + goto done; } } @@ -143,6 +149,9 @@ static void scsi_read_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; scsi_req_data(&r->req, r->iov.iov_len); + +done: + scsi_req_unref(&r->req); } @@ -169,6 +178,8 @@ static void scsi_read_data(SCSIRequest *req) /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + /* Save a ref for scsi_read_complete, in case r is canceled. */ + scsi_req_ref(&r->req); if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { DPRINTF("Data transfer direction invalid\n"); scsi_read_complete(r, -EINVAL); @@ -181,7 +192,9 @@ static void scsi_read_data(SCSIRequest *req) if (s->tray_open) { scsi_read_complete(r, -ENOMEDIUM); + return; } + r->iov.iov_len = n * 512; qemu_iovec_init_external(&r->qiov, &r->iov, 1); @@ -193,6 +206,13 @@ static void scsi_read_data(SCSIRequest *req) } } +/* + * scsi_handle_rw_error has two return values. 0 means that the error + * must be ignored, 1 means that the error has been processed and the + * caller should not do anything else for this request. Note that + * scsi_handle_rw_error always manages its reference counts, independent + * of the return value. + */ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) { int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); @@ -212,6 +232,10 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) bdrv_mon_event(s->bs, BDRV_ACTION_STOP, error, is_read); vm_stop(RUN_STATE_IO_ERROR); + + /* Keep a reference until the request completes; the corresponding + * unref is in scsi_dma_restart_bh and scsi_cancel_io. */ + scsi_req_ref(&r->req); } else { switch (error) { case ENOMEM: @@ -243,7 +267,7 @@ static void scsi_write_complete(void * opaque, int ret) if (ret) { if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) { - return; + goto done; } } @@ -261,6 +285,9 @@ static void scsi_write_complete(void * opaque, int ret) DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len); scsi_req_data(&r->req, len); } + +done: + scsi_req_unref(&r->req); } static void scsi_write_data(SCSIRequest *req) @@ -272,6 +299,8 @@ static void scsi_write_data(SCSIRequest *req) /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + /* Save a ref for scsi_write_complete, in case r is canceled. */ + scsi_req_ref(&r->req); if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { DPRINTF("Data transfer direction invalid\n"); scsi_write_complete(r, -EINVAL); @@ -282,6 +311,7 @@ static void scsi_write_data(SCSIRequest *req) if (n) { if (s->tray_open) { scsi_write_complete(r, -ENOMEDIUM); + return; } qemu_iovec_init_external(&r->qiov, &r->iov, 1); @@ -328,6 +358,8 @@ static void scsi_dma_restart_bh(void *opaque) scsi_req_complete(&r->req, GOOD); } } + /* This reference was left in by scsi_handle_rw_error. */ + scsi_req_unref(&r->req); } } } -- 1.7.4.4