linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / scsi / ibmvscsi / ibmvscsi.c
index 7b3c54e..eaefedd 100644 (file)
@@ -69,6 +69,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/dma-mapping.h>
+#include <linux/delay.h>
 #include <asm/vio.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -85,9 +86,8 @@ static int max_id = 64;
 static int max_channel = 3;
 static int init_timeout = 5;
 static int max_requests = 50;
-static int max_sectors = 32 * 8; /* default max I/O 32 pages */
 
-#define IBMVSCSI_VERSION "1.5.3"
+#define IBMVSCSI_VERSION "1.5.8"
 
 MODULE_DESCRIPTION("IBM Virtual SCSI");
 MODULE_AUTHOR("Dave Boutcher");
@@ -102,8 +102,6 @@ module_param_named(init_timeout, init_timeout, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds");
 module_param_named(max_requests, max_requests, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter");
-module_param_named(max_sectors, max_sectors, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(max_sectors, "Maximum sectors per request for this adapter");
 
 /* ------------------------------------------------------------
  * Routines for the event pool and event structs
@@ -147,6 +145,8 @@ static int initialize_event_pool(struct event_pool *pool,
                        sizeof(*evt->xfer_iu) * i;
                evt->xfer_iu = pool->iu_storage + i;
                evt->hostdata = hostdata;
+               evt->ext_list = NULL;
+               evt->ext_list_token = 0;
        }
 
        return 0;
@@ -163,9 +163,16 @@ static void release_event_pool(struct event_pool *pool,
                               struct ibmvscsi_host_data *hostdata)
 {
        int i, in_use = 0;
-       for (i = 0; i < pool->size; ++i)
+       for (i = 0; i < pool->size; ++i) {
                if (atomic_read(&pool->events[i].free) != 1)
                        ++in_use;
+               if (pool->events[i].ext_list) {
+                       dma_free_coherent(hostdata->dev,
+                                 SG_ALL * sizeof(struct memory_descriptor),
+                                 pool->events[i].ext_list,
+                                 pool->events[i].ext_list_token);
+               }
+       }
        if (in_use)
                printk(KERN_WARNING
                       "ibmvscsi: releasing event pool with %d "
@@ -258,6 +265,7 @@ static void init_event_struct(struct srp_event_struct *evt_struct,
 {
        evt_struct->cmnd = NULL;
        evt_struct->cmnd_done = NULL;
+       evt_struct->sync_srp = NULL;
        evt_struct->crq.format = format;
        evt_struct->crq.timeout = timeout;
        evt_struct->done = done;
@@ -287,24 +295,41 @@ static void set_srp_direction(struct scsi_cmnd *cmd,
        } else {
                if (cmd->sc_data_direction == DMA_TO_DEVICE) {
                        srp_cmd->data_out_format = SRP_INDIRECT_BUFFER;
-                       srp_cmd->data_out_count = numbuf;
+                       srp_cmd->data_out_count =
+                               numbuf < MAX_INDIRECT_BUFS ?
+                                       numbuf: MAX_INDIRECT_BUFS;
                } else {
                        srp_cmd->data_in_format = SRP_INDIRECT_BUFFER;
-                       srp_cmd->data_in_count = numbuf;
+                       srp_cmd->data_in_count =
+                               numbuf < MAX_INDIRECT_BUFS ?
+                                       numbuf: MAX_INDIRECT_BUFS;
                }
        }
 }
 
+static void unmap_sg_list(int num_entries, 
+               struct device *dev,
+               struct memory_descriptor *md)
+{ 
+       int i;
+
+       for (i = 0; i < num_entries; ++i) {
+               dma_unmap_single(dev,
+                       md[i].virtual_address,
+                       md[i].length, DMA_BIDIRECTIONAL);
+       }
+}
+
 /**
  * unmap_cmd_data: - Unmap data pointed in srp_cmd based on the format
  * @cmd:       srp_cmd whose additional_data member will be unmapped
  * @dev:       device for which the memory is mapped
  *
 */
-static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev)
+static void unmap_cmd_data(struct srp_cmd *cmd,
+                          struct srp_event_struct *evt_struct,
+                          struct device *dev)
 {
-       int i;
-
        if ((cmd->data_out_format == SRP_NO_BUFFER) &&
            (cmd->data_in_format == SRP_NO_BUFFER))
                return;
@@ -319,15 +344,34 @@ static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev)
                        (struct indirect_descriptor *)cmd->additional_data;
                int num_mapped = indirect->head.length / 
                        sizeof(indirect->list[0]);
-               for (i = 0; i < num_mapped; ++i) {
-                       struct memory_descriptor *data = &indirect->list[i];
-                       dma_unmap_single(dev,
-                                        data->virtual_address,
-                                        data->length, DMA_BIDIRECTIONAL);
+
+               if (num_mapped <= MAX_INDIRECT_BUFS) {
+                       unmap_sg_list(num_mapped, dev, &indirect->list[0]);
+                       return;
                }
+
+               unmap_sg_list(num_mapped, dev, evt_struct->ext_list);
        }
 }
 
+static int map_sg_list(int num_entries, 
+                      struct scatterlist *sg,
+                      struct memory_descriptor *md)
+{
+       int i;
+       u64 total_length = 0;
+
+       for (i = 0; i < num_entries; ++i) {
+               struct memory_descriptor *descr = md + i;
+               struct scatterlist *sg_entry = &sg[i];
+               descr->virtual_address = sg_dma_address(sg_entry);
+               descr->length = sg_dma_len(sg_entry);
+               descr->memory_handle = 0;
+               total_length += sg_dma_len(sg_entry);
+       }
+       return total_length;
+}
+
 /**
  * map_sg_data: - Maps dma for a scatterlist and initializes decriptor fields
  * @cmd:       Scsi_Cmnd with the scatterlist
@@ -338,10 +382,11 @@ static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev)
  * Returns 1 on success.
 */
 static int map_sg_data(struct scsi_cmnd *cmd,
+                      struct srp_event_struct *evt_struct,
                       struct srp_cmd *srp_cmd, struct device *dev)
 {
 
-       int i, sg_mapped;
+       int sg_mapped;
        u64 total_length = 0;
        struct scatterlist *sg = cmd->request_buffer;
        struct memory_descriptor *data =
@@ -364,27 +409,46 @@ static int map_sg_data(struct scsi_cmnd *cmd,
                return 1;
        }
 
-       if (sg_mapped > MAX_INDIRECT_BUFS) {
+       if (sg_mapped > SG_ALL) {
                printk(KERN_ERR
                       "ibmvscsi: More than %d mapped sg entries, got %d\n",
-                      MAX_INDIRECT_BUFS, sg_mapped);
+                      SG_ALL, sg_mapped);
                return 0;
        }
 
        indirect->head.virtual_address = 0;
        indirect->head.length = sg_mapped * sizeof(indirect->list[0]);
        indirect->head.memory_handle = 0;
-       for (i = 0; i < sg_mapped; ++i) {
-               struct memory_descriptor *descr = &indirect->list[i];
-               struct scatterlist *sg_entry = &sg[i];
-               descr->virtual_address = sg_dma_address(sg_entry);
-               descr->length = sg_dma_len(sg_entry);
-               descr->memory_handle = 0;
-               total_length += sg_dma_len(sg_entry);
+
+       if (sg_mapped <= MAX_INDIRECT_BUFS) {
+               total_length = map_sg_list(sg_mapped, sg, &indirect->list[0]);
+               indirect->total_length = total_length;
+               return 1;
        }
-       indirect->total_length = total_length;
 
-       return 1;
+       /* get indirect table */
+       if (!evt_struct->ext_list) {
+               evt_struct->ext_list =(struct memory_descriptor*)
+                       dma_alloc_coherent(dev, 
+                               SG_ALL * sizeof(struct memory_descriptor),
+                               &evt_struct->ext_list_token, 0);
+               if (!evt_struct->ext_list) {
+                   printk(KERN_ERR
+                       "ibmvscsi: Can't allocate memory for indirect table\n");
+                       return 0;
+                       
+               }
+       }
+
+       total_length = map_sg_list(sg_mapped, sg, evt_struct->ext_list);        
+
+       indirect->total_length = total_length;
+       indirect->head.virtual_address = evt_struct->ext_list_token;
+       indirect->head.length = sg_mapped * sizeof(indirect->list[0]);
+       memcpy(indirect->list, evt_struct->ext_list,
+               MAX_INDIRECT_BUFS * sizeof(struct memory_descriptor));
+       
+       return 1;
 }
 
 /**
@@ -429,6 +493,7 @@ static int map_single_data(struct scsi_cmnd *cmd,
  * Returns 1 on success.
 */
 static int map_data_for_srp_cmd(struct scsi_cmnd *cmd,
+                               struct srp_event_struct *evt_struct,
                                struct srp_cmd *srp_cmd, struct device *dev)
 {
        switch (cmd->sc_data_direction) {
@@ -451,7 +516,7 @@ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd,
        if (!cmd->request_buffer)
                return 1;
        if (cmd->use_sg)
-               return map_sg_data(cmd, srp_cmd, dev);
+               return map_sg_data(cmd, evt_struct, srp_cmd, dev);
        return map_single_data(cmd, srp_cmd, dev);
 }
 
@@ -469,7 +534,6 @@ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd,
 static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct,
                                   struct ibmvscsi_host_data *hostdata)
 {
-       struct scsi_cmnd *cmnd = evt_struct->cmnd;
        u64 *crq_as_u64 = (u64 *) &evt_struct->crq;
        int rc;
 
@@ -479,25 +543,8 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct,
         * can handle more requests (can_queue) when we actually can't
         */
        if ((evt_struct->crq.format == VIOSRP_SRP_FORMAT) &&
-           (atomic_dec_if_positive(&hostdata->request_limit) < 0)) {
-               /* See if the adapter is disabled */
-               if (atomic_read(&hostdata->request_limit) < 0) {
-                       if (cmnd)
-                               cmnd->result = DID_ERROR << 16;
-                       if (evt_struct->cmnd_done)
-                               evt_struct->cmnd_done(cmnd);
-                       unmap_cmd_data(&evt_struct->iu.srp.cmd,
-                                      hostdata->dev);
-                       free_event_struct(&hostdata->pool, evt_struct);
-                       return 0;
-               } else {
-                       printk("ibmvscsi: Warning, request_limit exceeded\n");
-                       unmap_cmd_data(&evt_struct->iu.srp.cmd,
-                                      hostdata->dev);
-                       free_event_struct(&hostdata->pool, evt_struct);
-                       return SCSI_MLQUEUE_HOST_BUSY;
-               }
-       }
+           (atomic_dec_if_positive(&hostdata->request_limit) < 0))
+               goto send_error;
 
        /* Copy the IU into the transfer area */
        *evt_struct->xfer_iu = evt_struct->iu;
@@ -513,18 +560,18 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct,
             ibmvscsi_send_crq(hostdata, crq_as_u64[0], crq_as_u64[1])) != 0) {
                list_del(&evt_struct->list);
 
-               cmnd = evt_struct->cmnd;
-               printk(KERN_ERR "ibmvscsi: failed to send event struct rc %d\n",
+               printk(KERN_ERR "ibmvscsi: send error %d\n",
                       rc);
-               unmap_cmd_data(&evt_struct->iu.srp.cmd, hostdata->dev);
-               free_event_struct(&hostdata->pool, evt_struct);
-               if (cmnd)
-                       cmnd->result = DID_ERROR << 16;
-               if (evt_struct->cmnd_done)
-                       evt_struct->cmnd_done(cmnd);
+               goto send_error;
        }
 
        return 0;
+
+ send_error:
+       unmap_cmd_data(&evt_struct->iu.srp.cmd, evt_struct, hostdata->dev);
+
+       free_event_struct(&hostdata->pool, evt_struct);
+       return SCSI_MLQUEUE_HOST_BUSY;
 }
 
 /**
@@ -539,6 +586,13 @@ static void handle_cmd_rsp(struct srp_event_struct *evt_struct)
        struct srp_rsp *rsp = &evt_struct->xfer_iu->srp.rsp;
        struct scsi_cmnd *cmnd = evt_struct->cmnd;
 
+       if (unlikely(rsp->type != SRP_RSP_TYPE)) {
+               if (printk_ratelimit())
+                       printk(KERN_WARNING 
+                              "ibmvscsi: bad SRP RSP type %d\n",
+                              rsp->type);
+       }
+       
        if (cmnd) {
                cmnd->result = rsp->status;
                if (((cmnd->result >> 1) & 0x1f) == CHECK_CONDITION)
@@ -546,6 +600,7 @@ static void handle_cmd_rsp(struct srp_event_struct *evt_struct)
                               rsp->sense_and_response_data,
                               rsp->sense_data_list_length);
                unmap_cmd_data(&evt_struct->iu.srp.cmd, 
+                              evt_struct, 
                               evt_struct->hostdata->dev);
 
                if (rsp->doover)
@@ -578,6 +633,7 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd,
 {
        struct srp_cmd *srp_cmd;
        struct srp_event_struct *evt_struct;
+       struct indirect_descriptor *indirect;
        struct ibmvscsi_host_data *hostdata =
                (struct ibmvscsi_host_data *)&cmnd->device->host->hostdata;
        u16 lun = lun_from_dev(cmnd->device);
@@ -586,14 +642,6 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd,
        if (!evt_struct)
                return SCSI_MLQUEUE_HOST_BUSY;
 
-       init_event_struct(evt_struct,
-                         handle_cmd_rsp,
-                         VIOSRP_SRP_FORMAT,
-                         cmnd->timeout);
-
-       evt_struct->cmnd = cmnd;
-       evt_struct->cmnd_done = done;
-
        /* Set up the actual SRP IU */
        srp_cmd = &evt_struct->iu.srp.cmd;
        memset(srp_cmd, 0x00, sizeof(*srp_cmd));
@@ -601,17 +649,25 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd,
        memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(cmnd->cmnd));
        srp_cmd->lun = ((u64) lun) << 48;
 
-       if (!map_data_for_srp_cmd(cmnd, srp_cmd, hostdata->dev)) {
+       if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) {
                printk(KERN_ERR "ibmvscsi: couldn't convert cmd to srp_cmd\n");
                free_event_struct(&hostdata->pool, evt_struct);
                return SCSI_MLQUEUE_HOST_BUSY;
        }
 
+       init_event_struct(evt_struct,
+                         handle_cmd_rsp,
+                         VIOSRP_SRP_FORMAT,
+                         cmnd->timeout_per_command/HZ);
+
+       evt_struct->cmnd = cmnd;
+       evt_struct->cmnd_done = done;
+
        /* Fix up dma address of the buffer itself */
-       if ((srp_cmd->data_out_format == SRP_INDIRECT_BUFFER) ||
-           (srp_cmd->data_in_format == SRP_INDIRECT_BUFFER)) {
-               struct indirect_descriptor *indirect =
-                   (struct indirect_descriptor *)srp_cmd->additional_data;
+       indirect = (struct indirect_descriptor *)srp_cmd->additional_data;
+       if (((srp_cmd->data_out_format == SRP_INDIRECT_BUFFER) ||
+           (srp_cmd->data_in_format == SRP_INDIRECT_BUFFER)) &&
+           (indirect->head.virtual_address == 0)) {
                indirect->head.virtual_address = evt_struct->crq.IU_data_ptr +
                    offsetof(struct srp_cmd, additional_data) +
                    offsetof(struct indirect_descriptor, list);
@@ -649,10 +705,20 @@ static void adapter_info_rsp(struct srp_event_struct *evt_struct)
                       hostdata->madapter_info.partition_number,
                       hostdata->madapter_info.os_type,
                       hostdata->madapter_info.port_max_txu[0]);
-
+               
                if (hostdata->madapter_info.port_max_txu[0]) 
-                   hostdata->host->max_sectors = 
-                       hostdata->madapter_info.port_max_txu[0] >> 9;
+                       hostdata->host->max_sectors = 
+                               hostdata->madapter_info.port_max_txu[0] >> 9;
+               
+               if (hostdata->madapter_info.os_type == 3 &&
+                   strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) {
+                       printk("ibmvscsi: host (Ver. %s) doesn't support large"
+                              "transfers\n",
+                              hostdata->madapter_info.srp_version);
+                       printk("ibmvscsi: limiting scatterlists to %d\n",
+                              MAX_INDIRECT_BUFS);
+                       hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS;
+               }
        }
 }
 
