X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fscsi%2Fqla4xxx%2Fql4_os.c;h=5822dd595826ddd8651078e8232a4461b5dda618;hb=89a93f2f4834f8c126e8d9dd6b368d0b9e21ec3d;hp=2e2b9fedffcc7c763b1f1d8f70df0faa38e27f61;hpb=6208e77e7fa9e69f399fddc55b1cf9527fbde599;p=linux-2.6 diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 2e2b9fedff..5822dd5958 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -63,8 +63,6 @@ static int qla4xxx_sess_get_param(struct iscsi_cls_session *sess, enum iscsi_param param, char *buf); static int qla4xxx_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, char *buf); -static void qla4xxx_conn_stop(struct iscsi_cls_conn *conn, int flag); -static int qla4xxx_conn_start(struct iscsi_cls_conn *conn); static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session); /* @@ -73,10 +71,12 @@ static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session); static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *)); static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); +static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); static int qla4xxx_slave_alloc(struct scsi_device *device); static int qla4xxx_slave_configure(struct scsi_device *device); static void qla4xxx_slave_destroy(struct scsi_device *sdev); +static void qla4xxx_scan_start(struct Scsi_Host *shost); static struct scsi_host_template qla4xxx_driver_template = { .module = THIS_MODULE, @@ -85,12 +85,16 @@ static struct scsi_host_template qla4xxx_driver_template = { .queuecommand = qla4xxx_queuecommand, .eh_device_reset_handler = qla4xxx_eh_device_reset, + .eh_target_reset_handler = qla4xxx_eh_target_reset, .eh_host_reset_handler = qla4xxx_eh_host_reset, .slave_configure = qla4xxx_slave_configure, .slave_alloc = qla4xxx_slave_alloc, .slave_destroy = qla4xxx_slave_destroy, + .scan_finished = iscsi_scan_finished, + .scan_start = qla4xxx_scan_start, + .this_id = -1, .cmd_per_lun = 3, .use_clustering = ENABLE_CLUSTERING, @@ -109,15 +113,10 @@ static struct iscsi_transport qla4xxx_iscsi_transport = { .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | ISCSI_HOST_INITIATOR_NAME, - .sessiondata_size = sizeof(struct ddb_entry), - .host_template = &qla4xxx_driver_template, - .tgt_dscvr = qla4xxx_tgt_dscvr, .get_conn_param = qla4xxx_conn_get_param, .get_session_param = qla4xxx_sess_get_param, .get_host_param = qla4xxx_host_get_param, - .start_conn = qla4xxx_conn_start, - .stop_conn = qla4xxx_conn_stop, .session_recovery_timedout = qla4xxx_recovery_timedout, }; @@ -128,48 +127,19 @@ static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session) struct ddb_entry *ddb_entry = session->dd_data; struct scsi_qla_host *ha = ddb_entry->ha; - DEBUG2(printk("scsi%ld: %s: index [%d] port down retry count of (%d) " - "secs exhausted, marking device DEAD.\n", ha->host_no, - __func__, ddb_entry->fw_ddb_index, - ha->port_down_retry_count)); - - atomic_set(&ddb_entry->state, DDB_STATE_DEAD); - - DEBUG2(printk("scsi%ld: %s: scheduling dpc routine - dpc flags = " - "0x%lx\n", ha->host_no, __func__, ha->dpc_flags)); - queue_work(ha->dpc_thread, &ha->dpc_work); -} - -static int qla4xxx_conn_start(struct iscsi_cls_conn *conn) -{ - struct iscsi_cls_session *session; - struct ddb_entry *ddb_entry; - - session = iscsi_dev_to_session(conn->dev.parent); - ddb_entry = session->dd_data; + if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { + atomic_set(&ddb_entry->state, DDB_STATE_DEAD); - DEBUG2(printk("scsi%ld: %s: index [%d] starting conn\n", - ddb_entry->ha->host_no, __func__, - ddb_entry->fw_ddb_index)); - iscsi_unblock_session(session); - return 0; -} + DEBUG2(printk("scsi%ld: %s: index [%d] port down retry count " + "of (%d) secs exhausted, marking device DEAD.\n", + ha->host_no, __func__, ddb_entry->fw_ddb_index, + ha->port_down_retry_count)); -static void qla4xxx_conn_stop(struct iscsi_cls_conn *conn, int flag) -{ - struct iscsi_cls_session *session; - struct ddb_entry *ddb_entry; - - session = iscsi_dev_to_session(conn->dev.parent); - ddb_entry = session->dd_data; - - DEBUG2(printk("scsi%ld: %s: index [%d] stopping conn\n", - ddb_entry->ha->host_no, __func__, - ddb_entry->fw_ddb_index)); - if (flag == STOP_CONN_RECOVER) - iscsi_block_session(session); - else - printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag); + DEBUG2(printk("scsi%ld: %s: scheduling dpc routine - dpc " + "flags = 0x%lx\n", + ha->host_no, __func__, ha->dpc_flags)); + queue_work(ha->dpc_thread, &ha->dpc_work); + } } static int qla4xxx_host_get_param(struct Scsi_Host *shost, @@ -302,12 +272,15 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry) return err; } - ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0); + ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0, 0); if (!ddb_entry->conn) { iscsi_remove_session(ddb_entry->sess); DEBUG2(printk(KERN_ERR "Could not add connection.\n")); return -ENOMEM; } + + /* finally ready to go */ + iscsi_unblock_session(ddb_entry->sess); return 0; } @@ -316,7 +289,8 @@ struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha) struct ddb_entry *ddb_entry; struct iscsi_cls_session *sess; - sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport); + sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport, + sizeof(struct ddb_entry)); if (!sess) return NULL; @@ -327,6 +301,18 @@ struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha) return ddb_entry; } +static void qla4xxx_scan_start(struct Scsi_Host *shost) +{ + struct scsi_qla_host *ha = shost_priv(shost); + struct ddb_entry *ddb_entry, *ddbtemp; + + /* finish setup of sessions that were already setup in firmware */ + list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) { + if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) + qla4xxx_add_sess(ddb_entry); + } +} + /* * Timer routines */ @@ -364,6 +350,7 @@ void qla4xxx_mark_device_missing(struct scsi_qla_host *ha, DEBUG3(printk("scsi%d:%d:%d: index [%d] marked MISSING\n", ha->host_no, ddb_entry->bus, ddb_entry->target, ddb_entry->fw_ddb_index)); + iscsi_block_session(ddb_entry->sess); iscsi_conn_error(ddb_entry->conn, ISCSI_ERR_CONN_FAILED); } @@ -430,9 +417,21 @@ static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, { struct scsi_qla_host *ha = to_qla_host(cmd->device->host); struct ddb_entry *ddb_entry = cmd->device->hostdata; + struct iscsi_cls_session *sess = ddb_entry->sess; struct srb *srb; int rval; + if (!sess) { + cmd->result = DID_IMM_RETRY << 16; + goto qc_fail_command; + } + + rval = iscsi_session_chkready(sess); + if (rval) { + cmd->result = rval; + goto qc_fail_command; + } + if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { if (atomic_read(&ddb_entry->state) == DDB_STATE_DEAD) { cmd->result = DID_NO_CONNECT << 16; @@ -879,8 +878,9 @@ static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha) * qla4xxx_recover_adapter - recovers adapter after a fatal error * @ha: Pointer to host adapter structure. * @renew_ddb_list: Indicates what to do with the adapter's ddb list - * after adapter recovery has completed. - * 0=preserve ddb list, 1=destroy and rebuild ddb list + * + * renew_ddb_list value can be 0=preserve ddb list, 1=destroy and rebuild + * ddb list. **/ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha, uint8_t renew_ddb_list) @@ -889,6 +889,7 @@ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha, /* Stall incoming I/O until we are done */ clear_bit(AF_ONLINE, &ha->flags); + DEBUG2(printk("scsi%ld: %s calling qla4xxx_cmd_wait\n", ha->host_no, __func__)); @@ -1191,7 +1192,6 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, int ret = -ENODEV, status; struct Scsi_Host *host; struct scsi_qla_host *ha; - struct ddb_entry *ddb_entry, *ddbtemp; uint8_t init_retry_count = 0; char buf[34]; @@ -1310,26 +1310,15 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, if (ret) goto probe_failed; - /* Update transport device information for all devices. */ - list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) { - if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) - if (qla4xxx_add_sess(ddb_entry)) - goto remove_host; - } - printk(KERN_INFO " QLogic iSCSI HBA Driver version: %s\n" " QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n", qla4xxx_version_str, ha->pdev->device, pci_name(ha->pdev), ha->host_no, ha->firmware_version[0], ha->firmware_version[1], ha->patch_number, ha->build_number); - + scsi_scan_host(host); return 0; -remove_host: - qla4xxx_free_ddb_list(ha); - scsi_remove_host(host); - probe_failed: qla4xxx_free_adapter(ha); scsi_host_put(ha->host); @@ -1493,7 +1482,7 @@ static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha) } /** - * qla4xxx_eh_wait_for_active_target_commands - wait for active cmds to finish. + * qla4xxx_eh_wait_for_commands - wait for active cmds to finish. * @ha: pointer to to HBA * @t: target id * @l: lun id @@ -1501,20 +1490,22 @@ static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha) * This function waits for all outstanding commands to a lun to complete. It * returns 0 if all pending commands are returned and 1 otherwise. **/ -static int qla4xxx_eh_wait_for_active_target_commands(struct scsi_qla_host *ha, - int t, int l) +static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha, + struct scsi_target *stgt, + struct scsi_device *sdev) { int cnt; int status = 0; struct scsi_cmnd *cmd; /* - * Waiting for all commands for the designated target in the active - * array + * Waiting for all commands for the designated target or dev + * in the active array */ for (cnt = 0; cnt < ha->host->can_queue; cnt++) { cmd = scsi_host_find_tag(ha->host, cnt); - if (cmd && cmd->device->id == t && cmd->device->lun == l) { + if (cmd && stgt == scsi_target(cmd->device) && + (!sdev || sdev == cmd->device)) { if (!qla4xxx_eh_wait_on_command(ha, cmd)) { status++; break; @@ -1559,24 +1550,19 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) goto eh_dev_reset_done; } - /* Send marker. */ - ha->marker_needed = 1; - - /* - * If we are coming down the EH path, wait for all commands to complete - * for the device. - */ - if (cmd->device->host->shost_state == SHOST_RECOVERY) { - if (qla4xxx_eh_wait_for_active_target_commands(ha, - cmd->device->id, - cmd->device->lun)){ - dev_info(&ha->pdev->dev, - "DEVICE RESET FAILED - waiting for " - "commands.\n"); - goto eh_dev_reset_done; - } + if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device), + cmd->device)) { + dev_info(&ha->pdev->dev, + "DEVICE RESET FAILED - waiting for " + "commands.\n"); + goto eh_dev_reset_done; } + /* Send marker. */ + if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun, + MM_LUN_RESET) != QLA_SUCCESS) + goto eh_dev_reset_done; + dev_info(&ha->pdev->dev, "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n", ha->host_no, cmd->device->channel, cmd->device->id, @@ -1589,6 +1575,59 @@ eh_dev_reset_done: return ret; } +/** + * qla4xxx_eh_target_reset - callback for target reset. + * @cmd: Pointer to Linux's SCSI command structure + * + * This routine is called by the Linux OS to reset the target. + **/ +static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd) +{ + struct scsi_qla_host *ha = to_qla_host(cmd->device->host); + struct ddb_entry *ddb_entry = cmd->device->hostdata; + int stat; + + if (!ddb_entry) + return FAILED; + + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET RESET ISSUED.\n"); + + DEBUG2(printk(KERN_INFO + "scsi%ld: TARGET_DEVICE_RESET cmd=%p jiffies = 0x%lx, " + "to=%x,dpc_flags=%lx, status=%x allowed=%d\n", + ha->host_no, cmd, jiffies, cmd->timeout_per_command / HZ, + ha->dpc_flags, cmd->result, cmd->allowed)); + + stat = qla4xxx_reset_target(ha, ddb_entry); + if (stat != QLA_SUCCESS) { + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET RESET FAILED.\n"); + return FAILED; + } + + if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device), + NULL)) { + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET DEVICE RESET FAILED - " + "waiting for commands.\n"); + return FAILED; + } + + /* Send marker. */ + if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun, + MM_TGT_WARM_RESET) != QLA_SUCCESS) { + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET DEVICE RESET FAILED - " + "marker iocb failed.\n"); + return FAILED; + } + + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET RESET SUCCEEDED.\n"); + return SUCCESS; +} + /** * qla4xxx_eh_host_reset - kernel callback * @cmd: Pointer to Linux's SCSI command structure @@ -1615,9 +1654,12 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd) return FAILED; } - if (qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST) == QLA_SUCCESS) { + /* make sure the dpc thread is stopped while we reset the hba */ + clear_bit(AF_ONLINE, &ha->flags); + flush_workqueue(ha->dpc_thread); + + if (qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST) == QLA_SUCCESS) return_status = SUCCESS; - } dev_info(&ha->pdev->dev, "HOST RESET %s.\n", return_status == FAILED ? "FAILED" : "SUCCEDED");