[SCSI] ibmvfc: Fix command completion handling
authorBrian King <brking@linux.vnet.ibm.com>
Tue, 20 Apr 2010 19:21:33 +0000 (14:21 -0500)
committerJames Bottomley <James.Bottomley@suse.de>
Sun, 2 May 2010 15:03:03 +0000 (11:03 -0400)
Commands which are completed by the VIOS are placed on a CRQ
in kernel memory for the ibmvfc driver to process. Each CRQ
entry is 16 bytes. The ibmvfc driver reads the first 8 bytes
to check if the entry is valid, then reads the next 8 bytes to get
the handle, which is a pointer the completed command. This fixes
an issue seen on Power 7 where the processor reordered the
loads from memory, resulting in processing command completion
with a stale handle. This could result in command timeouts,
and also early completion of commands.

Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
drivers/scsi/ibmvscsi/ibmvfc.c

index c2eea711a5ceea72103b8ac4bbeff90519c2747e..9372169374e2f7dbc0576a7d9bfbd2194290b540 100644 (file)
@@ -3013,6 +3013,7 @@ static struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost)
        if (crq->valid & 0x80) {
                if (++async_crq->cur == async_crq->size)
                        async_crq->cur = 0;
+               rmb();
        } else
                crq = NULL;
 
@@ -3035,6 +3036,7 @@ static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost)
        if (crq->valid & 0x80) {
                if (++queue->cur == queue->size)
                        queue->cur = 0;
+               rmb();
        } else
                crq = NULL;
 
@@ -3083,12 +3085,14 @@ static void ibmvfc_tasklet(void *data)
                while ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
                        ibmvfc_handle_async(async, vhost);
                        async->valid = 0;
+                       wmb();
                }
 
                /* Pull all the valid messages off the CRQ */
                while ((crq = ibmvfc_next_crq(vhost)) != NULL) {
                        ibmvfc_handle_crq(crq, vhost);
                        crq->valid = 0;
+                       wmb();
                }
 
                vio_enable_interrupts(vdev);
@@ -3096,10 +3100,12 @@ static void ibmvfc_tasklet(void *data)
                        vio_disable_interrupts(vdev);
                        ibmvfc_handle_async(async, vhost);
                        async->valid = 0;
+                       wmb();
                } else if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
                        vio_disable_interrupts(vdev);
                        ibmvfc_handle_crq(crq, vhost);
                        crq->valid = 0;
+                       wmb();
                } else
                        done = 1;
        }