@@ -670,8 +736,6 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata)
        struct viosrp_adapter_info *req;
        struct srp_event_struct *evt_struct;
        
-       memset(&hostdata->madapter_info, 0x00, sizeof(hostdata->madapter_info));
-       
        evt_struct = get_event_struct(&hostdata->pool);
        if (!evt_struct) {
                printk(KERN_ERR "ibmvscsi: couldn't allocate an event "
@@ -720,7 +784,8 @@ static void login_rsp(struct srp_event_struct *evt_struct)
        case SRP_LOGIN_RSP_TYPE:        /* it worked! */
                break;
        case SRP_LOGIN_REJ_TYPE:        /* refused! */
-               printk(KERN_INFO "ibmvscsi: SRP_LOGIN_REQ rejected\n");
+               printk(KERN_INFO "ibmvscsi: SRP_LOGIN_REJ reason %u\n",
+                      evt_struct->xfer_iu->srp.login_rej.reason);
                /* Login failed.  */
                atomic_set(&hostdata->request_limit, -1);
                return;
@@ -752,6 +817,9 @@ static void login_rsp(struct srp_event_struct *evt_struct)
                return;
        }
 
+       /* If we had any pending I/Os, kick them */
+       scsi_unblock_requests(hostdata->host);
+
        send_mad_adapter_info(hostdata);
        return;
 }
@@ -780,6 +848,7 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata)
                          init_timeout * HZ);
 
        login = &evt_struct->iu.srp.login_req;
+       memset(login, 0x00, sizeof(struct srp_login_req));
        login->type = SRP_LOGIN_REQ_TYPE;
        login->max_requested_initiator_to_target_iulen = sizeof(union srp_iu);
        login->required_buffer_formats = 0x0006;
@@ -803,6 +872,10 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata)
  */
 static void sync_completion(struct srp_event_struct *evt_struct)
 {
+       /* copy the response back */
+       if (evt_struct->sync_srp)
+               *evt_struct->sync_srp = *evt_struct->xfer_iu;
+       
        complete(&evt_struct->comp);
 }
 
@@ -817,11 +890,15 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
        struct srp_tsk_mgmt *tsk_mgmt;
        struct srp_event_struct *evt;
        struct srp_event_struct *tmp_evt, *found_evt;
+       union viosrp_iu srp_rsp;
+       int rsp_rc;
+       unsigned long flags;
        u16 lun = lun_from_dev(cmd->device);
 
        /* First, find this command in our sent list so we can figure
         * out the correct tag
         */
+       spin_lock_irqsave(hostdata->host->host_lock, flags);
        found_evt = NULL;
        list_for_each_entry(tmp_evt, &hostdata->sent, list) {
                if (tmp_evt->cmnd == cmd) {
@@ -830,11 +907,14 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
                }
        }
 
-       if (!found_evt) 
+       if (!found_evt) {
+               spin_unlock_irqrestore(hostdata->host->host_lock, flags);
                return FAILED;
+       }
 
        evt = get_event_struct(&hostdata->pool);
        if (evt == NULL) {
+               spin_unlock_irqrestore(hostdata->host->host_lock, flags);
                printk(KERN_ERR "ibmvscsi: failed to allocate abort event\n");
                return FAILED;
        }
@@ -856,20 +936,45 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
        printk(KERN_INFO "ibmvscsi: aborting command. lun 0x%lx, tag 0x%lx\n",
               tsk_mgmt->lun, tsk_mgmt->managed_task_tag);
 
+       evt->sync_srp = &srp_rsp;
        init_completion(&evt->comp);
-       if (ibmvscsi_send_srp_event(evt, hostdata) != 0) {
+       rsp_rc = ibmvscsi_send_srp_event(evt, hostdata);
+       spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+       if (rsp_rc != 0) {
                printk(KERN_ERR "ibmvscsi: failed to send abort() event\n");
                return FAILED;
        }
 
-       spin_unlock_irq(hostdata->host->host_lock);
        wait_for_completion(&evt->comp);
-       spin_lock_irq(hostdata->host->host_lock);
+
+       /* make sure we got a good response */
+       if (unlikely(srp_rsp.srp.generic.type != SRP_RSP_TYPE)) {
+               if (printk_ratelimit())
+                       printk(KERN_WARNING 
+                              "ibmvscsi: abort bad SRP RSP type %d\n",
+                              srp_rsp.srp.generic.type);
+               return FAILED;
+       }
+
+       if (srp_rsp.srp.rsp.rspvalid)
+               rsp_rc = *((int *)srp_rsp.srp.rsp.sense_and_response_data);
+       else
+               rsp_rc = srp_rsp.srp.rsp.status;
+
+       if (rsp_rc) {
+               if (printk_ratelimit())
+                       printk(KERN_WARNING 
+                      "ibmvscsi: abort code %d for task tag 0x%lx\n",
+                              rsp_rc,
+                              tsk_mgmt->managed_task_tag);
+               return FAILED;
+       }
 
        /* Because we dropped the spinlock above, it's possible
         * The event is no longer in our list.  Make sure it didn't
         * complete while we were aborting
         */
+       spin_lock_irqsave(hostdata->host->host_lock, flags);
        found_evt = NULL;
        list_for_each_entry(tmp_evt, &hostdata->sent, list) {
                if (tmp_evt->cmnd == cmd) {
@@ -878,17 +983,24 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
                }
        }
 
+       if (found_evt == NULL) {
+               spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+               printk(KERN_INFO
+                      "ibmvscsi: aborted task tag 0x%lx completed\n",
+                      tsk_mgmt->managed_task_tag);
+               return SUCCESS;
+       }
+
        printk(KERN_INFO
               "ibmvscsi: successfully aborted task tag 0x%lx\n",
               tsk_mgmt->managed_task_tag);
 
-       if (found_evt == NULL)
-               return SUCCESS;
-
        cmd->result = (DID_ABORT << 16);
        list_del(&found_evt->list);
-       unmap_cmd_data(&found_evt->iu.srp.cmd, found_evt->hostdata->dev);
+       unmap_cmd_data(&found_evt->iu.srp.cmd, found_evt,
+                      found_evt->hostdata->dev);
        free_event_struct(&found_evt->hostdata->pool, found_evt);
+       spin_unlock_irqrestore(hostdata->host->host_lock, flags);
        atomic_inc(&hostdata->request_limit);
        return SUCCESS;
 }
