1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2014 QLogic Corporation
24  * The contents of this file are subject to the terms of the
25  * QLogic End User License (the "License").
26  * You may not use this file except in compliance with the License.
27  *
28  * You can obtain a copy of the License at
29  * http://www.qlogic.com/Resources/Documents/DriverDownloadHelp/
30  * QLogic_End_User_Software_License.txt
31  * See the License for the specific language governing permissions
32  * and limitations under the License.
33  */
34 
35 #include "bnxe.h"
36 
37 
38 typedef struct _BnxeWorkItem
39 {
40     s_list_entry_t link;
41     void *         pWorkData;
42     u32_t          workDataLen;
43     u32_t          delayMs;
44     void (*pWorkCbkCopy)(um_device_t *, void *, u32_t);
45     void (*pWorkCbkNoCopy)(um_device_t *, void *);
46     void (*pWorkCbkGeneric)(um_device_t *);
47 } BnxeWorkItem;
48 
49 
BnxeWorkQueueInstanceWaitAndDestroy(BnxeWorkQueueInstance * pWorkq)50 static void BnxeWorkQueueInstanceWaitAndDestroy(BnxeWorkQueueInstance * pWorkq)
51 {
52     if (pWorkq->pTaskq)
53     {
54         ddi_taskq_wait(pWorkq->pTaskq);
55         ddi_taskq_destroy(pWorkq->pTaskq);
56         mutex_destroy(&pWorkq->workQueueMutex);
57     }
58 
59     memset(pWorkq, 0, sizeof(BnxeWorkQueueInstance));
60 }
61 
62 
BnxeWorkQueueInit(um_device_t * pUM)63 boolean_t BnxeWorkQueueInit(um_device_t * pUM)
64 {
65     pUM->workqs.instq.pUM = pUM;
66 
67     strcpy(pUM->workqs.instq.taskqName, pUM->devName);
68     strcat(pUM->workqs.instq.taskqName, "_inst_q");
69 
70     mutex_init(&pUM->workqs.instq.workQueueMutex, NULL,
71                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
72 
73     if ((pUM->workqs.instq.pTaskq =
74          ddi_taskq_create(pUM->pDev,
75                           pUM->workqs.instq.taskqName,
76                           1,
77                           TASKQ_DEFAULTPRI,
78                           0)) == NULL)
79     {
80         BnxeLogWarn(pUM, "Failed to create the workqs instq");
81         return B_FALSE;
82     }
83 
84     pUM->workqs.instq.pUM = pUM;
85 
86     strcpy(pUM->workqs.delayq.taskqName, pUM->devName);
87     strcat(pUM->workqs.delayq.taskqName, "_delay_q");
88 
89     mutex_init(&pUM->workqs.delayq.workQueueMutex, NULL,
90                MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
91 
92     if ((pUM->workqs.delayq.pTaskq =
93          ddi_taskq_create(pUM->pDev,
94                           pUM->workqs.delayq.taskqName,
95                           16, /* XXX Is this enough? */
96                           TASKQ_DEFAULTPRI,
97                           0)) == NULL)
98     {
99         BnxeLogWarn(pUM, "Failed to create the workqs delayq");
100         BnxeWorkQueueInstanceWaitAndDestroy(&pUM->workqs.instq);
101         return B_FALSE;
102     }
103 
104     pUM->workqs.delayq.pUM = pUM;
105 
106     return B_TRUE;
107 }
108 
109 
BnxeWorkQueueWaitAndDestroy(um_device_t * pUM)110 void BnxeWorkQueueWaitAndDestroy(um_device_t * pUM)
111 {
112     BnxeWorkQueueInstanceWaitAndDestroy(&pUM->workqs.instq);
113     BnxeWorkQueueInstanceWaitAndDestroy(&pUM->workqs.delayq);
114 }
115 
116 
BnxeWorkQueueDispatch(void * pArg)117 static void BnxeWorkQueueDispatch(void * pArg)
118 {
119     BnxeWorkQueueInstance * pWorkq = (BnxeWorkQueueInstance *)pArg;
120     um_device_t * pUM = (um_device_t *)pWorkq->pUM;
121     BnxeWorkItem * pWorkItem;
122 
123     mutex_enter(&pWorkq->workQueueMutex);
124     pWorkItem = (BnxeWorkItem *)s_list_pop_head(&pWorkq->workQueue);
125     mutex_exit(&pWorkq->workQueueMutex);
126 
127     if (pWorkItem == NULL)
128     {
129         BnxeLogWarn(pUM, "Work item is NULL!");
130         pWorkq->workItemError++;
131         return;
132     }
133 
134     if ((pWorkItem->pWorkCbkCopy == NULL) &&
135         (pWorkItem->pWorkCbkNoCopy == NULL) &&
136         (pWorkItem->pWorkCbkGeneric == NULL))
137     {
138         BnxeLogWarn(pUM, "Work item callback is NULL!");
139         pWorkq->workItemError++;
140         goto BnxeWorkQueueDispatch_done;
141     }
142 
143     if (pWorkItem->delayMs > 0)
144     {
145         /* this only occurs when processing the delayq */
146         drv_usecwait(pWorkItem->delayMs * 1000);
147     }
148 
149     if (pWorkItem->pWorkCbkCopy)
150     {
151         pWorkItem->pWorkCbkCopy(pUM,
152                                 pWorkItem->pWorkData,
153                                 pWorkItem->workDataLen);
154     }
155     else if (pWorkItem->pWorkCbkNoCopy)
156     {
157         pWorkItem->pWorkCbkNoCopy(pUM,
158                                   pWorkItem->pWorkData);
159     }
160     else /* (pWorkItem->pWorkCbkGeneric) */
161     {
162         pWorkItem->pWorkCbkGeneric(pUM);
163     }
164 
165     pWorkq->workItemComplete++;
166 
167 BnxeWorkQueueDispatch_done:
168 
169     kmem_free(pWorkItem, (sizeof(BnxeWorkItem) + pWorkItem->workDataLen));
170 }
171 
172 
BnxeWorkQueueTrigger(um_device_t * pUM,BnxeWorkQueueInstance * pWorkq)173 static void BnxeWorkQueueTrigger(um_device_t *           pUM,
174                                  BnxeWorkQueueInstance * pWorkq)
175 {
176     if (pUM->chipStarted)
177     {
178         ddi_taskq_dispatch(pWorkq->pTaskq,
179                            BnxeWorkQueueDispatch,
180                            (void *)pWorkq,
181                            DDI_NOSLEEP);
182     }
183     else
184     {
185         BnxeLogInfo(pUM, "Delaying WorkQ item since chip not yet started.");
186     }
187 }
188 
189 
BnxeWorkQueueStartPending(um_device_t * pUM)190 void BnxeWorkQueueStartPending(um_device_t * pUM)
191 {
192     u32_t cnt;
193 
194     if (!pUM->chipStarted)
195     {
196         BnxeLogWarn(pUM, "Triggering WorkQs and chip not started!");
197         return;
198     }
199 
200     mutex_enter(&pUM->workqs.instq.workQueueMutex);
201     cnt = s_list_entry_cnt(&pUM->workqs.instq.workQueue);
202     mutex_exit(&pUM->workqs.instq.workQueueMutex);
203 
204     if (cnt)
205     {
206         BnxeWorkQueueTrigger(pUM, &pUM->workqs.instq);
207     }
208 
209     mutex_enter(&pUM->workqs.delayq.workQueueMutex);
210     cnt = s_list_entry_cnt(&pUM->workqs.delayq.workQueue);
211     mutex_exit(&pUM->workqs.delayq.workQueueMutex);
212 
213     if (cnt)
214     {
215         BnxeWorkQueueTrigger(pUM, &pUM->workqs.delayq);
216     }
217 }
218 
219 
BnxeWorkQueueAdd(um_device_t * pUM,void (* pWorkCbkCopy)(um_device_t *,void *,u32_t),void * pWorkData,u32_t workDataLen)220 boolean_t BnxeWorkQueueAdd(um_device_t * pUM,
221                            void (*pWorkCbkCopy)(um_device_t *, void *, u32_t),
222                            void * pWorkData,
223                            u32_t  workDataLen)
224 {
225     BnxeWorkItem * pWorkItem;
226 
227     if ((pWorkItem = kmem_zalloc((sizeof(BnxeWorkItem) + workDataLen),
228                                  KM_NOSLEEP)) == NULL)
229     {
230         BnxeLogWarn(pUM, "Failed to allocate memory for work item!");
231         return B_FALSE;
232     }
233 
234     pWorkItem->pWorkData       = (pWorkItem + 1);
235     pWorkItem->workDataLen     = workDataLen;
236     pWorkItem->pWorkCbkCopy    = pWorkCbkCopy;
237     pWorkItem->pWorkCbkNoCopy  = NULL;
238     pWorkItem->pWorkCbkGeneric = NULL;
239     pWorkItem->delayMs         = 0;
240 
241     memcpy(pWorkItem->pWorkData, pWorkData, workDataLen);
242 
243     mutex_enter(&pUM->workqs.instq.workQueueMutex);
244 
245     s_list_push_tail(&pUM->workqs.instq.workQueue, &pWorkItem->link);
246     pUM->workqs.instq.workItemQueued++;
247     if (s_list_entry_cnt(&pUM->workqs.instq.workQueue) >
248         pUM->workqs.instq.highWater)
249     {
250         pUM->workqs.instq.highWater =
251             s_list_entry_cnt(&pUM->workqs.instq.workQueue);
252     }
253 
254     mutex_exit(&pUM->workqs.instq.workQueueMutex);
255 
256     BnxeWorkQueueTrigger(pUM, &pUM->workqs.instq);
257 
258     return B_TRUE;
259 }
260 
261 
BnxeWorkQueueAddNoCopy(um_device_t * pUM,void (* pWorkCbkNoCopy)(um_device_t *,void *),void * pWorkData)262 boolean_t BnxeWorkQueueAddNoCopy(um_device_t * pUM,
263                                  void (*pWorkCbkNoCopy)(um_device_t *, void *),
264                                  void * pWorkData)
265 {
266     BnxeWorkItem * pWorkItem;
267 
268     if ((pWorkItem = kmem_zalloc(sizeof(BnxeWorkItem), KM_NOSLEEP)) == NULL)
269     {
270         BnxeLogWarn(pUM, "Failed to allocate memory for work item!");
271         return B_FALSE;
272     }
273 
274     pWorkItem->pWorkData       = pWorkData;
275     pWorkItem->workDataLen     = 0;
276     pWorkItem->pWorkCbkCopy    = NULL;
277     pWorkItem->pWorkCbkNoCopy  = pWorkCbkNoCopy;
278     pWorkItem->pWorkCbkGeneric = NULL;
279     pWorkItem->delayMs         = 0;
280 
281     mutex_enter(&pUM->workqs.instq.workQueueMutex);
282 
283     s_list_push_tail(&pUM->workqs.instq.workQueue, &pWorkItem->link);
284     pUM->workqs.instq.workItemQueued++;
285     if (s_list_entry_cnt(&pUM->workqs.instq.workQueue) >
286         pUM->workqs.instq.highWater)
287     {
288         pUM->workqs.instq.highWater =
289             s_list_entry_cnt(&pUM->workqs.instq.workQueue);
290     }
291 
292     mutex_exit(&pUM->workqs.instq.workQueueMutex);
293 
294     BnxeWorkQueueTrigger(pUM, &pUM->workqs.instq);
295 
296     return B_TRUE;
297 }
298 
299 
BnxeWorkQueueAddGeneric(um_device_t * pUM,void (* pWorkCbkGeneric)(um_device_t *))300 boolean_t BnxeWorkQueueAddGeneric(um_device_t * pUM,
301                                   void (*pWorkCbkGeneric)(um_device_t *))
302 {
303     BnxeWorkItem * pWorkItem;
304 
305     if ((pWorkItem = kmem_zalloc(sizeof(BnxeWorkItem), KM_NOSLEEP)) == NULL)
306     {
307         BnxeLogWarn(pUM, "Failed to allocate memory for work item!");
308         return B_FALSE;
309     }
310 
311     pWorkItem->pWorkData       = NULL;
312     pWorkItem->workDataLen     = 0;
313     pWorkItem->pWorkCbkCopy    = NULL;
314     pWorkItem->pWorkCbkNoCopy  = NULL;
315     pWorkItem->pWorkCbkGeneric = pWorkCbkGeneric;
316     pWorkItem->delayMs         = 0;
317 
318     mutex_enter(&pUM->workqs.instq.workQueueMutex);
319 
320     s_list_push_tail(&pUM->workqs.instq.workQueue, &pWorkItem->link);
321     pUM->workqs.instq.workItemQueued++;
322     if (s_list_entry_cnt(&pUM->workqs.instq.workQueue) >
323         pUM->workqs.instq.highWater)
324     {
325         pUM->workqs.instq.highWater =
326             s_list_entry_cnt(&pUM->workqs.instq.workQueue);
327     }
328 
329     mutex_exit(&pUM->workqs.instq.workQueueMutex);
330 
331     BnxeWorkQueueTrigger(pUM, &pUM->workqs.instq);
332 
333     return B_TRUE;
334 }
335 
336 
BnxeWorkQueueAddDelay(um_device_t * pUM,void (* pWorkCbkCopy)(um_device_t *,void *,u32_t),void * pWorkData,u32_t workDataLen,u32_t delayMs)337 boolean_t BnxeWorkQueueAddDelay(um_device_t * pUM,
338                                 void (*pWorkCbkCopy)(um_device_t *, void *, u32_t),
339                                 void * pWorkData,
340                                 u32_t  workDataLen,
341                                 u32_t  delayMs)
342 {
343     BnxeWorkItem * pWorkItem;
344 
345     if ((pWorkItem = kmem_zalloc((sizeof(BnxeWorkItem) + workDataLen),
346                                  KM_NOSLEEP)) == NULL)
347     {
348         BnxeLogWarn(pUM, "Failed to allocate memory for work item!");
349         return B_FALSE;
350     }
351 
352     pWorkItem->pWorkData       = (pWorkItem + 1);
353     pWorkItem->workDataLen     = workDataLen;
354     pWorkItem->pWorkCbkCopy    = pWorkCbkCopy;
355     pWorkItem->pWorkCbkNoCopy  = NULL;
356     pWorkItem->pWorkCbkGeneric = NULL;
357     pWorkItem->delayMs         = delayMs;
358 
359     memcpy(pWorkItem->pWorkData, pWorkData, workDataLen);
360 
361     mutex_enter(&pUM->workqs.delayq.workQueueMutex);
362 
363     s_list_push_tail(&pUM->workqs.delayq.workQueue, &pWorkItem->link);
364     pUM->workqs.delayq.workItemQueued++;
365     if (s_list_entry_cnt(&pUM->workqs.delayq.workQueue) >
366         pUM->workqs.delayq.highWater)
367     {
368         pUM->workqs.delayq.highWater =
369             s_list_entry_cnt(&pUM->workqs.delayq.workQueue);
370     }
371 
372     mutex_exit(&pUM->workqs.delayq.workQueueMutex);
373 
374     BnxeWorkQueueTrigger(pUM, &pUM->workqs.delayq);
375 
376     return B_TRUE;
377 }
378 
379 
BnxeWorkQueueAddDelayNoCopy(um_device_t * pUM,void (* pWorkCbkNoCopy)(um_device_t *,void *),void * pWorkData,u32_t delayMs)380 boolean_t BnxeWorkQueueAddDelayNoCopy(um_device_t * pUM,
381                                       void (*pWorkCbkNoCopy)(um_device_t *, void *),
382                                       void * pWorkData,
383                                       u32_t  delayMs)
384 {
385     BnxeWorkItem * pWorkItem;
386 
387     if ((pWorkItem = kmem_zalloc(sizeof(BnxeWorkItem), KM_NOSLEEP)) == NULL)
388     {
389         BnxeLogWarn(pUM, "Failed to allocate memory for work item!");
390         return B_FALSE;
391     }
392 
393     pWorkItem->pWorkData       = pWorkData;
394     pWorkItem->workDataLen     = 0;
395     pWorkItem->pWorkCbkCopy    = NULL;
396     pWorkItem->pWorkCbkNoCopy  = pWorkCbkNoCopy;
397     pWorkItem->pWorkCbkGeneric = NULL;
398     pWorkItem->delayMs         = delayMs;
399 
400     mutex_enter(&pUM->workqs.delayq.workQueueMutex);
401 
402     s_list_push_tail(&pUM->workqs.delayq.workQueue, &pWorkItem->link);
403     pUM->workqs.delayq.workItemQueued++;
404     if (s_list_entry_cnt(&pUM->workqs.delayq.workQueue) >
405         pUM->workqs.delayq.highWater)
406     {
407         pUM->workqs.delayq.highWater =
408             s_list_entry_cnt(&pUM->workqs.delayq.workQueue);
409     }
410 
411     mutex_exit(&pUM->workqs.delayq.workQueueMutex);
412 
413     BnxeWorkQueueTrigger(pUM, &pUM->workqs.delayq);
414 
415     return B_TRUE;
416 }
417 
418 
BnxeWorkQueueAddDelayGeneric(um_device_t * pUM,void (* pWorkCbkGeneric)(um_device_t *),u32_t delayMs)419 boolean_t BnxeWorkQueueAddDelayGeneric(um_device_t * pUM,
420                                        void (*pWorkCbkGeneric)(um_device_t *),
421                                        u32_t delayMs)
422 {
423     BnxeWorkItem * pWorkItem;
424 
425     if ((pWorkItem = kmem_zalloc(sizeof(BnxeWorkItem), KM_NOSLEEP)) == NULL)
426     {
427         BnxeLogWarn(pUM, "Failed to allocate memory for work item!");
428         return B_FALSE;
429     }
430 
431     pWorkItem->pWorkData       = NULL;
432     pWorkItem->workDataLen     = 0;
433     pWorkItem->pWorkCbkCopy    = NULL;
434     pWorkItem->pWorkCbkNoCopy  = NULL;
435     pWorkItem->pWorkCbkGeneric = pWorkCbkGeneric;
436     pWorkItem->delayMs         = delayMs;
437 
438     mutex_enter(&pUM->workqs.delayq.workQueueMutex);
439 
440     s_list_push_tail(&pUM->workqs.delayq.workQueue, &pWorkItem->link);
441     pUM->workqs.delayq.workItemQueued++;
442     if (s_list_entry_cnt(&pUM->workqs.delayq.workQueue) >
443         pUM->workqs.delayq.highWater)
444     {
445         pUM->workqs.delayq.highWater =
446             s_list_entry_cnt(&pUM->workqs.delayq.workQueue);
447     }
448 
449     mutex_exit(&pUM->workqs.delayq.workQueueMutex);
450 
451     BnxeWorkQueueTrigger(pUM, &pUM->workqs.delayq);
452 
453     return B_TRUE;
454 }
455 
456