]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/scsi/ibmvscsi/ibmvfc.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[mv-sheeva.git] / drivers / scsi / ibmvscsi / ibmvfc.c
index 61f8fdea2d96e24824bb2926e1f4cd126912377f..4e0b7c8eb32e731a535b9d2110af3b52f494d78b 100644 (file)
@@ -521,9 +521,10 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
 static void ibmvfc_reinit_host(struct ibmvfc_host *vhost)
 {
        if (vhost->action == IBMVFC_HOST_ACTION_NONE) {
-               scsi_block_requests(vhost->host);
-               ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING);
-               ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+               if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
+                       scsi_block_requests(vhost->host);
+                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+               }
        } else
                vhost->reinit = 1;
 
@@ -555,11 +556,12 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
 /**
  * ibmvfc_init_host - Start host initialization
  * @vhost:             ibmvfc host struct
+ * @relogin:   is this a re-login?
  *
  * Return value:
  *     nothing
  **/
-static void ibmvfc_init_host(struct ibmvfc_host *vhost)
+static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
 {
        struct ibmvfc_target *tgt;
 
@@ -573,6 +575,11 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost)
        }
 
        if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
+               if (!relogin) {
+                       memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
+                       vhost->async_crq.cur = 0;
+               }
+
                list_for_each_entry(tgt, &vhost->targets, queue)
                        tgt->need_login = 1;
                scsi_block_requests(vhost->host);
@@ -854,39 +861,41 @@ static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
 }
 
 /**
- * __ibmvfc_find_target - Find the specified scsi_target (no locking)
+ * __ibmvfc_get_target - Find the specified scsi_target (no locking)
  * @starget:   scsi target struct
  *
  * Return value:
  *     ibmvfc_target struct / NULL if not found
  **/
-static struct ibmvfc_target *__ibmvfc_find_target(struct scsi_target *starget)
+static struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget)
 {
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
        struct ibmvfc_host *vhost = shost_priv(shost);
        struct ibmvfc_target *tgt;
 
        list_for_each_entry(tgt, &vhost->targets, queue)
-               if (tgt->target_id == starget->id)
+               if (tgt->target_id == starget->id) {
+                       kref_get(&tgt->kref);
                        return tgt;
+               }
        return NULL;
 }
 
 /**
- * ibmvfc_find_target - Find the specified scsi_target
+ * ibmvfc_get_target - Find the specified scsi_target
  * @starget:   scsi target struct
  *
  * Return value:
  *     ibmvfc_target struct / NULL if not found
  **/
-static struct ibmvfc_target *ibmvfc_find_target(struct scsi_target *starget)
+static struct ibmvfc_target *ibmvfc_get_target(struct scsi_target *starget)
 {
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
        struct ibmvfc_target *tgt;
        unsigned long flags;
 
        spin_lock_irqsave(shost->host_lock, flags);
-       tgt = __ibmvfc_find_target(starget);
+       tgt = __ibmvfc_get_target(starget);
        spin_unlock_irqrestore(shost->host_lock, flags);
        return tgt;
 }
@@ -963,6 +972,9 @@ static void ibmvfc_get_host_port_state(struct Scsi_Host *shost)
        case IBMVFC_HALTED:
                fc_host_port_state(shost) = FC_PORTSTATE_BLOCKED;
                break;
+       case IBMVFC_NO_CRQ:
+               fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+               break;
        default:
                ibmvfc_log(vhost, 3, "Unknown port state: %d\n", vhost->state);
                fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
@@ -987,6 +999,17 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
                rport->dev_loss_tmo = 1;
 }
 
+/**
+ * ibmvfc_release_tgt - Free memory allocated for a target
+ * @kref:              kref struct
+ *
+ **/
+static void ibmvfc_release_tgt(struct kref *kref)
+{
+       struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
+       kfree(tgt);
+}
+
 /**
  * ibmvfc_get_starget_node_name - Get SCSI target's node name
  * @starget:   scsi target struct
@@ -996,8 +1019,10 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
  **/
 static void ibmvfc_get_starget_node_name(struct scsi_target *starget)
 {
-       struct ibmvfc_target *tgt = ibmvfc_find_target(starget);
+       struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
        fc_starget_port_name(starget) = tgt ? tgt->ids.node_name : 0;
+       if (tgt)
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
 }
 
 /**
@@ -1009,8 +1034,10 @@ static void ibmvfc_get_starget_node_name(struct scsi_target *starget)
  **/
 static void ibmvfc_get_starget_port_name(struct scsi_target *starget)
 {
-       struct ibmvfc_target *tgt = ibmvfc_find_target(starget);
+       struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
        fc_starget_port_name(starget) = tgt ? tgt->ids.port_name : 0;
+       if (tgt)
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
 }
 
 /**
@@ -1022,8 +1049,10 @@ static void ibmvfc_get_starget_port_name(struct scsi_target *starget)
  **/
 static void ibmvfc_get_starget_port_id(struct scsi_target *starget)
 {
-       struct ibmvfc_target *tgt = ibmvfc_find_target(starget);
+       struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
        fc_starget_port_id(starget) = tgt ? tgt->scsi_id : -1;
+       if (tgt)
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
 }
 
 /**
@@ -1036,9 +1065,10 @@ static void ibmvfc_get_starget_port_id(struct scsi_target *starget)
 static int ibmvfc_wait_while_resetting(struct ibmvfc_host *vhost)
 {
        long timeout = wait_event_timeout(vhost->init_wait_q,
-                                         (vhost->state == IBMVFC_ACTIVE ||
-                                          vhost->state == IBMVFC_HOST_OFFLINE ||
-                                          vhost->state == IBMVFC_LINK_DEAD),
+                                         ((vhost->state == IBMVFC_ACTIVE ||
+                                           vhost->state == IBMVFC_HOST_OFFLINE ||
+                                           vhost->state == IBMVFC_LINK_DEAD) &&
+                                          vhost->action == IBMVFC_HOST_ACTION_NONE),
                                          (init_timeout * HZ));
 
        return timeout ? 0 : -EIO;
@@ -1113,7 +1143,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
        login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ;
        login_info->capabilities = IBMVFC_CAN_MIGRATE;
        login_info->async.va = vhost->async_crq.msg_token;
-       login_info->async.len = vhost->async_crq.size;
+       login_info->async.len = vhost->async_crq.size * sizeof(*vhost->async_crq.msgs);
        strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME);
        strncpy(login_info->device_name,
                vhost->host->shost_gendev.bus_id, IBMVFC_MAX_NAME);
@@ -1404,7 +1434,7 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
                err = cmd_status[index].name;
        }
 
-       if (!logerr && (vhost->log_level <= IBMVFC_DEFAULT_LOG_LEVEL))
+       if (!logerr && (vhost->log_level <= (IBMVFC_DEFAULT_LOG_LEVEL + 1)))
                return;
 
        if (rsp->flags & FCP_RSP_LEN_VALID)
@@ -1427,8 +1457,8 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
        struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
        struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
        struct scsi_cmnd *cmnd = evt->cmnd;
-       int rsp_len = 0;
-       int sense_len = rsp->fcp_sense_len;
+       u32 rsp_len = 0;
+       u32 sense_len = rsp->fcp_sense_len;
 
        if (cmnd) {
                if (vfc_cmd->response_flags & IBMVFC_ADAPTER_RESID_VALID)
@@ -1445,7 +1475,7 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
                                rsp_len = rsp->fcp_rsp_len;
                        if ((sense_len + rsp_len) > SCSI_SENSE_BUFFERSIZE)
                                sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len;
-                       if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_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);
 
                        ibmvfc_log_error(evt);
@@ -2054,17 +2084,18 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
 {
        const char *desc = ibmvfc_get_ae_desc(crq->event);
 
-       ibmvfc_log(vhost, 2, "%s event received\n", desc);
+       ibmvfc_log(vhost, 3, "%s event received. scsi_id: %lx, wwpn: %lx,"
+                  " node_name: %lx\n", desc, crq->scsi_id, crq->wwpn, crq->node_name);
 
        switch (crq->event) {
        case IBMVFC_AE_LINK_UP:
        case IBMVFC_AE_RESUME:
                vhost->events_to_log |= IBMVFC_AE_LINKUP;
-               ibmvfc_init_host(vhost);
+               ibmvfc_init_host(vhost, 1);
                break;
        case IBMVFC_AE_SCN_FABRIC:
                vhost->events_to_log |= IBMVFC_AE_RSCN;
-               ibmvfc_init_host(vhost);
+               ibmvfc_init_host(vhost, 1);
                break;
        case IBMVFC_AE_SCN_NPORT:
        case IBMVFC_AE_SCN_GROUP:
@@ -2110,13 +2141,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
                        /* Send back a response */
                        rc = ibmvfc_send_crq_init_complete(vhost);
                        if (rc == 0)
