/* * 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; }