@@ -906,10 +1018,15 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
        struct srp_tsk_mgmt *tsk_mgmt;
        struct srp_event_struct *evt;
        struct srp_event_struct *tmp_evt, *pos;
+       union viosrp_iu srp_rsp;
+       int rsp_rc;
+       unsigned long flags;
        u16 lun = lun_from_dev(cmd->device);
 
+       spin_lock_irqsave(hostdata->host->host_lock, flags);
        evt = get_event_struct(&hostdata->pool);
        if (evt == NULL) {
+               spin_unlock_irqrestore(hostdata->host->host_lock, flags);
                printk(KERN_ERR "ibmvscsi: failed to allocate reset event\n");
                return FAILED;
        }
@@ -930,25 +1047,51 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
        printk(KERN_INFO "ibmvscsi: resetting device. lun 0x%lx\n",
               tsk_mgmt->lun);
 
+       evt->sync_srp = &srp_rsp;
        init_completion(&evt->comp);
-       if (ibmvscsi_send_srp_event(evt, hostdata) != 0) {
+       rsp_rc = ibmvscsi_send_srp_event(evt, hostdata);
+       spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+       if (rsp_rc != 0) {
                printk(KERN_ERR "ibmvscsi: failed to send reset event\n");
                return FAILED;
        }
 
-       spin_unlock_irq(hostdata->host->host_lock);
        wait_for_completion(&evt->comp);
-       spin_lock_irq(hostdata->host->host_lock);
+
+       /* make sure we got a good response */
+       if (unlikely(srp_rsp.srp.generic.type != SRP_RSP_TYPE)) {
+               if (printk_ratelimit())
+                       printk(KERN_WARNING 
+                              "ibmvscsi: reset bad SRP RSP type %d\n",
+                              srp_rsp.srp.generic.type);
+               return FAILED;
+       }
+
+       if (srp_rsp.srp.rsp.rspvalid)
+               rsp_rc = *((int *)srp_rsp.srp.rsp.sense_and_response_data);
+       else
+               rsp_rc = srp_rsp.srp.rsp.status;
+
+       if (rsp_rc) {
+               if (printk_ratelimit())
+                       printk(KERN_WARNING 
+                              "ibmvscsi: reset code %d for task tag 0x%lx\n",
+                      rsp_rc,
+                              tsk_mgmt->managed_task_tag);
+               return FAILED;
+       }
 
        /* We need to find all commands for this LUN that have not yet been
         * responded to, and fail them with DID_RESET
         */
+       spin_lock_irqsave(hostdata->host->host_lock, flags);
        list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) {
                if ((tmp_evt->cmnd) && (tmp_evt->cmnd->device == cmd->device)) {
                        if (tmp_evt->cmnd)
                                tmp_evt->cmnd->result = (DID_RESET << 16);
                        list_del(&tmp_evt->list);
-                       unmap_cmd_data(&tmp_evt->iu.srp.cmd, tmp_evt->hostdata->dev);
+                       unmap_cmd_data(&tmp_evt->iu.srp.cmd, tmp_evt,
+                                      tmp_evt->hostdata->dev);
                        free_event_struct(&tmp_evt->hostdata->pool,
                                                   tmp_evt);
                        atomic_inc(&hostdata->request_limit);
@@ -958,6 +1101,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
                                tmp_evt->done(tmp_evt);
                }
        }
