| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright 2014 QLogic Corporation |
| * The contents of this file are subject to the terms of the |
| * QLogic End User License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the License at |
| * http://www.qlogic.com/Resources/Documents/DriverDownloadHelp/ |
| * QLogic_End_User_Software_License.txt |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| */ |
| |
| #include "bnxe.h" |
| |
| |
| typedef struct _BnxeWorkItem |
| { |
| s_list_entry_t link; |
| void * pWorkData; |
| u32_t workDataLen; |
| u32_t delayMs; |
| void (*pWorkCbkCopy)(um_device_t *, void *, u32_t); |
| void (*pWorkCbkNoCopy)(um_device_t *, void *); |
| void (*pWorkCbkGeneric)(um_device_t *); |
| } BnxeWorkItem; |
| |
| |
| static void BnxeWorkQueueInstanceWaitAndDestroy(BnxeWorkQueueInstance * pWorkq) |
| { |
| if (pWorkq->pTaskq) |
| { |
| ddi_taskq_wait(pWorkq->pTaskq); |
| ddi_taskq_destroy(pWorkq->pTaskq); |
| mutex_destroy(&pWorkq->workQueueMutex); |
| } |
| |
| memset(pWorkq, 0, sizeof(BnxeWorkQueueInstance)); |
| } |
| |
| |
| boolean_t BnxeWorkQueueInit(um_device_t * pUM) |
| { |
| pUM->workqs.instq.pUM = pUM; |
| |
| strcpy(pUM->workqs.instq.taskqName, pUM->devName); |
| strcat(pUM->workqs.instq.taskqName, "_inst_q"); |
| |
| mutex_init(&pUM->workqs.instq.workQueueMutex, NULL, |
| MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority)); |
| |
| if ((pUM->workqs.instq.pTaskq = |
| ddi_taskq_create(pUM->pDev, |
| pUM->workqs.instq.taskqName, |
| 1, |
| TASKQ_DEFAULTPRI, |
| 0)) == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to create the workqs instq"); |
| return B_FALSE; |
| } |
| |
| pUM->workqs.instq.pUM = pUM; |
| |
| strcpy(pUM->workqs.delayq.taskqName, pUM->devName); |
| strcat(pUM->workqs.delayq.taskqName, "_delay_q"); |
| |
| mutex_init(&pUM->workqs.delayq.workQueueMutex, NULL, |
| MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority)); |
| |
| if ((pUM->workqs.delayq.pTaskq = |
| ddi_taskq_create(pUM->pDev, |
| pUM->workqs.delayq.taskqName, |
| 16, /* XXX Is this enough? */ |
| TASKQ_DEFAULTPRI, |
| 0)) == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to create the workqs delayq"); |
| BnxeWorkQueueInstanceWaitAndDestroy(&pUM->workqs.instq); |
| return B_FALSE; |
| } |
| |
| pUM->workqs.delayq.pUM = pUM; |
| |
| return B_TRUE; |
| } |
| |
| |
| void BnxeWorkQueueWaitAndDestroy(um_device_t * pUM) |
| { |
| BnxeWorkQueueInstanceWaitAndDestroy(&pUM->workqs.instq); |
| BnxeWorkQueueInstanceWaitAndDestroy(&pUM->workqs.delayq); |
| } |
| |
| |
| static void BnxeWorkQueueDispatch(void * pArg) |
| { |
| BnxeWorkQueueInstance * pWorkq = (BnxeWorkQueueInstance *)pArg; |
| um_device_t * pUM = (um_device_t *)pWorkq->pUM; |
| BnxeWorkItem * pWorkItem; |
| |
| mutex_enter(&pWorkq->workQueueMutex); |
| pWorkItem = (BnxeWorkItem *)s_list_pop_head(&pWorkq->workQueue); |
| mutex_exit(&pWorkq->workQueueMutex); |
| |
| if (pWorkItem == NULL) |
| { |
| BnxeLogWarn(pUM, "Work item is NULL!"); |
| pWorkq->workItemError++; |
| return; |
| } |
| |
| if ((pWorkItem->pWorkCbkCopy == NULL) && |
| (pWorkItem->pWorkCbkNoCopy == NULL) && |
| (pWorkItem->pWorkCbkGeneric == NULL)) |
| { |
| BnxeLogWarn(pUM, "Work item callback is NULL!"); |
| pWorkq->workItemError++; |
| goto BnxeWorkQueueDispatch_done; |
| } |
| |
| if (pWorkItem->delayMs > 0) |
| { |
| /* this only occurs when processing the delayq */ |
| drv_usecwait(pWorkItem->delayMs * 1000); |
| } |
| |
| if (pWorkItem->pWorkCbkCopy) |
| { |
| pWorkItem->pWorkCbkCopy(pUM, |
| pWorkItem->pWorkData, |
| pWorkItem->workDataLen); |
| } |
| else if (pWorkItem->pWorkCbkNoCopy) |
| { |
| pWorkItem->pWorkCbkNoCopy(pUM, |
| pWorkItem->pWorkData); |
| } |
| else /* (pWorkItem->pWorkCbkGeneric) */ |
| { |
| pWorkItem->pWorkCbkGeneric(pUM); |
| } |
| |
| pWorkq->workItemComplete++; |
| |
| BnxeWorkQueueDispatch_done: |
| |
| kmem_free(pWorkItem, (sizeof(BnxeWorkItem) + pWorkItem->workDataLen)); |
| } |
| |
| |
| static void BnxeWorkQueueTrigger(um_device_t * pUM, |
| BnxeWorkQueueInstance * pWorkq) |
| { |
| if (pUM->chipStarted) |
| { |
| ddi_taskq_dispatch(pWorkq->pTaskq, |
| BnxeWorkQueueDispatch, |
| (void *)pWorkq, |
| DDI_NOSLEEP); |
| } |
| else |
| { |
| BnxeLogInfo(pUM, "Delaying WorkQ item since chip not yet started."); |
| } |
| } |
| |
| |
| void BnxeWorkQueueStartPending(um_device_t * pUM) |
| { |
| u32_t cnt; |
| |
| if (!pUM->chipStarted) |
| { |
| BnxeLogWarn(pUM, "Triggering WorkQs and chip not started!"); |
| return; |
| } |
| |
| mutex_enter(&pUM->workqs.instq.workQueueMutex); |
| cnt = s_list_entry_cnt(&pUM->workqs.instq.workQueue); |
| mutex_exit(&pUM->workqs.instq.workQueueMutex); |
| |
| if (cnt) |
| { |
| BnxeWorkQueueTrigger(pUM, &pUM->workqs.instq); |
| } |
| |
| mutex_enter(&pUM->workqs.delayq.workQueueMutex); |
| cnt = s_list_entry_cnt(&pUM->workqs.delayq.workQueue); |
| mutex_exit(&pUM->workqs.delayq.workQueueMutex); |
| |
| if (cnt) |
| { |
| BnxeWorkQueueTrigger(pUM, &pUM->workqs.delayq); |
| } |
| } |
| |
| |
| boolean_t BnxeWorkQueueAdd(um_device_t * pUM, |
| void (*pWorkCbkCopy)(um_device_t *, void *, u32_t), |
| void * pWorkData, |
| u32_t workDataLen) |
| { |
| BnxeWorkItem * pWorkItem; |
| |
| if ((pWorkItem = kmem_zalloc((sizeof(BnxeWorkItem) + workDataLen), |
| KM_NOSLEEP)) == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate memory for work item!"); |
| return B_FALSE; |
| } |
| |
| pWorkItem->pWorkData = (pWorkItem + 1); |
| pWorkItem->workDataLen = workDataLen; |
| pWorkItem->pWorkCbkCopy = pWorkCbkCopy; |
| pWorkItem->pWorkCbkNoCopy = NULL; |
| pWorkItem->pWorkCbkGeneric = NULL; |
| pWorkItem->delayMs = 0; |
| |
| memcpy(pWorkItem->pWorkData, pWorkData, workDataLen); |
| |
| mutex_enter(&pUM->workqs.instq.workQueueMutex); |
| |
| s_list_push_tail(&pUM->workqs.instq.workQueue, &pWorkItem->link); |
| pUM->workqs.instq.workItemQueued++; |
| if (s_list_entry_cnt(&pUM->workqs.instq.workQueue) > |
| pUM->workqs.instq.highWater) |
| { |
| pUM->workqs.instq.highWater = |
| s_list_entry_cnt(&pUM->workqs.instq.workQueue); |
| } |
| |
| mutex_exit(&pUM->workqs.instq.workQueueMutex); |
| |
| BnxeWorkQueueTrigger(pUM, &pUM->workqs.instq); |
| |
| return B_TRUE; |
| } |
| |
| |
| boolean_t BnxeWorkQueueAddNoCopy(um_device_t * pUM, |
| void (*pWorkCbkNoCopy)(um_device_t *, void *), |
| void * pWorkData) |
| { |
| BnxeWorkItem * pWorkItem; |
| |
| if ((pWorkItem = kmem_zalloc(sizeof(BnxeWorkItem), KM_NOSLEEP)) == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate memory for work item!"); |
| return B_FALSE; |
| } |
| |
| pWorkItem->pWorkData = pWorkData; |
| pWorkItem->workDataLen = 0; |
| pWorkItem->pWorkCbkCopy = NULL; |
| pWorkItem->pWorkCbkNoCopy = pWorkCbkNoCopy; |
| pWorkItem->pWorkCbkGeneric = NULL; |
| pWorkItem->delayMs = 0; |
| |
| mutex_enter(&pUM->workqs.instq.workQueueMutex); |
| |
| s_list_push_tail(&pUM->workqs.instq.workQueue, &pWorkItem->link); |
| pUM->workqs.instq.workItemQueued++; |
| if (s_list_entry_cnt(&pUM->workqs.instq.workQueue) > |
| pUM->workqs.instq.highWater) |
| { |
| pUM->workqs.instq.highWater = |
| s_list_entry_cnt(&pUM->workqs.instq.workQueue); |
| } |
| |
| mutex_exit(&pUM->workqs.instq.workQueueMutex); |
| |
| BnxeWorkQueueTrigger(pUM, &pUM->workqs.instq); |
| |
| return B_TRUE; |
| } |
| |
| |
| boolean_t BnxeWorkQueueAddGeneric(um_device_t * pUM, |
| void (*pWorkCbkGeneric)(um_device_t *)) |
| { |
| BnxeWorkItem * pWorkItem; |
| |
| if ((pWorkItem = kmem_zalloc(sizeof(BnxeWorkItem), KM_NOSLEEP)) == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate memory for work item!"); |
| return B_FALSE; |
| } |
| |
| pWorkItem->pWorkData = NULL; |
| pWorkItem->workDataLen = 0; |
| pWorkItem->pWorkCbkCopy = NULL; |
| pWorkItem->pWorkCbkNoCopy = NULL; |
| pWorkItem->pWorkCbkGeneric = pWorkCbkGeneric; |
| pWorkItem->delayMs = 0; |
| |
| mutex_enter(&pUM->workqs.instq.workQueueMutex); |
| |
| s_list_push_tail(&pUM->workqs.instq.workQueue, &pWorkItem->link); |
| pUM->workqs.instq.workItemQueued++; |
| if (s_list_entry_cnt(&pUM->workqs.instq.workQueue) > |
| pUM->workqs.instq.highWater) |
| { |
| pUM->workqs.instq.highWater = |
| s_list_entry_cnt(&pUM->workqs.instq.workQueue); |
| } |
| |
| mutex_exit(&pUM->workqs.instq.workQueueMutex); |
| |
| BnxeWorkQueueTrigger(pUM, &pUM->workqs.instq); |
| |
| return B_TRUE; |
| } |
| |
| |
| boolean_t BnxeWorkQueueAddDelay(um_device_t * pUM, |
| void (*pWorkCbkCopy)(um_device_t *, void *, u32_t), |
| void * pWorkData, |
| u32_t workDataLen, |
| u32_t delayMs) |
| { |
| BnxeWorkItem * pWorkItem; |
| |
| if ((pWorkItem = kmem_zalloc((sizeof(BnxeWorkItem) + workDataLen), |
| KM_NOSLEEP)) == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate memory for work item!"); |
| return B_FALSE; |
| } |
| |
| pWorkItem->pWorkData = (pWorkItem + 1); |
| pWorkItem->workDataLen = workDataLen; |
| pWorkItem->pWorkCbkCopy = pWorkCbkCopy; |
| pWorkItem->pWorkCbkNoCopy = NULL; |
| pWorkItem->pWorkCbkGeneric = NULL; |
| pWorkItem->delayMs = delayMs; |
| |
| memcpy(pWorkItem->pWorkData, pWorkData, workDataLen); |
| |
| mutex_enter(&pUM->workqs.delayq.workQueueMutex); |
| |
| s_list_push_tail(&pUM->workqs.delayq.workQueue, &pWorkItem->link); |
| pUM->workqs.delayq.workItemQueued++; |
| if (s_list_entry_cnt(&pUM->workqs.delayq.workQueue) > |
| pUM->workqs.delayq.highWater) |
| { |
| pUM->workqs.delayq.highWater = |
| s_list_entry_cnt(&pUM->workqs.delayq.workQueue); |
| } |
| |
| mutex_exit(&pUM->workqs.delayq.workQueueMutex); |
| |
| BnxeWorkQueueTrigger(pUM, &pUM->workqs.delayq); |
| |
| return B_TRUE; |
| } |
| |
| |
| boolean_t BnxeWorkQueueAddDelayNoCopy(um_device_t * pUM, |
| void (*pWorkCbkNoCopy)(um_device_t *, void *), |
| void * pWorkData, |
| u32_t delayMs) |
| { |
| BnxeWorkItem * pWorkItem; |
| |
| if ((pWorkItem = kmem_zalloc(sizeof(BnxeWorkItem), KM_NOSLEEP)) == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate memory for work item!"); |
| return B_FALSE; |
| } |
| |
| pWorkItem->pWorkData = pWorkData; |
| pWorkItem->workDataLen = 0; |
| pWorkItem->pWorkCbkCopy = NULL; |
| pWorkItem->pWorkCbkNoCopy = pWorkCbkNoCopy; |
| pWorkItem->pWorkCbkGeneric = NULL; |
| pWorkItem->delayMs = delayMs; |
| |
| mutex_enter(&pUM->workqs.delayq.workQueueMutex); |
| |
| s_list_push_tail(&pUM->workqs.delayq.workQueue, &pWorkItem->link); |
| pUM->workqs.delayq.workItemQueued++; |
| if (s_list_entry_cnt(&pUM->workqs.delayq.workQueue) > |
| pUM->workqs.delayq.highWater) |
| { |
| pUM->workqs.delayq.highWater = |
| s_list_entry_cnt(&pUM->workqs.delayq.workQueue); |
| } |
| |
| mutex_exit(&pUM->workqs.delayq.workQueueMutex); |
| |
| BnxeWorkQueueTrigger(pUM, &pUM->workqs.delayq); |
| |
| return B_TRUE; |
| } |
| |
| |
| boolean_t BnxeWorkQueueAddDelayGeneric(um_device_t * pUM, |
| void (*pWorkCbkGeneric)(um_device_t *), |
| u32_t delayMs) |
| { |
| BnxeWorkItem * pWorkItem; |
| |
| if ((pWorkItem = kmem_zalloc(sizeof(BnxeWorkItem), KM_NOSLEEP)) == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate memory for work item!"); |
| return B_FALSE; |
| } |
| |
| pWorkItem->pWorkData = NULL; |
| pWorkItem->workDataLen = 0; |
| pWorkItem->pWorkCbkCopy = NULL; |
| pWorkItem->pWorkCbkNoCopy = NULL; |
| pWorkItem->pWorkCbkGeneric = pWorkCbkGeneric; |
| pWorkItem->delayMs = delayMs; |
| |
| mutex_enter(&pUM->workqs.delayq.workQueueMutex); |
| |
| s_list_push_tail(&pUM->workqs.delayq.workQueue, &pWorkItem->link); |
| pUM->workqs.delayq.workItemQueued++; |
| if (s_list_entry_cnt(&pUM->workqs.delayq.workQueue) > |
| pUM->workqs.delayq.highWater) |
| { |
| pUM->workqs.delayq.highWater = |
| s_list_entry_cnt(&pUM->workqs.delayq.workQueue); |
| } |
| |
| mutex_exit(&pUM->workqs.delayq.workQueueMutex); |
| |
| BnxeWorkQueueTrigger(pUM, &pUM->workqs.delayq); |
| |
| return B_TRUE; |
| } |
| |