]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.25/patches.drivers/ibmvfc-async-events-oops
Revert "Move xen patchset to new version's subdir."
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.25 / patches.drivers / ibmvfc-async-events-oops
diff --git a/src/patches/suse-2.6.27.25/patches.drivers/ibmvfc-async-events-oops b/src/patches/suse-2.6.27.25/patches.drivers/ibmvfc-async-events-oops
new file mode 100644 (file)
index 0000000..e33ce64
--- /dev/null
@@ -0,0 +1,599 @@
+Subject: ibmvfc oops while processing async events
+From: Brian King <brking@us.ibm.com>
+Date: Thu Nov 20 13:43:45 2008 +0100:
+References: bnc#445541
+
+While running error injection (port disable/enable loop) during
+I/O stress, the following oops occurred:
+
+cpu 0x0: Vector: 300 (Data Access) at [c00000000f4cbb10]
+    pc: d0000000000c5c44: .ibmvfc_interrupt+0x1a4/0x1f8 [ibmvfc]
+    lr: d0000000000c5c44: .ibmvfc_interrupt+0x1a4/0x1f8 [ibmvfc]
+    sp: c00000000f4cbd90
+   msr: 8000000000009032
+   dar: 0
+ dsisr: 42000000
+  current = 0xc00000001dee77a0
+  paca    = 0xc000000000a92c80
+
+There is a window in the ibmvfc interrupt handler. If, while
+processing an interrupt, after processing both the regular crq
+and the async crq, an async event is added to the async crq, we
+will oops when we process the async queue a second time, due to
+an obvious bug in the code.
+
+Signed-off-by: Brian King <brking@us.ibm.com>
+Acked-by: Hannes Reinecke <hare@suse.de>
+
+---
+ drivers/scsi/ibmvscsi/ibmvfc.c |  266 +++++++++++++++++++++++++++--------------
+ drivers/scsi/ibmvscsi/ibmvfc.h |   26 ++--
+ 2 files changed, 191 insertions(+), 101 deletions(-)
+
+--- a/drivers/scsi/ibmvscsi/ibmvfc.c
++++ b/drivers/scsi/ibmvscsi/ibmvfc.c
+@@ -121,6 +121,7 @@ static const struct {
+       { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ABORT, 0, 1, "transaction cancelled" },
+       { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ABORT, 0, 1, "transaction cancelled implicit" },
+       { IBMVFC_VIOS_FAILURE, IBMVFC_INSUFFICIENT_RESOURCE, DID_REQUEUE, 1, 1, "insufficient resources" },
++      { IBMVFC_VIOS_FAILURE, IBMVFC_PLOGI_REQUIRED, DID_ERROR, 0, 1, "port login required" },
+       { IBMVFC_VIOS_FAILURE, IBMVFC_COMMAND_FAILED, DID_ERROR, 1, 1, "command failed" },
+       { IBMVFC_FC_FAILURE, IBMVFC_INVALID_ELS_CMD_CODE, DID_ERROR, 0, 1, "invalid ELS command code" },
+@@ -278,13 +279,6 @@ static int ibmvfc_get_err_result(struct 
+            rsp->data.info.rsp_code))
+               return DID_ERROR << 16;
+-      if (!vfc_cmd->status) {
+-              if (rsp->flags & FCP_RESID_OVER)
+-                      return rsp->scsi_status | (DID_ERROR << 16);
+-              else
+-                      return rsp->scsi_status | (DID_OK << 16);
+-      }
+-
+       err = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
+       if (err >= 0)
+               return rsp->scsi_status | (cmd_status[err].result << 16);
+@@ -503,6 +497,7 @@ static void ibmvfc_set_host_action(struc
+       case IBMVFC_HOST_ACTION_INIT:
+       case IBMVFC_HOST_ACTION_TGT_DEL:
+       case IBMVFC_HOST_ACTION_QUERY_TGTS:
++      case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
+       case IBMVFC_HOST_ACTION_TGT_ADD:
+       case IBMVFC_HOST_ACTION_NONE:
+       default:
+@@ -765,6 +760,9 @@ static void ibmvfc_scsi_eh_done(struct i
+               cmnd->scsi_done(cmnd);
+       }
++      if (evt->eh_comp)
++              complete(evt->eh_comp);
++
+       ibmvfc_free_event(evt);
+ }
+@@ -1253,6 +1251,7 @@ static void ibmvfc_init_event(struct ibm
+       evt->sync_iu = NULL;
+       evt->crq.format = format;
+       evt->done = done;
++      evt->eh_comp = NULL;
+ }
+ /**
+@@ -1478,6 +1477,11 @@ static void ibmvfc_scsi_done(struct ibmv
+                               sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len;
+                       if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
+                               memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
++                      if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED))
++                              ibmvfc_reinit_host(evt->vhost);
++
++                      if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
++                              cmnd->result = (DID_ERROR << 16);
+                       ibmvfc_log_error(evt);
+               }
+@@ -1490,6 +1494,9 @@ static void ibmvfc_scsi_done(struct ibmv
+               cmnd->scsi_done(cmnd);
+       }
++      if (evt->eh_comp)
++              complete(evt->eh_comp);
++
+       ibmvfc_free_event(evt);
+ }
+@@ -1628,7 +1635,7 @@ static int ibmvfc_reset_device(struct sc
+       struct ibmvfc_host *vhost = shost_priv(sdev->host);
+       struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+       struct ibmvfc_cmd *tmf;
+-      struct ibmvfc_event *evt;
++      struct ibmvfc_event *evt = NULL;
+       union ibmvfc_iu rsp_iu;
+       struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
+       int rsp_rc = -EBUSY;
+@@ -1790,7 +1797,8 @@ static int ibmvfc_abort_task_set(struct 
+ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
+ {
+       struct ibmvfc_host *vhost = shost_priv(sdev->host);
+-      struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
++      struct scsi_target *starget = scsi_target(sdev);
++      struct fc_rport *rport = starget_to_rport(starget);
+       struct ibmvfc_tmf *tmf;
+       struct ibmvfc_event *evt, *found_evt;
+       union ibmvfc_iu rsp;
+@@ -1828,7 +1836,7 @@ static int ibmvfc_cancel_all(struct scsi
+               int_to_scsilun(sdev->lun, &tmf->lun);
+               tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
+               tmf->cancel_key = (unsigned long)sdev->hostdata;
+-              tmf->my_cancel_key = (IBMVFC_TMF_CANCEL_KEY | (unsigned long)sdev->hostdata);
++              tmf->my_cancel_key = (unsigned long)starget->hostdata;
+               evt->sync_iu = &rsp;
+               init_completion(&evt->comp);
+@@ -1860,6 +1868,91 @@ static int ibmvfc_cancel_all(struct scsi
+ }
+ /**
++ * ibmvfc_match_target - Match function for specified target
++ * @evt:      ibmvfc event struct
++ * @device:   device to match (starget)
++ *
++ * Returns:
++ *    1 if event matches starget / 0 if event does not match starget
++ **/
++static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device)
++{
++      if (evt->cmnd && scsi_target(evt->cmnd->device) == device)
++              return 1;
++      return 0;
++}
++
++/**
++ * ibmvfc_match_lun - Match function for specified LUN
++ * @evt:      ibmvfc event struct
++ * @device:   device to match (sdev)
++ *
++ * Returns:
++ *    1 if event matches sdev / 0 if event does not match sdev
++ **/
++static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
++{
++      if (evt->cmnd && evt->cmnd->device == device)
++              return 1;
++      return 0;
++}
++
++/**
++ * ibmvfc_wait_for_ops - Wait for ops to complete
++ * @vhost:    ibmvfc host struct
++ * @device:   device to match (starget or sdev)
++ * @match:    match function
++ *
++ * Returns:
++ *    SUCCESS / FAILED
++ **/
++static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
++                             int (*match) (struct ibmvfc_event *, void *))
++{
++      struct ibmvfc_event *evt;
++      DECLARE_COMPLETION_ONSTACK(comp);
++      int wait;
++      unsigned long flags;
++      signed long timeout = init_timeout * HZ;
++
++      ENTER;
++      do {
++              wait = 0;
++              spin_lock_irqsave(vhost->host->host_lock, flags);
++              list_for_each_entry(evt, &vhost->sent, queue) {
++                      if (match(evt, device)) {
++                              evt->eh_comp = &comp;
++                              wait++;
++                      }
++              }
++              spin_unlock_irqrestore(vhost->host->host_lock, flags);
++
++              if (wait) {
++                      timeout = wait_for_completion_timeout(&comp, timeout);
++
++                      if (!timeout) {
++                              wait = 0;
++                              spin_lock_irqsave(vhost->host->host_lock, flags);
++                              list_for_each_entry(evt, &vhost->sent, queue) {
++                                      if (match(evt, device)) {
++                                              evt->eh_comp = NULL;
++                                              wait++;
++                                      }
++                              }
++                              spin_unlock_irqrestore(vhost->host->host_lock, flags);
++                              if (wait)
++                                      dev_err(vhost->dev, "Timed out waiting for aborted commands\n");
++                              LEAVE;
++                              return wait ? FAILED : SUCCESS;
++                      }
++              }
++      } while (wait);
++
++      LEAVE;
++      return SUCCESS;
++}
++
++/**
+  * ibmvfc_eh_abort_handler - Abort a command
+  * @cmd:      scsi command to abort
+  *
+@@ -1868,29 +1961,21 @@ static int ibmvfc_cancel_all(struct scsi
+  **/
+ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
+ {
+-      struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
+-      struct ibmvfc_event *evt, *pos;
++      struct scsi_device *sdev = cmd->device;
++      struct ibmvfc_host *vhost = shost_priv(sdev->host);
+       int cancel_rc, abort_rc;
+-      unsigned long flags;
++      int rc = FAILED;
+       ENTER;
+       ibmvfc_wait_while_resetting(vhost);
+-      cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_ABORT_TASK_SET);
+-      abort_rc = ibmvfc_abort_task_set(cmd->device);
++      cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
++      abort_rc = ibmvfc_abort_task_set(sdev);
+-      if (!cancel_rc && !abort_rc) {
+-              spin_lock_irqsave(vhost->host->host_lock, flags);
+-              list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
+-                      if (evt->cmnd && evt->cmnd->device == cmd->device)
+-                              ibmvfc_fail_request(evt, DID_ABORT);
+-              }
+-              spin_unlock_irqrestore(vhost->host->host_lock, flags);
+-              LEAVE;
+-              return SUCCESS;
+-      }
++      if (!cancel_rc && !abort_rc)
++              rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
+       LEAVE;
+-      return FAILED;
++      return rc;
+ }
+ /**
+@@ -1902,29 +1987,21 @@ static int ibmvfc_eh_abort_handler(struc
+  **/
+ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
+ {
+-      struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
+-      struct ibmvfc_event *evt, *pos;
++      struct scsi_device *sdev = cmd->device;
++      struct ibmvfc_host *vhost = shost_priv(sdev->host);
+       int cancel_rc, reset_rc;
+-      unsigned long flags;
++      int rc = FAILED;
+       ENTER;
+       ibmvfc_wait_while_resetting(vhost);
+-      cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_LUN_RESET);
+-      reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_LUN_RESET, "LUN");
++      cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
++      reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
+-      if (!cancel_rc && !reset_rc) {
+-              spin_lock_irqsave(vhost->host->host_lock, flags);
+-              list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
+-                      if (evt->cmnd && evt->cmnd->device == cmd->device)
+-                              ibmvfc_fail_request(evt, DID_ABORT);
+-              }
+-              spin_unlock_irqrestore(vhost->host->host_lock, flags);
+-              LEAVE;
+-              return SUCCESS;
+-      }
++      if (!cancel_rc && !reset_rc)
++              rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
+       LEAVE;
+-      return FAILED;
++      return rc;
+ }
+ /**
+@@ -1960,31 +2037,23 @@ static void ibmvfc_dev_abort_all(struct 
+  **/
+ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
+ {
+-      struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
+-      struct scsi_target *starget = scsi_target(cmd->device);
+-      struct ibmvfc_event *evt, *pos;
++      struct scsi_device *sdev = cmd->device;
++      struct ibmvfc_host *vhost = shost_priv(sdev->host);
++      struct scsi_target *starget = scsi_target(sdev);
+       int reset_rc;
++      int rc = FAILED;
+       unsigned long cancel_rc = 0;
+-      unsigned long flags;
+       ENTER;
+       ibmvfc_wait_while_resetting(vhost);
+       starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+-      reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_TARGET_RESET, "target");
++      reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
+-      if (!cancel_rc && !reset_rc) {
+-              spin_lock_irqsave(vhost->host->host_lock, flags);
+-              list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
+-                      if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
+-                              ibmvfc_fail_request(evt, DID_ABORT);
+-              }
+-              spin_unlock_irqrestore(vhost->host->host_lock, flags);
+-              LEAVE;
+-              return SUCCESS;
+-      }
++      if (!cancel_rc && !reset_rc)
++              rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
+       LEAVE;
+-      return FAILED;
++      return rc;
+ }
+ /**
+@@ -2014,23 +2083,18 @@ static void ibmvfc_terminate_rport_io(st
+       struct scsi_target *starget = to_scsi_target(&rport->dev);
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct ibmvfc_host *vhost = shost_priv(shost);
+-      struct ibmvfc_event *evt, *pos;
+       unsigned long cancel_rc = 0;
+       unsigned long abort_rc = 0;
+-      unsigned long flags;
++      int rc = FAILED;
+       ENTER;
+       starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+       starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
+-      if (!cancel_rc && !abort_rc) {
+-              spin_lock_irqsave(shost->host_lock, flags);
+-              list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
+-                      if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
+-                              ibmvfc_fail_request(evt, DID_ABORT);
+-              }
+-              spin_unlock_irqrestore(shost->host_lock, flags);
+-      } else
++      if (!cancel_rc && !abort_rc)
++              rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
++
++      if (rc == FAILED)
+               ibmvfc_issue_fc_host_lip(shost);
+       LEAVE;
+ }
+@@ -2266,6 +2330,28 @@ static int ibmvfc_slave_alloc(struct scs
+ }
+ /**
++ * ibmvfc_target_alloc - Setup the target's task set value
++ * @starget:  struct scsi_target
++ *
++ * Set the target's task set value so that error handling works as
++ * expected.
++ *
++ * Returns:
++ *    0 on success / -ENXIO if device does not exist
++ **/
++static int ibmvfc_target_alloc(struct scsi_target *starget)
++{
++      struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
++      struct ibmvfc_host *vhost = shost_priv(shost);
++      unsigned long flags = 0;
++
++      spin_lock_irqsave(shost->host_lock, flags);
++      starget->hostdata = (void *)(unsigned long)vhost->task_set++;
++      spin_unlock_irqrestore(shost->host_lock, flags);
++      return 0;
++}
++
++/**
+  * ibmvfc_slave_configure - Configure the device
+  * @sdev:     struct scsi_device device to configure
+  *
+@@ -2544,6 +2630,7 @@ static struct scsi_host_template driver_
+       .eh_host_reset_handler = ibmvfc_eh_host_reset_handler,
+       .slave_alloc = ibmvfc_slave_alloc,
+       .slave_configure = ibmvfc_slave_configure,
++      .target_alloc = ibmvfc_target_alloc,
+       .scan_finished = ibmvfc_scan_finished,
+       .change_queue_depth = ibmvfc_change_queue_depth,
+       .change_queue_type = ibmvfc_change_queue_type,
+@@ -2640,7 +2727,7 @@ static irqreturn_t ibmvfc_interrupt(int 
+               } else if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
+                       vio_disable_interrupts(vdev);
+                       ibmvfc_handle_async(async, vhost);
+-                      crq->valid = 0;
++                      async->valid = 0;
+               } else
+                       done = 1;
+       }
+@@ -2711,6 +2798,8 @@ static void ibmvfc_tgt_prli_done(struct 
+                       rsp->status, rsp->error, status);
+               if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+                       ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
++              else
++                      ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+               break;
+       };
+@@ -2805,6 +2894,8 @@ static void ibmvfc_tgt_plogi_done(struct
+               if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+                       ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
++              else
++                      ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+               break;
+       };
+@@ -3096,6 +3187,8 @@ static void ibmvfc_tgt_query_target_done
+                       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+               else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+                       ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
++              else
++                      ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+               break;
+       };
+@@ -3426,6 +3519,7 @@ static int __ibmvfc_work_to_do(struct ib
+       case IBMVFC_HOST_ACTION_ALLOC_TGTS:
+       case IBMVFC_HOST_ACTION_TGT_ADD:
+       case IBMVFC_HOST_ACTION_TGT_DEL:
++      case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
+       case IBMVFC_HOST_ACTION_QUERY:
+       default:
+               break;
+@@ -3547,6 +3641,7 @@ static void ibmvfc_do_work(struct ibmvfc
+                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
+               break;
+       case IBMVFC_HOST_ACTION_TGT_DEL:
++      case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
+               list_for_each_entry(tgt, &vhost->targets, queue) {
+                       if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+                               tgt_dbg(tgt, "Deleting rport\n");
+@@ -3562,8 +3657,17 @@ static void ibmvfc_do_work(struct ibmvfc
+               }
+               if (vhost->state == IBMVFC_INITIALIZING) {
+-                      ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+-                      vhost->job_step = ibmvfc_discover_targets;
++                      if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
++                              ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
++                              ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
++                              vhost->init_retries = 0;
++                              spin_unlock_irqrestore(vhost->host->host_lock, flags);
++                              scsi_unblock_requests(vhost->host);
++                              return;
++                      } else {
++                              ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
++                              vhost->job_step = ibmvfc_discover_targets;
++                      }
+               } else {
+                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
+                       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+@@ -3586,14 +3690,8 @@ static void ibmvfc_do_work(struct ibmvfc
+                       }
+               }
+-              if (!ibmvfc_dev_init_to_do(vhost)) {
+-                      ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
+-                      ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
+-                      vhost->init_retries = 0;
+-                      spin_unlock_irqrestore(vhost->host->host_lock, flags);
+-                      scsi_unblock_requests(vhost->host);
+-                      return;
+-              }
++              if (!ibmvfc_dev_init_to_do(vhost))
++                      ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED);
+               break;
+       case IBMVFC_HOST_ACTION_TGT_ADD:
+               list_for_each_entry(tgt, &vhost->targets, queue) {
+@@ -3601,16 +3699,6 @@ static void ibmvfc_do_work(struct ibmvfc
+                               spin_unlock_irqrestore(vhost->host->host_lock, flags);
+                               ibmvfc_tgt_add_rport(tgt);
+                               return;
+-                      } else if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+-                              tgt_dbg(tgt, "Deleting rport\n");
+-                              rport = tgt->rport;
+-                              tgt->rport = NULL;
+-                              list_del(&tgt->queue);
+-                              spin_unlock_irqrestore(vhost->host->host_lock, flags);
+-                              if (rport)
+-                                      fc_remote_port_delete(rport);
+-                              kref_put(&tgt->kref, ibmvfc_release_tgt);
+-                              return;
+                       }
+               }
+--- a/drivers/scsi/ibmvscsi/ibmvfc.h
++++ b/drivers/scsi/ibmvscsi/ibmvfc.h
+@@ -29,8 +29,8 @@
+ #include "viosrp.h"
+ #define IBMVFC_NAME   "ibmvfc"
+-#define IBMVFC_DRIVER_VERSION         "1.0.2"
+-#define IBMVFC_DRIVER_DATE            "(August 14, 2008)"
++#define IBMVFC_DRIVER_VERSION         "1.0.4"
++#define IBMVFC_DRIVER_DATE            "(November 14, 2008)"
+ #define IBMVFC_DEFAULT_TIMEOUT        15
+ #define IBMVFC_INIT_TIMEOUT           120
+@@ -110,6 +110,7 @@ enum ibmvfc_vios_errors {
+       IBMVFC_TRANS_CANCELLED                  = 0x0006,
+       IBMVFC_TRANS_CANCELLED_IMPLICIT = 0x0007,
+       IBMVFC_INSUFFICIENT_RESOURCE            = 0x0008,
++      IBMVFC_PLOGI_REQUIRED                   = 0x0010,
+       IBMVFC_COMMAND_FAILED                   = 0x8000,
+ };
+@@ -338,7 +339,6 @@ struct ibmvfc_tmf {
+ #define IBMVFC_TMF_LUA_VALID          0x40
+       u32 cancel_key;
+       u32 my_cancel_key;
+-#define IBMVFC_TMF_CANCEL_KEY         0x80000000
+       u32 pad;
+       u64 reserved[2];
+ }__attribute__((packed, aligned (8)));
+@@ -525,10 +525,10 @@ enum ibmvfc_async_event {
+ };
+ struct ibmvfc_crq {
+-      u8 valid;
+-      u8 format;
++      volatile u8 valid;
++      volatile u8 format;
+       u8 reserved[6];
+-      u64 ioba;
++      volatile u64 ioba;
+ }__attribute__((packed, aligned (8)));
+ struct ibmvfc_crq_queue {
+@@ -538,13 +538,13 @@ struct ibmvfc_crq_queue {
+ };
+ struct ibmvfc_async_crq {
+-      u8 valid;
++      volatile u8 valid;
+       u8 pad[3];
+       u32 pad2;
+-      u64 event;
+-      u64 scsi_id;
+-      u64 wwpn;
+-      u64 node_name;
++      volatile u64 event;
++      volatile u64 scsi_id;
++      volatile u64 wwpn;
++      volatile u64 node_name;
+       u64 reserved;
+ }__attribute__((packed, aligned (8)));
+@@ -607,6 +607,7 @@ struct ibmvfc_event {
+       struct srp_direct_buf *ext_list;
+       dma_addr_t ext_list_token;
+       struct completion comp;
++      struct completion *eh_comp;
+       struct timer_list timer;
+ };
+@@ -627,6 +628,7 @@ enum ibmvfc_host_action {
+       IBMVFC_HOST_ACTION_TGT_DEL,
+       IBMVFC_HOST_ACTION_ALLOC_TGTS,
+       IBMVFC_HOST_ACTION_TGT_INIT,
++      IBMVFC_HOST_ACTION_TGT_DEL_FAILED,
+       IBMVFC_HOST_ACTION_TGT_ADD,
+ };
+@@ -702,7 +704,7 @@ struct ibmvfc_host {
+ #define ibmvfc_log(vhost, level, ...) \
+       do { \
+-              if (level >= (vhost)->log_level) \
++              if ((vhost)->log_level >= level) \
+                       dev_err((vhost)->dev, ##__VA_ARGS__); \
+       } while (0)