+       spin_unlock_irqrestore(hostdata->host->host_lock, flags);
        return SUCCESS;
 }
 
@@ -965,7 +1109,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
  * purge_requests: Our virtual adapter just shut down.  purge any sent requests
  * @hostdata:    the adapter
  */
-static void purge_requests(struct ibmvscsi_host_data *hostdata)
+static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code)
 {
        struct srp_event_struct *tmp_evt, *pos;
        unsigned long flags;
@@ -974,8 +1118,9 @@ static void purge_requests(struct ibmvscsi_host_data *hostdata)
        list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) {
                list_del(&tmp_evt->list);
                if (tmp_evt->cmnd) {
-                       tmp_evt->cmnd->result = (DID_ERROR << 16);
+                       tmp_evt->cmnd->result = (error_code << 16);
                        unmap_cmd_data(&tmp_evt->iu.srp.cmd, 
+                                      tmp_evt, 
                                       tmp_evt->hostdata->dev);
                        if (tmp_evt->cmnd_done)
                                tmp_evt->cmnd_done(tmp_evt->cmnd);
@@ -1028,12 +1173,30 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
                        printk(KERN_ERR "ibmvscsi: unknown crq message type\n");
                }
                return;
-       case 0xFF:              /* Hypervisor telling us the connection is closed */
-               printk(KERN_INFO "ibmvscsi: Virtual adapter failed!\n");
+       case 0xFF:      /* Hypervisor telling us the connection is closed */
+               scsi_block_requests(hostdata->host);
+               if (crq->format == 0x06) {
+                       /* We need to re-setup the interpartition connection */
+                       printk(KERN_INFO
+                              "ibmvscsi: Re-enabling adapter!\n");
+                       purge_requests(hostdata, DID_REQUEUE);
+                       if (ibmvscsi_reenable_crq_queue(&hostdata->queue,
+                                                       hostdata) == 0)
+                               if (ibmvscsi_send_crq(hostdata,
+                                                     0xC001000000000000LL, 0))
+                                       printk(KERN_ERR
+                                              "ibmvscsi: transmit error after"
+                                              " enable\n");
+               } else {
+                       printk(KERN_INFO
+                              "ibmvscsi: Virtual adapter failed rc %d!\n",
+                              crq->format);
 
-               atomic_set(&hostdata->request_limit, -1);
-               purge_requests(hostdata);
-               ibmvscsi_reset_crq_queue(&hostdata->queue, hostdata);
+                       atomic_set(&hostdata->request_limit, -1);
+                       purge_requests(hostdata, DID_ERROR);
+                       ibmvscsi_reset_crq_queue(&hostdata->queue, hostdata);
+               }
+               scsi_unblock_requests(hostdata->host);
                return;
        case 0x80:              /* real payload */
                break;
@@ -1055,6 +1218,13 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
                return;
        }
 
+       if (atomic_read(&evt_struct->free)) {
+               printk(KERN_ERR
+                      "ibmvscsi: received duplicate  correlation_token 0x%p!\n",
+                      (void *)crq->IU_data_ptr);
+               return;
+       }
+
        if (crq->format == VIOSRP_SRP_FORMAT)
                atomic_add(evt_struct->xfer_iu->srp.rsp.request_limit_delta,
                           &hostdata->request_limit);
@@ -1273,7 +1443,7 @@ static struct scsi_host_template driver_template = {
        .cmd_per_lun = 16,
        .can_queue = 1,         /* Updated after SRP_LOGIN */
        .this_id = -1,
-       .sg_tablesize = MAX_INDIRECT_BUFS,
+       .sg_tablesize = SG_ALL,
        .use_clustering = ENABLE_CLUSTERING,
        .shost_attrs = ibmvscsi_attrs,
 };
@@ -1302,7 +1472,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
        hostdata->host = host;
        hostdata->dev = dev;
        atomic_set(&hostdata->request_limit, -1);
-       hostdata->host->max_sectors = max_sectors; 
+       hostdata->host->max_sectors = 32 * 8; /* default max I/O 32 pages */
 
        if (ibmvscsi_init_crq_queue(&hostdata->queue, hostdata,
                                    max_requests) != 0) {
@@ -1336,8 +1506,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
                     time_before(jiffies, wait_switch) &&
                     atomic_read(&hostdata->request_limit) < 2;) {
 
-                       set_current_state(TASK_UNINTERRUPTIBLE);
-                       schedule_timeout(HZ / 100);
+                       msleep(10);
                }
 
                /* if we now have a valid request_limit, initiate a scan */
@@ -1377,15 +1546,18 @@ static int ibmvscsi_remove(struct vio_dev *vdev)
  */
 static struct vio_device_id ibmvscsi_device_table[] __devinitdata = {
        {"vscsi", "IBM,v-scsi"},
-       {0,}
+       { "", "" }
 };
-
 MODULE_DEVICE_TABLE(vio, ibmvscsi_device_table);
+
 static struct vio_driver ibmvscsi_driver = {
-       .name = "ibmvscsi",
        .id_table = ibmvscsi_device_table,
        .probe = ibmvscsi_probe,
-       .remove = ibmvscsi_remove
+       .remove = ibmvscsi_remove,
+       .driver = {
+               .name = "ibmvscsi",
+               .owner = THIS_MODULE,
+       }
 };
 
 int __init ibmvscsi_module_init(void)