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 + -
显示快捷键?