ql4_os.c
来自「linux 内核源代码」· C语言 代码 · 共 1,732 行 · 第 1/4 页
C
1,732 行
/** * qla4xxx_flush_active_srbs - returns all outstanding i/o requests to O.S. * @ha: Pointer to host adapter structure. * * This routine is called just prior to a HARD RESET to return all * outstanding commands back to the Operating System. * Caller should make sure that the following locks are released * before this calling routine: Hardware lock, and io_request_lock. **/static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha){ struct srb *srb; int i; unsigned long flags; spin_lock_irqsave(&ha->hardware_lock, flags); for (i = 0; i < ha->host->can_queue; i++) { srb = qla4xxx_del_from_active_array(ha, i); if (srb != NULL) { srb->cmd->result = DID_RESET << 16; qla4xxx_srb_compl(ha, srb); } } spin_unlock_irqrestore(&ha->hardware_lock, flags);}/** * 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 **/static int qla4xxx_recover_adapter(struct scsi_qla_host *ha, uint8_t renew_ddb_list){ int status; /* 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__)); /* Wait for outstanding commands to complete. * Stalls the driver for max 30 secs */ status = qla4xxx_cmd_wait(ha); qla4xxx_disable_intrs(ha); /* Flush any pending ddb changed AENs */ qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS); /* Reset the firmware. If successful, function * returns with ISP interrupts enabled. */ if (status == QLA_SUCCESS) { DEBUG2(printk("scsi%ld: %s - Performing soft reset..\n", ha->host_no, __func__)); qla4xxx_flush_active_srbs(ha); if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS) status = qla4xxx_soft_reset(ha); else status = QLA_ERROR; } /* Flush any pending ddb changed AENs */ qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS); /* Re-initialize firmware. If successful, function returns * with ISP interrupts enabled */ if (status == QLA_SUCCESS) { DEBUG2(printk("scsi%ld: %s - Initializing adapter..\n", ha->host_no, __func__)); /* If successful, AF_ONLINE flag set in * qla4xxx_initialize_adapter */ status = qla4xxx_initialize_adapter(ha, renew_ddb_list); } /* Failed adapter initialization? * Retry reset_ha only if invoked via DPC (DPC_RESET_HA) */ if ((test_bit(AF_ONLINE, &ha->flags) == 0) && (test_bit(DPC_RESET_HA, &ha->dpc_flags))) { /* Adapter initialization failed, see if we can retry * resetting the ha */ if (!test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags)) { ha->retry_reset_ha_cnt = MAX_RESET_HA_RETRIES; DEBUG2(printk("scsi%ld: recover adapter - retrying " "(%d) more times\n", ha->host_no, ha->retry_reset_ha_cnt)); set_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags); status = QLA_ERROR; } else { if (ha->retry_reset_ha_cnt > 0) { /* Schedule another Reset HA--DPC will retry */ ha->retry_reset_ha_cnt--; DEBUG2(printk("scsi%ld: recover adapter - " "retry remaining %d\n", ha->host_no, ha->retry_reset_ha_cnt)); status = QLA_ERROR; } if (ha->retry_reset_ha_cnt == 0) { /* Recover adapter retries have been exhausted. * Adapter DEAD */ DEBUG2(printk("scsi%ld: recover adapter " "failed - board disabled\n", ha->host_no)); qla4xxx_flush_active_srbs(ha); clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags); clear_bit(DPC_RESET_HA, &ha->dpc_flags); clear_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags); status = QLA_ERROR; } } } else { clear_bit(DPC_RESET_HA, &ha->dpc_flags); clear_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags); clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags); } ha->adapter_error_count++; if (status == QLA_SUCCESS) qla4xxx_enable_intrs(ha); DEBUG2(printk("scsi%ld: recover adapter .. DONE\n", ha->host_no)); return status;}/** * qla4xxx_do_dpc - dpc routine * @data: in our case pointer to adapter structure * * This routine is a task that is schedule by the interrupt handler * to perform the background processing for interrupts. We put it * on a task queue that is consumed whenever the scheduler runs; that's * so you can do anything (i.e. put the process to sleep etc). In fact, * the mid-level tries to sleep when it reaches the driver threshold * "host->can_queue". This can cause a panic if we were in our interrupt code. **/static void qla4xxx_do_dpc(struct work_struct *work){ struct scsi_qla_host *ha = container_of(work, struct scsi_qla_host, dpc_work); struct ddb_entry *ddb_entry, *dtemp; int status = QLA_ERROR; DEBUG2(printk("scsi%ld: %s: DPC handler waking up." "flags = 0x%08lx, dpc_flags = 0x%08lx ctrl_stat = 0x%08x\n", ha->host_no, __func__, ha->flags, ha->dpc_flags, readw(&ha->reg->ctrl_status))); /* Initialization not yet finished. Don't do anything yet. */ if (!test_bit(AF_INIT_DONE, &ha->flags)) return; if (adapter_up(ha) || test_bit(DPC_RESET_HA, &ha->dpc_flags) || test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) || test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags)) { if (test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) || test_bit(DPC_RESET_HA, &ha->dpc_flags)) qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST); if (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) { uint8_t wait_time = RESET_INTR_TOV; while ((readw(&ha->reg->ctrl_status) & (CSR_SOFT_RESET | CSR_FORCE_SOFT_RESET)) != 0) { if (--wait_time == 0) break; msleep(1000); } if (wait_time == 0) DEBUG2(printk("scsi%ld: %s: SR|FSR " "bit not cleared-- resetting\n", ha->host_no, __func__)); qla4xxx_flush_active_srbs(ha); if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS) { qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS); status = qla4xxx_initialize_adapter(ha, PRESERVE_DDB_LIST); } clear_bit(DPC_RESET_HA_INTR, &ha->dpc_flags); if (status == QLA_SUCCESS) qla4xxx_enable_intrs(ha); } } /* ---- process AEN? --- */ if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) qla4xxx_process_aen(ha, PROCESS_ALL_AENS); /* ---- Get DHCP IP Address? --- */ if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags)) qla4xxx_get_dhcp_ip_address(ha); /* ---- relogin device? --- */ if (adapter_up(ha) && test_and_clear_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags)) { list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) { if (test_and_clear_bit(DF_RELOGIN, &ddb_entry->flags) && atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) qla4xxx_relogin_device(ha, ddb_entry); /* * If mbx cmd times out there is no point * in continuing further. * With large no of targets this can hang * the system. */ if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) { printk(KERN_WARNING "scsi%ld: %s: " "need to reset hba\n", ha->host_no, __func__); break; } } }}/** * qla4xxx_free_adapter - release the adapter * @ha: pointer to adapter structure **/static void qla4xxx_free_adapter(struct scsi_qla_host *ha){ if (test_bit(AF_INTERRUPTS_ON, &ha->flags)) { /* Turn-off interrupts on the card. */ qla4xxx_disable_intrs(ha); } /* Kill the kernel thread for this host */ if (ha->dpc_thread) destroy_workqueue(ha->dpc_thread); /* Issue Soft Reset to put firmware in unknown state */ if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS) qla4xxx_hw_reset(ha); /* Remove timer thread, if present */ if (ha->timer_active) qla4xxx_stop_timer(ha); /* Detach interrupts */ if (test_and_clear_bit(AF_IRQ_ATTACHED, &ha->flags)) free_irq(ha->pdev->irq, ha); /* free extra memory */ qla4xxx_mem_free(ha); pci_disable_device(ha->pdev);}/*** * qla4xxx_iospace_config - maps registers * @ha: pointer to adapter structure * * This routines maps HBA's registers from the pci address space * into the kernel virtual address space for memory mapped i/o. **/static int qla4xxx_iospace_config(struct scsi_qla_host *ha){ unsigned long pio, pio_len, pio_flags; unsigned long mmio, mmio_len, mmio_flags; pio = pci_resource_start(ha->pdev, 0); pio_len = pci_resource_len(ha->pdev, 0); pio_flags = pci_resource_flags(ha->pdev, 0); if (pio_flags & IORESOURCE_IO) { if (pio_len < MIN_IOBASE_LEN) { dev_warn(&ha->pdev->dev, "Invalid PCI I/O region size\n"); pio = 0; } } else { dev_warn(&ha->pdev->dev, "region #0 not a PIO resource\n"); pio = 0; } /* Use MMIO operations for all accesses. */ mmio = pci_resource_start(ha->pdev, 1); mmio_len = pci_resource_len(ha->pdev, 1); mmio_flags = pci_resource_flags(ha->pdev, 1); if (!(mmio_flags & IORESOURCE_MEM)) { dev_err(&ha->pdev->dev, "region #0 not an MMIO resource, aborting\n"); goto iospace_error_exit; } if (mmio_len < MIN_IOBASE_LEN) { dev_err(&ha->pdev->dev, "Invalid PCI mem region size, aborting\n"); goto iospace_error_exit; } if (pci_request_regions(ha->pdev, DRIVER_NAME)) { dev_warn(&ha->pdev->dev, "Failed to reserve PIO/MMIO regions\n"); goto iospace_error_exit; } ha->pio_address = pio; ha->pio_length = pio_len; ha->reg = ioremap(mmio, MIN_IOBASE_LEN); if (!ha->reg) { dev_err(&ha->pdev->dev, "cannot remap MMIO, aborting\n"); goto iospace_error_exit; } return 0;iospace_error_exit: return -ENOMEM;}/** * qla4xxx_probe_adapter - callback function to probe HBA * @pdev: pointer to pci_dev structure * @pci_device_id: pointer to pci_device entry * * This routine will probe for Qlogic 4xxx iSCSI host adapters. * It returns zero if successful. It also initializes all data necessary for * the driver. **/static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, const struct pci_device_id *ent){ 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]; if (pci_enable_device(pdev)) return -1; host = scsi_host_alloc(&qla4xxx_driver_template, sizeof(*ha)); if (host == NULL) { printk(KERN_WARNING "qla4xxx: Couldn't allocate host from scsi layer!\n"); goto probe_disable_device; } /* Clear our data area */ ha = (struct scsi_qla_host *) host->hostdata; memset(ha, 0, sizeof(*ha)); /* Save the information from PCI BIOS. */ ha->pdev = pdev; ha->host = host; ha->host_no = host->host_no; /* Configure PCI I/O space. */ ret = qla4xxx_iospace_config(ha); if (ret) goto probe_failed; dev_info(&ha->pdev->dev, "Found an ISP%04x, irq %d, iobase 0x%p\n", pdev->device, pdev->irq, ha->reg); qla4xxx_config_dma_addressing(ha); /* Initialize lists and spinlocks. */ INIT_LIST_HEAD(&ha->ddb_list); INIT_LIST_HEAD(&ha->free_srb_q); mutex_init(&ha->mbox_sem); spin_lock_init(&ha->hardware_lock); /* Allocate dma buffers */ if (qla4xxx_mem_alloc(ha)) { dev_warn(&ha->pdev->dev, "[ERROR] Failed to allocate memory for adapter\n"); ret = -ENOMEM; goto probe_failed; } /* * Initialize the Host adapter request/response queues and * firmware * NOTE: interrupts enabled upon successful completion */ status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST); while (status == QLA_ERROR && init_retry_count++ < MAX_INIT_RETRIES) { DEBUG2(printk("scsi: %s: retrying adapter initialization " "(%d)\n", __func__, init_retry_count)); qla4xxx_soft_reset(ha); status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST); } if (status == QLA_ERROR) { dev_warn(&ha->pdev->dev, "Failed to initialize adapter\n"); ret = -ENODEV; goto probe_failed; } host->cmd_per_lun = 3; host->max_channel = 0; host->max_lun = MAX_LUNS - 1; host->max_id = MAX_TARGETS; host->max_cmd_len = IOCB_MAX_CDB_LEN; host->can_queue = MAX_SRBS ; host->transportt = qla4xxx_scsi_transport; ret = scsi_init_shared_tag_map(host, MAX_SRBS); if (ret) { dev_warn(&ha->pdev->dev, "scsi_init_shared_tag_map failed\n"); goto probe_failed; } /* Startup the kernel thread for this host adapter. */ DEBUG2(printk("scsi: %s: Starting kernel thread for " "qla4xxx_dpc\n", __func__)); sprintf(buf, "qla4xxx_%lu_dpc", ha->host_no); ha->dpc_thread = create_singlethread_workqueue(buf); if (!ha->dpc_thread) { dev_warn(&ha->pdev->dev, "Unable to start DPC thread!\n");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?