7efdba3d4e7caeaeb99acf40e159880b3066c1c2
[mirror/winof/.git] / core / winmad / kernel / wm_provider.c
1 /*\r
2  * Copyright (c) 2008 Intel Corporation. All rights reserved.\r
3  *\r
4  * This software is available to you under the OpenIB.org BSD license\r
5  * below:\r
6  *\r
7  *     Redistribution and use in source and binary forms, with or\r
8  *     without modification, are permitted provided that the following\r
9  *     conditions are met:\r
10  *\r
11  *      - Redistributions of source code must retain the above\r
12  *        copyright notice, this list of conditions and the following\r
13  *        disclaimer.\r
14  *\r
15  *      - Redistributions in binary form must reproduce the above\r
16  *        copyright notice, this list of conditions and the following\r
17  *        disclaimer in the documentation and/or other materials\r
18  *        provided with the distribution.\r
19  *\r
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AWV\r
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
27  * SOFTWARE.\r
28  */\r
29 \r
30 #include "index_list.c"\r
31 #include "wm_driver.h"\r
32 #include "wm_ioctl.h"\r
33 #include "wm_provider.h"\r
34 #include "wm_reg.h"\r
35 \r
36 void WmProviderGet(WM_PROVIDER *pProvider)\r
37 {\r
38         InterlockedIncrement(&pProvider->Ref);\r
39 }\r
40 \r
41 void WmProviderPut(WM_PROVIDER *pProvider)\r
42 {\r
43         if (InterlockedDecrement(&pProvider->Ref) == 0) {\r
44                 KeSetEvent(&pProvider->Event, 0, FALSE);\r
45         }\r
46 }\r
47 \r
48 NTSTATUS WmProviderInit(WM_PROVIDER *pProvider)\r
49 {\r
50         WDF_IO_QUEUE_CONFIG     config;\r
51         NTSTATUS status;\r
52 \r
53         IndexListInit(&pProvider->RegIndex);\r
54         pProvider->MadHead = NULL;\r
55         pProvider->MadTail = NULL;\r
56 \r
57         KeInitializeGuardedMutex(&pProvider->Lock);\r
58         pProvider->Ref = 1;\r
59         KeInitializeEvent(&pProvider->Event, NotificationEvent, FALSE);\r
60 \r
61         pProvider->Pending = 0;\r
62         pProvider->Active = 0;\r
63         KeInitializeEvent(&pProvider->SharedEvent, NotificationEvent, FALSE);\r
64         pProvider->Exclusive = 0;\r
65         KeInitializeEvent(&pProvider->ExclusiveEvent, SynchronizationEvent, FALSE);\r
66 \r
67         WDF_IO_QUEUE_CONFIG_INIT(&config, WdfIoQueueDispatchManual);\r
68         status = WdfIoQueueCreate(ControlDevice, &config,\r
69                                                           WDF_NO_OBJECT_ATTRIBUTES, &pProvider->ReadQueue);\r
70         return status;\r
71 }\r
72 \r
73 static void WmInsertMad(WM_PROVIDER *pProvider, ib_mad_element_t *pMad)\r
74 {\r
75         if (pProvider->MadHead == NULL) {\r
76                 pProvider->MadHead = pMad;\r
77         } else {\r
78                 pProvider->MadTail->p_next = pMad;\r
79         }\r
80         pProvider->MadTail = pMad;\r
81 }\r
82 \r
83 static ib_mad_element_t *WmRemoveMad(WM_PROVIDER *pProvider)\r
84 {\r
85         ib_mad_element_t *mad;\r
86 \r
87         mad = pProvider->MadHead;\r
88         if (mad != NULL) {\r
89                 pProvider->MadHead = (ib_mad_element_t *) mad->p_next;\r
90                 mad->p_next = NULL;\r
91         }\r
92         return mad;\r
93 }\r
94 \r
95 void WmProviderFlushReceives(WM_PROVIDER *pProvider, WM_REGISTRATION *pRegistration)\r
96 {\r
97         ib_mad_element_t        *mad, *next, *list;\r
98 \r
99         WdfObjectAcquireLock(pProvider->ReadQueue);\r
100         list = pProvider->MadHead;\r
101         pProvider->MadHead = NULL;\r
102 \r
103         for (mad = list; mad != NULL; mad = next) {\r
104                 next = mad->p_next;\r
105                 mad->p_next = NULL;\r
106 \r
107                 if (mad->send_context1 == pRegistration) {\r
108                         pRegistration->pDevice->IbInterface.put_mad(mad);\r
109                 } else {\r
110                         WmInsertMad(pProvider, mad);\r
111                 }\r
112         }\r
113         WdfObjectReleaseLock(pProvider->ReadQueue);\r
114 }\r
115 \r
116 void WmProviderCleanup(WM_PROVIDER *pProvider)\r
117 {\r
118         WM_REGISTRATION         *reg;\r
119 \r
120         while ((reg = IndexListRemoveHead(&pProvider->RegIndex)) != NULL) {\r
121                 WmRegFree(reg);\r
122         }\r
123 \r
124         if (InterlockedDecrement(&pProvider->Ref) > 0) {\r
125                 KeWaitForSingleObject(&pProvider->Event, Executive, KernelMode, FALSE, NULL);\r
126         }\r
127 \r
128         WdfIoQueuePurgeSynchronously(pProvider->ReadQueue);\r
129         WdfObjectDelete(pProvider->ReadQueue);\r
130 \r
131         IndexListDestroy(&pProvider->RegIndex);\r
132 }\r
133 \r
134 // See comment above WmProviderRemoveHandler.\r
135 static void WmProviderLockRemove(WM_PROVIDER *pProvider)\r
136 {\r
137         KeAcquireGuardedMutex(&pProvider->Lock);\r
138         pProvider->Exclusive++;\r
139         KeClearEvent(&pProvider->SharedEvent);\r
140         while (pProvider->Active > 0) {\r
141                 KeReleaseGuardedMutex(&pProvider->Lock);\r
142                 KeWaitForSingleObject(&pProvider->ExclusiveEvent, Executive, KernelMode,\r
143                                                           FALSE, NULL);\r
144                 KeAcquireGuardedMutex(&pProvider->Lock);\r
145         }\r
146         pProvider->Active++;\r
147         KeReleaseGuardedMutex(&pProvider->Lock);\r
148 }\r
149 \r
150 // See comment above WmProviderRemoveHandler.\r
151 static void WmProviderUnlockRemove(WM_PROVIDER *pProvider)\r
152 {\r
153         KeAcquireGuardedMutex(&pProvider->Lock);\r
154         pProvider->Exclusive--;\r
155         pProvider->Active--;\r
156         if (pProvider->Exclusive > 0) {\r
157                 KeSetEvent(&pProvider->ExclusiveEvent, 0, FALSE);\r
158         } else if (pProvider->Pending > 0) {\r
159                 KeSetEvent(&pProvider->SharedEvent, 0, FALSE);\r
160         }\r
161         KeReleaseGuardedMutex(&pProvider->Lock);\r
162 }\r
163 \r
164 /*\r
165  * Must hold pProvider->Lock.  Function may release and re-acquire.\r
166  * See comment above WmProviderRemoveHandler.\r
167  */\r
168 void WmProviderDisableRemove(WM_PROVIDER *pProvider)\r
169 {\r
170         while (pProvider->Exclusive > 0) {\r
171                 pProvider->Pending++;\r
172                 KeReleaseGuardedMutex(&pProvider->Lock);\r
173                 KeWaitForSingleObject(&pProvider->SharedEvent, Executive, KernelMode,\r
174                                                           FALSE, NULL);\r
175                 KeAcquireGuardedMutex(&pProvider->Lock);\r
176                 pProvider->Pending--;\r
177         }\r
178         InterlockedIncrement(&pProvider->Active);\r
179 }\r
180 \r
181 /*\r
182  * No need to hold pProvider->Lock when releasing.\r
183  * See comment above WmProviderRemoveHandler.\r
184  */\r
185 void WmProviderEnableRemove(WM_PROVIDER *pProvider)\r
186 {\r
187         InterlockedDecrement(&pProvider->Active);\r
188         if (pProvider->Exclusive > 0) {\r
189                 KeSetEvent(&pProvider->ExclusiveEvent, 0, FALSE);\r
190         }\r
191 }\r
192 \r
193 /*\r
194  * The remove handler blocks all other threads executing through this\r
195  * provider until the remove has been processed.  Because device removal is\r
196  * rare, we want a simple, optimized code path for all calls that access\r
197  * the underlying hardware device, making use of any locks that we would\r
198  * have to acquire anyway.  The locking for exclusive access can be\r
199  * as ugly and slow as needed.\r
200  */\r
201 void WmProviderRemoveHandler(WM_PROVIDER *pProvider, WM_IB_DEVICE *pDevice)\r
202 {\r
203         WM_REGISTRATION *reg;\r
204         SIZE_T i;\r
205 \r
206         WmProviderLockRemove(pProvider);\r
207         IndexListForEach(&pProvider->RegIndex, i) {\r
208                 reg = IndexListAt(&pProvider->RegIndex, i);\r
209                 if (reg->pDevice == pDevice) {\r
210                         WmRegRemoveHandler(reg);\r
211                 }\r
212         }\r
213         WmProviderUnlockRemove(pProvider);\r
214 }\r
215 \r
216 static NTSTATUS WmCopyRead(WM_PROVIDER *pProvider, WM_IO_MAD *pIoMad,\r
217                                                    ib_mad_element_t *pMad, size_t *pLen)\r
218 {\r
219         WM_REGISTRATION         *reg;\r
220 \r
221         reg = (WM_REGISTRATION *) pMad->send_context1;\r
222         pIoMad->Id = reg->Id;\r
223 \r
224         pIoMad->Status = pMad->status;\r
225         pIoMad->Timeout = pMad->timeout_ms;\r
226         pIoMad->Retries = pMad->retry_cnt;\r
227         pIoMad->Length = pMad->size;\r
228 \r
229         pIoMad->Address.Qpn = pMad->remote_qp;\r
230         pIoMad->Address.Qkey = pMad->remote_qkey;\r
231         pIoMad->Address.PkeyIndex = pMad->pkey_index;\r
232 \r
233         if ((pIoMad->Address.GrhValid = (UINT8) pMad->grh_valid)) {\r
234                 pIoMad->Address.VersionClassFlow = pMad->p_grh->ver_class_flow;\r
235                 pIoMad->Address.HopLimit = pMad->p_grh->hop_limit;\r
236                 pIoMad->Address.GidIndex = 0;   // TODO: update IBAL to use SGID index\r
237                 RtlCopyMemory(pIoMad->Address.Gid, pMad->p_grh->dest_gid.raw, 16);\r
238         }\r
239 \r
240         pIoMad->Address.Lid = pMad->remote_lid;\r
241         pIoMad->Address.ServiceLevel = pMad->remote_sl;\r
242         pIoMad->Address.PathBits = pMad->path_bits;\r
243         pIoMad->Address.StaticRate = 0;\r
244         pIoMad->Address.Reserved = 0;\r
245 \r
246         if (*pLen >= sizeof(WM_IO_MAD) + pMad->size) {\r
247                 RtlCopyMemory(pIoMad + 1, pMad->p_mad_buf, pMad->size);\r
248                 *pLen = sizeof(WM_IO_MAD) + pMad->size;\r
249                 return STATUS_SUCCESS;\r
250         } else {\r
251                 *pLen = sizeof(WM_IO_MAD);\r
252                 return STATUS_MORE_ENTRIES;\r
253         }\r
254 }\r
255 \r
256 void WmProviderRead(WM_PROVIDER *pProvider, WDFREQUEST Request)\r
257 {\r
258         WM_REGISTRATION         *reg;\r
259         NTSTATUS                        status;\r
260         WM_IO_MAD                       *wmad;\r
261         size_t                          outlen, len = 0;\r
262 \r
263         status = WdfRequestRetrieveOutputBuffer(Request, sizeof(WM_IO_MAD), &wmad, &outlen);\r
264         if (!NT_SUCCESS(status)) {\r
265                 goto out;\r
266         }\r
267 \r
268         WdfObjectAcquireLock(pProvider->ReadQueue);\r
269         if (pProvider->MadHead == NULL) {\r
270                 status = WdfRequestForwardToIoQueue(Request, pProvider->ReadQueue);\r
271                 WdfObjectReleaseLock(pProvider->ReadQueue);\r
272                 if (NT_SUCCESS(status)) {\r
273                         return;\r
274                 }\r
275                 goto out;\r
276         }\r
277 \r
278         len = outlen;\r
279         status = WmCopyRead(pProvider, wmad, pProvider->MadHead, &len);\r
280         if (NT_SUCCESS(status)) {\r
281                 reg = (WM_REGISTRATION *) pProvider->MadHead->send_context1;\r
282                 reg->pDevice->IbInterface.put_mad(WmRemoveMad(pProvider));\r
283         }\r
284         WdfObjectReleaseLock(pProvider->ReadQueue);\r
285 \r
286 out:\r
287         WdfRequestCompleteWithInformation(Request, status, len);\r
288 }\r
289 \r
290 static NTSTATUS WmSendMad(WM_REGISTRATION *pRegistration, WM_IO_MAD *pIoMad, UINT32 size)\r
291 {\r
292         ib_al_ifc_t                     *pifc;\r
293         NTSTATUS                        status;\r
294         ib_mad_element_t        *mad;\r
295         ib_api_status_t         ib_status;\r
296 \r
297         pifc = &pRegistration->pDevice->IbInterface;\r
298         ib_status = pifc->get_mad(pRegistration->hMadPool, size, &mad);\r
299         if (ib_status != IB_SUCCESS) {\r
300                 return STATUS_NO_MEMORY;\r
301         }\r
302 \r
303         mad->context1 = pRegistration;\r
304         RtlCopyMemory(mad->p_mad_buf, pIoMad + 1, size);\r
305         mad->remote_qp = pIoMad->Address.Qpn;\r
306         mad->remote_qkey = pIoMad->Address.Qkey;\r
307         mad->resp_expected = (pIoMad->Timeout > 0);\r
308         mad->timeout_ms = pIoMad->Timeout;\r
309         mad->retry_cnt = pIoMad->Retries;\r
310 \r
311         if ((mad->grh_valid = pIoMad->Address.GrhValid)) {\r
312                 mad->p_grh->ver_class_flow = pIoMad->Address.VersionClassFlow;\r
313                 mad->p_grh->hop_limit = pIoMad->Address.HopLimit;\r
314                 // TODO: update IBAL to use SGID index\r
315                 // mad->p_grh->src_gid_index = pIoMad->Address.GidIndex;\r
316                 RtlCopyMemory(mad->p_grh->dest_gid.raw, pIoMad->Address.Gid, 16);\r
317         }\r
318 \r
319         mad->remote_lid = pIoMad->Address.Lid;\r
320         mad->remote_sl = pIoMad->Address.ServiceLevel;\r
321         mad->pkey_index = pIoMad->Address.PkeyIndex;\r
322         mad->path_bits = pIoMad->Address.PathBits;\r
323         mad->p_mad_buf->trans_id &= 0xFFFFFFFF00000000;\r
324 \r
325         ib_status = pifc->send_mad(pRegistration->hService, mad, NULL);\r
326         if (ib_status != IB_SUCCESS) {\r
327                 status = STATUS_UNSUCCESSFUL;\r
328                 goto err;\r
329         }\r
330 \r
331         return STATUS_SUCCESS;\r
332 \r
333 err:\r
334         pRegistration->pDevice->IbInterface.put_mad(mad);\r
335         return status;\r
336 }\r
337 \r
338 void WmProviderWrite(WM_PROVIDER *pProvider, WDFREQUEST Request)\r
339 {\r
340         WM_REGISTRATION         *reg;\r
341         NTSTATUS                        status;\r
342         WM_IO_MAD                       *wmad;\r
343         size_t                          inlen;\r
344 \r
345         status = WdfRequestRetrieveInputBuffer(Request, sizeof(WM_IO_MAD) + 24,\r
346                                                                                    &wmad, &inlen);\r
347         if (!NT_SUCCESS(status)) {\r
348                 goto out;\r
349         }\r
350 \r
351         reg = WmRegAcquire(pProvider, wmad->Id);\r
352         if (reg == NULL) {\r
353                 status = STATUS_NOT_FOUND;\r
354                 goto out;\r
355         }\r
356 \r
357         status = WmSendMad(reg, wmad, (UINT32) (inlen - sizeof(WM_IO_MAD)));\r
358         WmRegRelease(reg);\r
359 \r
360 out:\r
361         WdfRequestComplete(Request, status);\r
362 }\r
363 \r
364 void WmReceiveHandler(ib_mad_svc_handle_t hService, void *Context,\r
365                                           ib_mad_element_t *pMad)\r
366 {\r
367         WM_REGISTRATION *reg;\r
368         WM_PROVIDER             *prov = Context;\r
369         WDFREQUEST              request;\r
370         NTSTATUS                status;\r
371         WM_IO_MAD               *wmad;\r
372         size_t                  len = 0;\r
373 \r
374         UNREFERENCED_PARAMETER(hService);\r
375 \r
376         WdfObjectAcquireLock(prov->ReadQueue);\r
377         status = WdfIoQueueRetrieveNextRequest(prov->ReadQueue, &request);\r
378         if (!NT_SUCCESS(status)) {\r
379                 WmInsertMad(prov, pMad);\r
380                 WdfObjectReleaseLock(prov->ReadQueue);\r
381                 return;\r
382         }\r
383 \r
384         status = WdfRequestRetrieveOutputBuffer(request, sizeof(WM_IO_MAD), &wmad, &len);\r
385         if (!NT_SUCCESS(status)) {\r
386                 reg = (WM_REGISTRATION *) pMad->send_context1;\r
387                 reg->pDevice->IbInterface.put_mad(pMad);\r
388                 goto out;\r
389         }\r
390 \r
391         status = WmCopyRead(prov, wmad, pMad, &len);\r
392         if (NT_SUCCESS(status)) {\r
393                 reg = (WM_REGISTRATION *) pMad->send_context1;\r
394                 reg->pDevice->IbInterface.put_mad(pMad);\r
395         } else {\r
396                 WmInsertMad(prov, pMad);\r
397         }\r
398         WdfObjectReleaseLock(prov->ReadQueue);\r
399 \r
400 out:\r
401         WdfRequestCompleteWithInformation(request, status, len);        \r
402 }\r
403 \r
404 void WmSendHandler(ib_mad_svc_handle_t hService, void *Context,\r
405                                    ib_mad_element_t *pMad)\r
406 {\r
407         if (pMad->status == IB_SUCCESS) {\r
408                 ((WM_REGISTRATION *) pMad->context1)->pDevice->IbInterface.put_mad(pMad);\r
409         } else {\r
410                 pMad->send_context1 = (void*) pMad->context1;\r
411                 WmReceiveHandler(hService, Context, pMad);\r
412         }\r
413 }\r
414 \r
415 void WmProviderCancel(WM_PROVIDER *pProvider, WDFREQUEST Request)\r
416 {\r
417         WDFREQUEST      request;\r
418         NTSTATUS        status;\r
419 \r
420         WdfObjectAcquireLock(pProvider->ReadQueue);\r
421         status = WdfIoQueueRetrieveNextRequest(pProvider->ReadQueue, &request);\r
422 \r
423         while (NT_SUCCESS(status)) {\r
424                 WdfRequestComplete(request, STATUS_CANCELLED);\r
425                 status = WdfIoQueueRetrieveNextRequest(pProvider->ReadQueue, &request);\r
426         }\r
427         WdfObjectReleaseLock(pProvider->ReadQueue);\r
428 \r
429         WdfRequestComplete(Request, status);\r
430 }\r