-                               ibmvfc_init_host(vhost);
+                               ibmvfc_init_host(vhost, 0);
                        else
                                dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc);
                        break;
                case IBMVFC_CRQ_INIT_COMPLETE:
                        dev_info(vhost->dev, "Partner initialization complete\n");
-                       ibmvfc_init_host(vhost);
+                       ibmvfc_init_host(vhost, 0);
                        break;
                default:
                        dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format);
@@ -2647,17 +2678,6 @@ static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
                ibmvfc_init_tgt(tgt, job_step);
 }
 
-/**
- * ibmvfc_release_tgt - Free memory allocated for a target
- * @kref:              kref struct
- *
- **/
-static void ibmvfc_release_tgt(struct kref *kref)
-{
-       struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
-       kfree(tgt);
-}
-
 /**
  * ibmvfc_tgt_prli_done - Completion handler for Process Login
  * @evt:       ibmvfc event struct
@@ -2901,6 +2921,139 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
                tgt_dbg(tgt, "Sent Implicit Logout\n");
 }
 
+/**
+ * ibmvfc_adisc_needs_plogi - Does device need PLOGI?
+ * @mad:       ibmvfc passthru mad struct
+ * @tgt:       ibmvfc target struct
+ *
+ * Returns:
+ *     1 if PLOGI needed / 0 if PLOGI not needed
+ **/
+static int ibmvfc_adisc_needs_plogi(struct ibmvfc_passthru_mad *mad,
+                                   struct ibmvfc_target *tgt)
+{
+       if (memcmp(&mad->fc_iu.response[2], &tgt->ids.port_name,
+                  sizeof(tgt->ids.port_name)))
+               return 1;
+       if (memcmp(&mad->fc_iu.response[4], &tgt->ids.node_name,
+                  sizeof(tgt->ids.node_name)))
+               return 1;
+       if (mad->fc_iu.response[6] != tgt->scsi_id)
+               return 1;
+       return 0;
+}
+
+/**
+ * ibmvfc_tgt_adisc_done - Completion handler for ADISC
+ * @evt:       ibmvfc event struct
+ *
+ **/
+static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
+{
+       struct ibmvfc_target *tgt = evt->tgt;
+       struct ibmvfc_host *vhost = evt->vhost;
+       struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
+       u32 status = mad->common.status;
+       u8 fc_reason, fc_explain;
+
+       vhost->discovery_threads--;
+       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+
+       switch (status) {
+       case IBMVFC_MAD_SUCCESS:
+               tgt_dbg(tgt, "ADISC succeeded\n");
+               if (ibmvfc_adisc_needs_plogi(mad, tgt))
+                       tgt->need_login = 1;
+               break;
+       case IBMVFC_MAD_DRIVER_FAILED:
+               break;
+       case IBMVFC_MAD_FAILED:
+       default:
+               tgt->need_login = 1;
+               fc_reason = (mad->fc_iu.response[1] & 0x00ff0000) >> 16;
+               fc_explain = (mad->fc_iu.response[1] & 0x0000ff00) >> 8;
+               tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+                        ibmvfc_get_cmd_error(mad->iu.status, mad->iu.error),
+                        mad->iu.status, mad->iu.error,
+                        ibmvfc_get_fc_type(fc_reason), fc_reason,
+                        ibmvfc_get_ls_explain(fc_explain), fc_explain, status);
+               break;
+       };
+
+       kref_put(&tgt->kref, ibmvfc_release_tgt);
+       ibmvfc_free_event(evt);
+       wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_init_passthru - Initialize an event struct for FC passthru
+ * @evt:               ibmvfc event struct
+ *
+ **/
+static void ibmvfc_init_passthru(struct ibmvfc_event *evt)
+{
+       struct ibmvfc_passthru_mad *mad = &evt->iu.passthru;
+
+       memset(mad, 0, sizeof(*mad));
+       mad->common.version = 1;
+       mad->common.opcode = IBMVFC_PASSTHRU;
+       mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu);
+       mad->cmd_ioba.va = (u64)evt->crq.ioba +
+               offsetof(struct ibmvfc_passthru_mad, iu);
+       mad->cmd_ioba.len = sizeof(mad->iu);
+       mad->iu.cmd_len = sizeof(mad->fc_iu.payload);
+       mad->iu.rsp_len = sizeof(mad->fc_iu.response);
+       mad->iu.cmd.va = (u64)evt->crq.ioba +
+               offsetof(struct ibmvfc_passthru_mad, fc_iu) +
+               offsetof(struct ibmvfc_passthru_fc_iu, payload);
+       mad->iu.cmd.len = sizeof(mad->fc_iu.payload);
+       mad->iu.rsp.va = (u64)evt->crq.ioba +
+               offsetof(struct ibmvfc_passthru_mad, fc_iu) +
+               offsetof(struct ibmvfc_passthru_fc_iu, response);
+       mad->iu.rsp.len = sizeof(mad->fc_iu.response);
+}
+
+/**
+ * ibmvfc_tgt_adisc - Initiate an ADISC for specified target
+ * @tgt:               ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt)
+{
+       struct ibmvfc_passthru_mad *mad;
+       struct ibmvfc_host *vhost = tgt->vhost;
+       struct ibmvfc_event *evt;
+
+       if (vhost->discovery_threads >= disc_threads)
+               return;
+
+       kref_get(&tgt->kref);
+       evt = ibmvfc_get_event(vhost);
+       vhost->discovery_threads++;
+       ibmvfc_init_event(evt, ibmvfc_tgt_adisc_done, IBMVFC_MAD_FORMAT);
+       evt->tgt = tgt;
+
+       ibmvfc_init_passthru(evt);
+       mad = &evt->iu.passthru;
+       mad->iu.flags = IBMVFC_FC_ELS;
+       mad->iu.scsi_id = tgt->scsi_id;
+
+       mad->fc_iu.payload[0] = IBMVFC_ADISC;
+       memcpy(&mad->fc_iu.payload[2], &vhost->login_buf->resp.port_name,
+              sizeof(vhost->login_buf->resp.port_name));
+       memcpy(&mad->fc_iu.payload[4], &vhost->login_buf->resp.node_name,
+              sizeof(vhost->login_buf->resp.node_name));
+       mad->fc_iu.payload[6] = vhost->login_buf->resp.scsi_id & 0x00ffffff;
+
+       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
+       if (ibmvfc_send_event(evt, vhost, default_timeout)) {
+               vhost->discovery_threads--;
+               ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
+       } else
+               tgt_dbg(tgt, "Sent ADISC\n");
+}
+
 /**
  * ibmvfc_tgt_query_target_done - Completion handler for Query Target MAD
  * @evt:       ibmvfc event struct
@@ -2921,6 +3074,8 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
                tgt->new_scsi_id = rsp->scsi_id;
                if (rsp->scsi_id != tgt->scsi_id)
                        ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
+               else
+                       ibmvfc_init_tgt(tgt, ibmvfc_tgt_adisc);
                break;
        case IBMVFC_MAD_DRIVER_FAILED:
                break;
@@ -3210,8 +3365,6 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
        mad->buffer.va = vhost->login_buf_dma;
        mad->buffer.len = sizeof(*vhost->login_buf);
 
-       memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
-       vhost->async_crq.cur = 0;
        ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
 
        if (!ibmvfc_send_event(evt, vhost, default_timeout))
@@ -3336,6 +3489,7 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
                tgt_dbg(tgt, "rport add succeeded\n");
                rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff;
                rport->supported_classes = 0;
+               tgt->target_id = rport->scsi_target_id;
                if (tgt->service_parms.class1_parms[0] & 0x80000000)
                        rport->supported_classes |= FC_COS_CLASS1;
                if (tgt->service_parms.class2_parms[0] & 0x80000000)
@@ -3453,8 +3607,9 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
                        }
                }
 
-               if (vhost->reinit) {
+               if (vhost->reinit && !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
                        vhost->reinit = 0;
+                       scsi_block_requests(vhost->host);
                        ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
                } else {
                        ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
@@ -3800,10 +3955,12 @@ static int ibmvfc_remove(struct vio_dev *vdev)
 
        ENTER;
        ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr);
+       ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
+       ibmvfc_wait_while_resetting(vhost);
+       ibmvfc_release_crq_queue(vhost);
        kthread_stop(vhost->work_thread);
        fc_remove_host(vhost->host);
        scsi_remove_host(vhost->host);
-       ibmvfc_release_crq_queue(vhost);
 
        spin_lock_irqsave(vhost->host->host_lock, flags);
        ibmvfc_purge_requests(vhost, DID_ERROR);