📄 megaraid_mm.c
字号:
/* * While attaching the dma buffer, if we didn't get the * required buffer from the pool, we would have allocated * it at the run time and set the free_buf flag. We must * free that buffer. Otherwise, just mark that the buffer is * not in use */ if (kioc->free_buf == 1) pci_pool_free(pool->handle, kioc->buf_vaddr, kioc->buf_paddr); else pool->in_use = 0; spin_unlock_irqrestore(&pool->lock, flags); } /* Return the kioc to the free pool */ spin_lock_irqsave(&adp->kioc_pool_lock, flags); list_add(&kioc->list, &adp->kioc_pool); spin_unlock_irqrestore(&adp->kioc_pool_lock, flags); /* increment the free kioc count */ up(&adp->kioc_semaphore); return;}/** * lld_ioctl - Routine to issue ioctl to low level drvr * * @adp : The adapter handle * @kioc : The ioctl packet with kernel addresses */static intlld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc){ int rval; struct timer_list timer; struct timer_list *tp = NULL; kioc->status = -ENODATA; rval = adp->issue_uioc(adp->drvr_data, kioc, IOCTL_ISSUE); if (rval) return rval; /* * Start the timer */ if (adp->timeout > 0) { tp = &timer; init_timer(tp); tp->function = lld_timedout; tp->data = (unsigned long)kioc; tp->expires = jiffies + adp->timeout * HZ; add_timer(tp); } /* * Wait till the low level driver completes the ioctl. After this * call, the ioctl either completed successfully or timedout. */ wait_event(wait_q, (kioc->status != -ENODATA)); if (tp) { del_timer_sync(tp); } /* * If the command had timedout, we mark the controller offline * before returning */ if (kioc->timedout) { adp->quiescent = 0; } return kioc->status;}/** * ioctl_done - callback from the low level driver * * @kioc : completed ioctl packet */static voidioctl_done(uioc_t *kioc){ uint32_t adapno; int iterator; mraid_mmadp_t* adapter; /* * When the kioc returns from driver, make sure it still doesn't * have ENODATA in status. Otherwise, driver will hang on wait_event * forever */ if (kioc->status == -ENODATA) { con_log(CL_ANN, (KERN_WARNING "megaraid cmm: lld didn't change status!\n")); kioc->status = -EINVAL; } /* * Check if this kioc was timedout before. If so, nobody is waiting * on this kioc. We don't have to wake up anybody. Instead, we just * have to free the kioc */ if (kioc->timedout) { iterator = 0; adapter = NULL; adapno = kioc->adapno; con_log(CL_ANN, ( KERN_WARNING "megaraid cmm: completed " "ioctl that was timedout before\n")); list_for_each_entry(adapter, &adapters_list_g, list) { if (iterator++ == adapno) break; } kioc->timedout = 0; if (adapter) { mraid_mm_dealloc_kioc( adapter, kioc ); } } else { wake_up(&wait_q); }}/* * lld_timedout : callback from the expired timer * * @ptr : ioctl packet that timed out */static voidlld_timedout(unsigned long ptr){ uioc_t *kioc = (uioc_t *)ptr; kioc->status = -ETIME; kioc->timedout = 1; con_log(CL_ANN, (KERN_WARNING "megaraid cmm: ioctl timed out\n")); wake_up(&wait_q);}/** * kioc_to_mimd : Converter from new back to old format * * @kioc : Kernel space IOCTL packet (successfully issued) * @mimd : User space MIMD packet */static intkioc_to_mimd(uioc_t *kioc, mimd_t __user *mimd){ mimd_t kmimd; uint8_t opcode; uint8_t subopcode; mbox64_t *mbox64; mraid_passthru_t __user *upthru32; mraid_passthru_t *kpthru32; mcontroller_t cinfo; mraid_hba_info_t *hinfo; if (copy_from_user(&kmimd, mimd, sizeof(mimd_t))) return (-EFAULT); opcode = kmimd.ui.fcs.opcode; subopcode = kmimd.ui.fcs.subopcode; if (opcode == 0x82) { switch (subopcode) { case MEGAIOC_QADAPINFO: hinfo = (mraid_hba_info_t *)(unsigned long) kioc->buf_vaddr; hinfo_to_cinfo(hinfo, &cinfo); if (copy_to_user(kmimd.data, &cinfo, sizeof(cinfo))) return (-EFAULT); return 0; default: return (-EINVAL); } return 0; } mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf; if (kioc->user_pthru) { upthru32 = kioc->user_pthru; kpthru32 = kioc->pthru32; if (copy_to_user(&upthru32->scsistatus, &kpthru32->scsistatus, sizeof(uint8_t))) { return (-EFAULT); } } if (kioc->user_data) { if (copy_to_user(kioc->user_data, kioc->buf_vaddr, kioc->user_data_len)) { return (-EFAULT); } } if (copy_to_user(&mimd->mbox[17], &mbox64->mbox32.status, sizeof(uint8_t))) { return (-EFAULT); } return 0;}/** * hinfo_to_cinfo - Convert new format hba info into old format * * @hinfo : New format, more comprehensive adapter info * @cinfo : Old format adapter info to support mimd_t apps */static voidhinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo){ if (!hinfo || !cinfo) return; cinfo->base = hinfo->baseport; cinfo->irq = hinfo->irq; cinfo->numldrv = hinfo->num_ldrv; cinfo->pcibus = hinfo->pci_bus; cinfo->pcidev = hinfo->pci_slot; cinfo->pcifun = PCI_FUNC(hinfo->pci_dev_fn); cinfo->pciid = hinfo->pci_device_id; cinfo->pcivendor = hinfo->pci_vendor_id; cinfo->pcislot = hinfo->pci_slot; cinfo->uid = hinfo->unique_id;}/* * mraid_mm_register_adp - Registration routine for low level drvrs * * @adp : Adapter objejct */intmraid_mm_register_adp(mraid_mmadp_t *lld_adp){ mraid_mmadp_t *adapter; mbox64_t *mbox_list; uioc_t *kioc; uint32_t rval; int i; if (lld_adp->drvr_type != DRVRTYPE_MBOX) return (-EINVAL); adapter = kmalloc(sizeof(mraid_mmadp_t), GFP_KERNEL); if (!adapter) { rval = -ENOMEM; goto memalloc_error; } memset(adapter, 0, sizeof(mraid_mmadp_t)); adapter->unique_id = lld_adp->unique_id; adapter->drvr_type = lld_adp->drvr_type; adapter->drvr_data = lld_adp->drvr_data; adapter->pdev = lld_adp->pdev; adapter->issue_uioc = lld_adp->issue_uioc; adapter->timeout = lld_adp->timeout; adapter->max_kioc = lld_adp->max_kioc; adapter->quiescent = 1; /* * Allocate single blocks of memory for all required kiocs, * mailboxes and passthru structures. */ adapter->kioc_list = kmalloc(sizeof(uioc_t) * lld_adp->max_kioc, GFP_KERNEL); adapter->mbox_list = kmalloc(sizeof(mbox64_t) * lld_adp->max_kioc, GFP_KERNEL); adapter->pthru_dma_pool = pci_pool_create("megaraid mm pthru pool", adapter->pdev, sizeof(mraid_passthru_t), 16, 0); if (!adapter->kioc_list || !adapter->mbox_list || !adapter->pthru_dma_pool) { con_log(CL_ANN, (KERN_WARNING "megaraid cmm: out of memory, %s %d\n", __FUNCTION__, __LINE__)); rval = (-ENOMEM); goto memalloc_error; } /* * Slice kioc_list and make a kioc_pool with the individiual kiocs */ INIT_LIST_HEAD(&adapter->kioc_pool); spin_lock_init(&adapter->kioc_pool_lock); sema_init(&adapter->kioc_semaphore, lld_adp->max_kioc); mbox_list = (mbox64_t *)adapter->mbox_list; for (i = 0; i < lld_adp->max_kioc; i++) { kioc = adapter->kioc_list + i; kioc->cmdbuf = (uint64_t)(unsigned long)(mbox_list + i); kioc->pthru32 = pci_pool_alloc(adapter->pthru_dma_pool, GFP_KERNEL, &kioc->pthru32_h); if (!kioc->pthru32) { con_log(CL_ANN, (KERN_WARNING "megaraid cmm: out of memory, %s %d\n", __FUNCTION__, __LINE__)); rval = (-ENOMEM); goto pthru_dma_pool_error; } list_add_tail(&kioc->list, &adapter->kioc_pool); } // Setup the dma pools for data buffers if ((rval = mraid_mm_setup_dma_pools(adapter)) != 0) { goto dma_pool_error; } list_add_tail(&adapter->list, &adapters_list_g); adapters_count_g++; return 0;dma_pool_error: /* Do nothing */pthru_dma_pool_error: for (i = 0; i < lld_adp->max_kioc; i++) { kioc = adapter->kioc_list + i; if (kioc->pthru32) { pci_pool_free(adapter->pthru_dma_pool, kioc->pthru32, kioc->pthru32_h); } }memalloc_error: kfree(adapter->kioc_list); kfree(adapter->mbox_list); if (adapter->pthru_dma_pool) pci_pool_destroy(adapter->pthru_dma_pool); kfree(adapter); return rval;}/** * mraid_mm_adapter_app_handle - return the application handle for this adapter * * For the given driver data, locate the adadpter in our global list and * return the corresponding handle, which is also used by applications to * uniquely identify an adapter. * * @param unique_id : adapter unique identifier * * @return adapter handle if found in the list * @return 0 if adapter could not be located, should never happen though */uint32_tmraid_mm_adapter_app_handle(uint32_t unique_id){ mraid_mmadp_t *adapter; mraid_mmadp_t *tmp; int index = 0; list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) { if (adapter->unique_id == unique_id) { return MKADAP(index); } index++; } return 0;}/** * mraid_mm_setup_dma_pools - Set up dma buffer pools per adapter * * @adp : Adapter softstate * * We maintain a pool of dma buffers per each adapter. Each pool has one * buffer. E.g, we may have 5 dma pools - one each for 4k, 8k ... 64k buffers. * We have just one 4k buffer in 4k pool, one 8k buffer in 8k pool etc. We * dont' want to waste too much memory by allocating more buffers per each * pool. */static intmraid_mm_setup_dma_pools(mraid_mmadp_t *adp){ mm_dmapool_t *pool; int bufsize; int i; /* * Create MAX_DMA_POOLS number of pools */ bufsize = MRAID_MM_INIT_BUFF_SIZE; for (i = 0; i < MAX_DMA_POOLS; i++){ pool = &adp->dma_pool_list[i]; pool->buf_size = bufsize; spin_lock_init(&pool->lock); pool->handle = pci_pool_create("megaraid mm data buffer", adp->pdev, bufsize, 16, 0); if (!pool->handle) { goto dma_pool_setup_error; } pool->vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL, &pool->paddr); if (!pool->vaddr) goto dma_pool_setup_error; bufsize = bufsize * 2; } return 0;dma_pool_setup_error: mraid_mm_teardown_dma_pools(adp); return (-ENOMEM);}/* * mraid_mm_unregister_adp - Unregister routine for low level drivers * Assume no outstanding ioctls to llds. * * @unique_id : UID of the adpater */intmraid_mm_unregister_adp(uint32_t unique_id){ mraid_mmadp_t *adapter; mraid_mmadp_t *tmp; list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) { if (adapter->unique_id == unique_id) { adapters_count_g--; list_del_init(&adapter->list); mraid_mm_free_adp_resources(adapter); kfree(adapter); con_log(CL_ANN, ( "megaraid cmm: Unregistered one adapter:%#x\n", unique_id)); return 0; } } return (-ENODEV);}/** * mraid_mm_free_adp_resources - Free adapter softstate * * @adp : Adapter softstate */static voidmraid_mm_free_adp_resources(mraid_mmadp_t *adp){ uioc_t *kioc; int i; mraid_mm_teardown_dma_pools(adp); for (i = 0; i < adp->max_kioc; i++) { kioc = adp->kioc_list + i; pci_pool_free(adp->pthru_dma_pool, kioc->pthru32, kioc->pthru32_h); } kfree(adp->kioc_list); kfree(adp->mbox_list); pci_pool_destroy(adp->pthru_dma_pool); return;}/** * mraid_mm_teardown_dma_pools - Free all per adapter dma buffers * * @adp : Adapter softstate */static voidmraid_mm_teardown_dma_pools(mraid_mmadp_t *adp){ int i; mm_dmapool_t *pool; for (i = 0; i < MAX_DMA_POOLS; i++) { pool = &adp->dma_pool_list[i]; if (pool->handle) { if (pool->vaddr) pci_pool_free(pool->handle, pool->vaddr, pool->paddr); pci_pool_destroy(pool->handle); pool->handle = NULL; } } return;}/** * mraid_mm_init : Module entry point */static int __initmraid_mm_init(void){ // Announce the driver version con_log(CL_ANN, (KERN_INFO "megaraid cmm: %s %s\n", LSI_COMMON_MOD_VERSION, LSI_COMMON_MOD_EXT_VERSION)); majorno = register_chrdev(0, "megadev", &lsi_fops); if (majorno < 0) { con_log(CL_ANN, ("megaraid cmm: cannot get major\n")); return majorno; } init_waitqueue_head(&wait_q); INIT_LIST_HEAD(&adapters_list_g); return 0;}/** * mraid_mm_compat_ioctl : 32bit to 64bit ioctl conversion routine */#ifdef CONFIG_COMPATstatic longmraid_mm_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg){ int err; err = mraid_mm_ioctl(NULL, filep, cmd, arg); return err;}#endif/** * mraid_mm_exit : Module exit point */static void __exitmraid_mm_exit(void){ con_log(CL_DLEVEL1 , ("exiting common mod\n")); unregister_chrdev(majorno, "megadev");}module_init(mraid_mm_init);module_exit(mraid_mm_exit);/* vi: set ts=8 sw=8 tw=78: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -