📄 megaraid_sas.c
字号:
goto fail_irq; } megasas_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; down(&megasas_async_queue_mutex); rc = fasync_helper(fd, filep, mode, &megasas_async_queue); up(&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_sge32[i].length, kbuff_arr[i], kern_sge32[i].phys_addr); } megasas_return_cmd(instance, cmd); return error;}static struct megasas_instance *megasas_lookup_instance(u16 host_no){ int i; for (i = 0; i < megasas_mgmt_info.max_index; i++) { if ((megasas_mgmt_info.instance[i]) && (megasas_mgmt_info.instance[i]->host->host_no == host_no)) return megasas_mgmt_info.instance[i]; } return NULL;}static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg){ struct megasas_iocpacket __user *user_ioc = (struct megasas_iocpacket __user *)arg; struct megasas_iocpacket *ioc; struct megasas_instance *instance; int error; ioc = kmalloc(sizeof(*ioc), GFP_KERNEL); if (!ioc) return -ENOMEM; if (copy_from_user(ioc, user_ioc, sizeof(*ioc))) { error = -EFAULT; goto out_kfree_ioc; } instance = megasas_lookup_instance(ioc->host_no); if (!instance) { error = -ENODEV; goto out_kfree_ioc; } /* * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds */ if (down_interruptible(&instance->ioctl_sem)) { error = -ERESTARTSYS; goto out_kfree_ioc; } error = megasas_mgmt_fw_ioctl(instance, user_ioc, ioc); up(&instance->ioctl_sem); out_kfree_ioc: kfree(ioc); return error;}static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg){ struct megasas_instance *instance; struct megasas_aen aen; int error; if (file->private_data != file) { printk(KERN_DEBUG "megasas: fasync_helper was not " "called first\n"); return -EINVAL; } if (copy_from_user(&aen, (void __user *)arg, sizeof(aen))) return -EFAULT; instance = megasas_lookup_instance(aen.host_no); if (!instance) return -ENODEV; down(&instance->aen_mutex); error = megasas_register_aen(instance, aen.seq_num, aen.class_locale_word); up(&instance->aen_mutex); return error;}/** * megasas_mgmt_ioctl - char node ioctl entry point */static longmegasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ switch (cmd) { case MEGASAS_IOC_FIRMWARE: return megasas_mgmt_ioctl_fw(file, arg); case MEGASAS_IOC_GET_AEN: return megasas_mgmt_ioctl_aen(file, arg); } return -ENOTTY;}#ifdef CONFIG_COMPATstatic int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg){ struct compat_megasas_iocpacket __user *cioc = (struct compat_megasas_iocpacket __user *)arg; struct megasas_iocpacket __user *ioc = compat_alloc_user_space(sizeof(struct megasas_iocpacket)); int i; int error = 0; clear_user(ioc, sizeof(*ioc)); if (copy_in_user(&ioc->host_no, &cioc->host_no, sizeof(u16)) || copy_in_user(&ioc->sgl_off, &cioc->sgl_off, sizeof(u32)) || copy_in_user(&ioc->sense_off, &cioc->sense_off, sizeof(u32)) || copy_in_user(&ioc->sense_len, &cioc->sense_len, sizeof(u32)) || copy_in_user(ioc->frame.raw, cioc->frame.raw, 128) || copy_in_user(&ioc->sge_count, &cioc->sge_count,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -