megaraid_sas.c

来自「linux2.6.16版本」· C语言 代码 · 共 2,625 行 · 第 1/5 页

C
2,625
字号
 * megasas_probe_one -	PCI hotplug entry point * @pdev:		PCI device structure * @id:			PCI ids of supported hotplugged adapter	 */static int __devinitmegasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id){	int rval;	struct Scsi_Host *host;	struct megasas_instance *instance;	/*	 * Announce PCI information	 */	printk(KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",	       pdev->vendor, pdev->device, pdev->subsystem_vendor,	       pdev->subsystem_device);	printk("bus %d:slot %d:func %d\n",	       pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));	/*	 * PCI prepping: enable device set bus mastering and dma mask	 */	rval = pci_enable_device(pdev);	if (rval) {		return rval;	}	pci_set_master(pdev);	/*	 * All our contollers are capable of performing 64-bit DMA	 */	if (IS_DMA64) {		if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) != 0) {			if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0)				goto fail_set_dma_mask;		}	} else {		if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0)			goto fail_set_dma_mask;	}	host = scsi_host_alloc(&megasas_template,			       sizeof(struct megasas_instance));	if (!host) {		printk(KERN_DEBUG "megasas: scsi_host_alloc failed\n");		goto fail_alloc_instance;	}	instance = (struct megasas_instance *)host->hostdata;	memset(instance, 0, sizeof(*instance));	instance->producer = pci_alloc_consistent(pdev, sizeof(u32),						  &instance->producer_h);	instance->consumer = pci_alloc_consistent(pdev, sizeof(u32),						  &instance->consumer_h);	if (!instance->producer || !instance->consumer) {		printk(KERN_DEBUG "megasas: Failed to allocate memory for "		       "producer, consumer\n");		goto fail_alloc_dma_buf;	}	*instance->producer = 0;	*instance->consumer = 0;	instance->evt_detail = pci_alloc_consistent(pdev,						    sizeof(struct							   megasas_evt_detail),						    &instance->evt_detail_h);	if (!instance->evt_detail) {		printk(KERN_DEBUG "megasas: Failed to allocate memory for "		       "event detail structure\n");		goto fail_alloc_dma_buf;	}	/*	 * Initialize locks and queues	 */	INIT_LIST_HEAD(&instance->cmd_pool);	init_waitqueue_head(&instance->int_cmd_wait_q);	init_waitqueue_head(&instance->abort_cmd_wait_q);	spin_lock_init(&instance->cmd_pool_lock);	spin_lock_init(&instance->instance_lock);	sema_init(&instance->aen_mutex, 1);	sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);	/*	 * Initialize PCI related and misc parameters	 */	instance->pdev = pdev;	instance->host = host;	instance->unique_id = pdev->bus->number << 8 | pdev->devfn;	instance->init_id = MEGASAS_DEFAULT_INIT_ID;	/*	 * Initialize MFI Firmware	 */	if (megasas_init_mfi(instance))		goto fail_init_mfi;	/*	 * Register IRQ	 */	if (request_irq(pdev->irq, megasas_isr, SA_SHIRQ, "megasas", instance)) {		printk(KERN_DEBUG "megasas: Failed to register IRQ\n");		goto fail_irq;	}	instance->instancet->enable_intr(instance->reg_set);	/*	 * Store instance in PCI softstate	 */	pci_set_drvdata(pdev, instance);	/*	 * Add this controller to megasas_mgmt_info structure so that it	 * can be exported to management applications	 */	megasas_mgmt_info.count++;	megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = instance;	megasas_mgmt_info.max_index++;	/*	 * Initiate AEN (Asynchronous Event Notification)	 */	if (megasas_start_aen(instance)) {		printk(KERN_DEBUG "megasas: start aen failed\n");		goto fail_start_aen;	}	/*	 * Register with SCSI mid-layer	 */	if (megasas_io_attach(instance))		goto fail_io_attach;	return 0;      fail_start_aen:      fail_io_attach:	megasas_mgmt_info.count--;	megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL;	megasas_mgmt_info.max_index--;	pci_set_drvdata(pdev, NULL);	megasas_disable_intr(instance->reg_set);	free_irq(instance->pdev->irq, instance);	megasas_release_mfi(instance);      fail_irq:      fail_init_mfi:      fail_alloc_dma_buf:	if (instance->evt_detail)		pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),				    instance->evt_detail,				    instance->evt_detail_h);	if (instance->producer)		pci_free_consistent(pdev, sizeof(u32), instance->producer,				    instance->producer_h);	if (instance->consumer)		pci_free_consistent(pdev, sizeof(u32), instance->consumer,				    instance->consumer_h);	scsi_host_put(host);      fail_alloc_instance:      fail_set_dma_mask:	pci_disable_device(pdev);	return -ENODEV;}/** * megasas_flush_cache -	Requests FW to flush all its caches * @instance:			Adapter soft state */static void megasas_flush_cache(struct megasas_instance *instance){	struct megasas_cmd *cmd;	struct megasas_dcmd_frame *dcmd;	cmd = megasas_get_cmd(instance);	if (!cmd)		return;	dcmd = &cmd->frame->dcmd;	memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);	dcmd->cmd = MFI_CMD_DCMD;	dcmd->cmd_status = 0x0;	dcmd->sge_count = 0;	dcmd->flags = MFI_FRAME_DIR_NONE;	dcmd->timeout = 0;	dcmd->data_xfer_len = 0;	dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH;	dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE;	megasas_issue_blocked_cmd(instance, cmd);	megasas_return_cmd(instance, cmd);	return;}/** * megasas_shutdown_controller -	Instructs FW to shutdown the controller * @instance:				Adapter soft state */static void megasas_shutdown_controller(struct megasas_instance *instance){	struct megasas_cmd *cmd;	struct megasas_dcmd_frame *dcmd;	cmd = megasas_get_cmd(instance);	if (!cmd)		return;	if (instance->aen_cmd)		megasas_issue_blocked_abort_cmd(instance, instance->aen_cmd);	dcmd = &cmd->frame->dcmd;	memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);	dcmd->cmd = MFI_CMD_DCMD;	dcmd->cmd_status = 0x0;	dcmd->sge_count = 0;	dcmd->flags = MFI_FRAME_DIR_NONE;	dcmd->timeout = 0;	dcmd->data_xfer_len = 0;	dcmd->opcode = MR_DCMD_CTRL_SHUTDOWN;	megasas_issue_blocked_cmd(instance, cmd);	megasas_return_cmd(instance, cmd);	return;}/** * megasas_detach_one -	PCI hot"un"plug entry point * @pdev:		PCI device structure */static void megasas_detach_one(struct pci_dev *pdev){	int i;	struct Scsi_Host *host;	struct megasas_instance *instance;	instance = pci_get_drvdata(pdev);	host = instance->host;	scsi_remove_host(instance->host);	megasas_flush_cache(instance);	megasas_shutdown_controller(instance);	/*	 * Take the instance off the instance array. Note that we will not	 * decrement the max_index. We let this array be sparse array	 */	for (i = 0; i < megasas_mgmt_info.max_index; i++) {		if (megasas_mgmt_info.instance[i] == instance) {			megasas_mgmt_info.count--;			megasas_mgmt_info.instance[i] = NULL;			break;		}	}	pci_set_drvdata(instance->pdev, NULL);	megasas_disable_intr(instance->reg_set);	free_irq(instance->pdev->irq, instance);	megasas_release_mfi(instance);	pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),			    instance->evt_detail, instance->evt_detail_h);	pci_free_consistent(pdev, sizeof(u32), instance->producer,			    instance->producer_h);	pci_free_consistent(pdev, sizeof(u32), instance->consumer,			    instance->consumer_h);	scsi_host_put(host);	pci_set_drvdata(pdev, NULL);	pci_disable_device(pdev);	return;}/** * megasas_shutdown -	Shutdown entry point * @device:		Generic device structure */static void megasas_shutdown(struct pci_dev *pdev){	struct megasas_instance *instance = pci_get_drvdata(pdev);	megasas_flush_cache(instance);}/** * megasas_mgmt_open -	char node "open" entry point */static int megasas_mgmt_open(struct inode *inode, struct file *filep){	/*	 * Allow only those users with admin rights	 */	if (!capable(CAP_SYS_ADMIN))		return -EACCES;	return 0;}/** * megasas_mgmt_release - char node "release" entry point */static int megasas_mgmt_release(struct inode *inode, struct file *filep){	filep->private_data = NULL;	fasync_helper(-1, filep, 0, &megasas_async_queue);	return 0;}/** * megasas_mgmt_fasync -	Async notifier registration from applications * * This function adds the calling process to a driver global queue. When an * event occurs, SIGIO will be sent to all processes in this queue. */static int megasas_mgmt_fasync(int fd, struct file *filep, int mode){	int rc;	mutex_lock(&megasas_async_queue_mutex);	rc = fasync_helper(fd, filep, mode, &megasas_async_queue);	mutex_unlock(&megasas_async_queue_mutex);	if (rc >= 0) {		/* For sanity check when we get ioctl */		filep->private_data = filep;		return 0;	}	printk(KERN_DEBUG "megasas: fasync_helper failed [%d]\n", rc);	return rc;}/** * megasas_mgmt_fw_ioctl -	Issues management ioctls to FW * @instance:			Adapter soft state * @argp:			User's ioctl packet */static intmegasas_mgmt_fw_ioctl(struct megasas_instance *instance,		      struct megasas_iocpacket __user * user_ioc,		      struct megasas_iocpacket *ioc){	struct megasas_sge32 *kern_sge32;	struct megasas_cmd *cmd;	void *kbuff_arr[MAX_IOCTL_SGE];	dma_addr_t buf_handle = 0;	int error = 0, i;	void *sense = NULL;	dma_addr_t sense_handle;	u32 *sense_ptr;	memset(kbuff_arr, 0, sizeof(kbuff_arr));	if (ioc->sge_count > MAX_IOCTL_SGE) {		printk(KERN_DEBUG "megasas: SGE count [%d] >  max limit [%d]\n",		       ioc->sge_count, MAX_IOCTL_SGE);		return -EINVAL;	}	cmd = megasas_get_cmd(instance);	if (!cmd) {		printk(KERN_DEBUG "megasas: Failed to get a cmd packet\n");		return -ENOMEM;	}	/*	 * User's IOCTL packet has 2 frames (maximum). Copy those two	 * frames into our cmd's frames. cmd->frame's context will get	 * overwritten when we copy from user's frames. So set that value	 * alone separately	 */	memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);	cmd->frame->hdr.context = cmd->index;	/*	 * The management interface between applications and the fw uses	 * MFI frames. E.g, RAID configuration changes, LD property changes	 * etc are accomplishes through different kinds of MFI frames. The	 * driver needs to care only about substituting user buffers with	 * kernel buffers in SGLs. The location of SGL is embedded in the	 * struct iocpacket itself.	 */	kern_sge32 = (struct megasas_sge32 *)	    ((unsigned long)cmd->frame + ioc->sgl_off);	/*	 * For each user buffer, create a mirror buffer and copy in	 */	for (i = 0; i < ioc->sge_count; i++) {		kbuff_arr[i] = pci_alloc_consistent(instance->pdev,						    ioc->sgl[i].iov_len,						    &buf_handle);		if (!kbuff_arr[i]) {			printk(KERN_DEBUG "megasas: Failed to alloc "			       "kernel SGL buffer for IOCTL \n");			error = -ENOMEM;			goto out;		}		/*		 * We don't change the dma_coherent_mask, so		 * pci_alloc_consistent only returns 32bit addresses		 */		kern_sge32[i].phys_addr = (u32) buf_handle;		kern_sge32[i].length = ioc->sgl[i].iov_len;		/*		 * We created a kernel buffer corresponding to the		 * user buffer. Now copy in from the user buffer		 */		if (copy_from_user(kbuff_arr[i], ioc->sgl[i].iov_base,				   (u32) (ioc->sgl[i].iov_len))) {			error = -EFAULT;			goto out;		}	}	if (ioc->sense_len) {		sense = pci_alloc_consistent(instance->pdev, ioc->sense_len,					     &sense_handle);		if (!sense) {			error = -ENOMEM;			goto out;		}		sense_ptr =		    (u32 *) ((unsigned long)cmd->frame + ioc->sense_off);		*sense_ptr = sense_handle;	}	/*	 * Set the sync_cmd flag so that the ISR knows not to complete this	 * cmd to the SCSI mid-layer	 */	cmd->sync_cmd = 1;	megasas_issue_blocked_cmd(instance, cmd);	cmd->sync_cmd = 0;	/*	 * copy out the kernel buffers to user buffers	 */	for (i = 0; i < ioc->sge_count; i++) {		if (copy_to_user(ioc->sgl[i].iov_base, kbuff_arr[i],				 ioc->sgl[i].iov_len)) {			error = -EFAULT;			goto out;		}	}	/*	 * copy out the sense	 */	if (ioc->sense_len) {		/*		 * sense_ptr points to the location that has the user		 * sense buffer address		 */		sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw +				     ioc->sense_off);		if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)),				 sense, ioc->sense_len)) {			error = -EFAULT;			goto out;		}	}	/*	 * copy the status codes returned by the fw	 */	if (copy_to_user(&user_ioc->frame.hdr.cmd_status,			 &cmd->frame->hdr.cmd_status, sizeof(u8))) {		printk(KERN_DEBUG "megasas: Error copying out cmd_status\n");		error = -EFAULT;	}      out:	if (sense) {		pci_free_consistent(instance->pdev, ioc->sense_len,				    sense, sense_handle);	}	for (i = 0; i < ioc->sge_count && kbuff_arr[i]; i++) {		pci_free_consistent(instance->pdev,				    kern

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?