From 8e6c01e698283111e609498daf53915b5fad6563 Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Wed, 22 Feb 2012 14:11:47 +0100
Subject: [PATCH 031/109] scsi-disk: reconcile differences around cancellation

RH-Author: Paolo Bonzini <pbonzini@redhat.com>
Message-id: <1329919979-20948-31-git-send-email-pbonzini@redhat.com>
Patchwork-id: 37509
O-Subject: [RHEL 6.3 qemu-kvm PATCH v2 030/102] scsi-disk: reconcile differences around cancellation
Bugzilla: 782029
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
RH-Acked-by: Orit Wasserman <owasserm@redhat.com>
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>

Upstream commit c7bae6a (scsi-disk: bump SCSIRequest reference count
until aio completion runs, 2011-10-25) is a bit different from what
went into RHEL6.2.  Now that the previous patch introduced io_canceled,
pull the differences.
---
 hw/scsi-disk.c |   24 +++++++++++++-----------
 1 files changed, 13 insertions(+), 11 deletions(-)

Signed-off-by: Michal Novotny <minovotn@redhat.com>
---
 hw/scsi-disk.c |   24 +++++++++++++-----------
 1 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 2cc61d7..7f30567 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -101,16 +101,16 @@ static void scsi_cancel_io(SCSIRequest *req)
     SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
 
     DPRINTF("Cancel tag=0x%x\n", req->tag);
+    r->status &= ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK);
     if (r->req.aiocb) {
         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.  */
+        /* This reference was left in by scsi_*_data.  We take ownership of
+         * it the moment scsi_req_cancel is called, independent of whether
+         * bdrv_aio_cancel completes the request or not.  */
         scsi_req_unref(&r->req);
     }
+    r->req.aiocb = NULL;
 }
 
 static void scsi_read_complete(void * opaque, int ret)
@@ -138,10 +138,11 @@ static void scsi_read_complete(void * opaque, int ret)
     scsi_req_data(&r->req, r->iov.iov_len);
 
 done:
-    scsi_req_unref(&r->req);
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
 }
 
-
 /* Read more data from scsi device into buffer.  */
 static void scsi_read_data(SCSIRequest *req)
 {
@@ -165,7 +166,7 @@ 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.  */
+    /* The request is used as the AIO opaque value, so add a ref.  */
     scsi_req_ref(&r->req);
     if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
         DPRINTF("Data transfer direction invalid\n");
@@ -274,7 +275,9 @@ static void scsi_write_complete(void * opaque, int ret)
     }
 
 done:
-    scsi_req_unref(&r->req);
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
 }
 
 static void scsi_write_data(SCSIRequest *req)
@@ -286,7 +289,7 @@ 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.  */
+    /* The request is used as the AIO opaque value, so add a ref.  */
     scsi_req_ref(&r->req);
     if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
         DPRINTF("Data transfer direction invalid\n");
@@ -345,7 +348,6 @@ 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.7.6

