sync to trunk head (37853) (except rbuild changes)
[reactos.git] / reactos / drivers / network / afd / afd / lock.c
1 /* $Id$
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/net/afd/afd/lock.c
5 * PURPOSE: Ancillary functions driver
6 * PROGRAMMER: Art Yerkes (ayerkes@speakeasy.net)
7 * UPDATE HISTORY:
8 * 20040708 Created
9 */
10 #include "afd.h"
11 #include "tdi_proto.h"
12 #include "tdiconn.h"
13 #include "debug.h"
14 #include "pseh/pseh2.h"
15
16 /* Lock a method_neither request so it'll be available from DISPATCH_LEVEL */
17 PVOID LockRequest( PIRP Irp, PIO_STACK_LOCATION IrpSp ) {
18 BOOLEAN LockFailed = FALSE;
19
20 Irp->MdlAddress =
21 IoAllocateMdl( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
22 IrpSp->Parameters.DeviceIoControl.InputBufferLength,
23 FALSE,
24 FALSE,
25 NULL );
26 if( Irp->MdlAddress ) {
27 _SEH2_TRY {
28 MmProbeAndLockPages( Irp->MdlAddress, Irp->RequestorMode, IoModifyAccess );
29 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
30 LockFailed = TRUE;
31 } _SEH2_END;
32
33 if( LockFailed ) {
34 IoFreeMdl( Irp->MdlAddress );
35 Irp->MdlAddress = NULL;
36 return NULL;
37 }
38
39 IrpSp->Parameters.DeviceIoControl.Type3InputBuffer =
40 MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority );
41
42 if( !IrpSp->Parameters.DeviceIoControl.Type3InputBuffer ) {
43 IoFreeMdl( Irp->MdlAddress );
44 Irp->MdlAddress = NULL;
45 return NULL;
46 }
47
48 return IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
49 } else return NULL;
50 }
51
52 VOID UnlockRequest( PIRP Irp, PIO_STACK_LOCATION IrpSp ) {
53 PVOID Buffer = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority );
54 if( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer == Buffer || Buffer == NULL ) {
55 MmUnmapLockedPages( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, Irp->MdlAddress );
56 MmUnlockPages( Irp->MdlAddress );
57 IoFreeMdl( Irp->MdlAddress );
58 }
59
60 Irp->MdlAddress = NULL;
61 }
62
63 /* Note: We add an extra buffer if LockAddress is true. This allows us to
64 * treat the address buffer as an ordinary client buffer. It's only used
65 * for datagrams. */
66
67 PAFD_WSABUF LockBuffers( PAFD_WSABUF Buf, UINT Count,
68 PVOID AddressBuf, PINT AddressLen,
69 BOOLEAN Write, BOOLEAN LockAddress ) {
70 UINT i;
71 /* Copy the buffer array so we don't lose it */
72 UINT Lock = (LockAddress && AddressLen) ? 2 : 0;
73 UINT Size = sizeof(AFD_WSABUF) * (Count + Lock);
74 PAFD_WSABUF NewBuf = ExAllocatePool( PagedPool, Size * 2 );
75 PMDL NewMdl;
76 BOOLEAN LockFailed = FALSE;
77
78 AFD_DbgPrint(MID_TRACE,("Called(%08x)\n", NewBuf));
79
80 if( NewBuf ) {
81 PAFD_MAPBUF MapBuf = (PAFD_MAPBUF)(NewBuf + Count + Lock);
82
83 _SEH2_TRY {
84 RtlCopyMemory( NewBuf, Buf, sizeof(AFD_WSABUF) * Count );
85 if( LockAddress ) {
86 NewBuf[Count].buf = AddressBuf;
87 NewBuf[Count].len = *AddressLen;
88 Count++;
89 NewBuf[Count].buf = (PVOID)AddressLen;
90 NewBuf[Count].len = sizeof(*AddressLen);
91 Count++;
92 }
93 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
94 AFD_DbgPrint(MIN_TRACE,("Access violation copying buffer info "
95 "from userland (%x %x)\n",
96 Buf, AddressLen));
97 ExFreePool( NewBuf );
98 _SEH2_YIELD(return NULL);
99 } _SEH2_END;
100
101 for( i = 0; i < Count; i++ ) {
102 AFD_DbgPrint(MID_TRACE,("Locking buffer %d (%x:%d)\n",
103 i, NewBuf[i].buf, NewBuf[i].len));
104
105 if( NewBuf[i].len ) {
106 NewMdl = IoAllocateMdl( NewBuf[i].buf,
107 NewBuf[i].len,
108 FALSE,
109 FALSE,
110 NULL );
111 } else {
112 MapBuf[i].Mdl = NULL;
113 continue;
114 }
115
116 AFD_DbgPrint(MID_TRACE,("NewMdl @ %x\n", NewMdl));
117
118 MapBuf[i].Mdl = NewMdl;
119
120 if( MapBuf[i].Mdl ) {
121 AFD_DbgPrint(MID_TRACE,("Probe and lock pages\n"));
122 _SEH2_TRY {
123 MmProbeAndLockPages( MapBuf[i].Mdl, KernelMode,
124 Write ? IoModifyAccess : IoReadAccess );
125 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
126 LockFailed = TRUE;
127 } _SEH2_END;
128 AFD_DbgPrint(MID_TRACE,("MmProbeAndLock finished\n"));
129
130 if( LockFailed ) {
131 IoFreeMdl( MapBuf[i].Mdl );
132 MapBuf[i].Mdl = NULL;
133 ExFreePool( NewBuf );
134 return NULL;
135 }
136 } else {
137 ExFreePool( NewBuf );
138 return NULL;
139 }
140 }
141 }
142
143 AFD_DbgPrint(MID_TRACE,("Leaving %x\n", NewBuf));
144
145 return NewBuf;
146 }
147
148 VOID UnlockBuffers( PAFD_WSABUF Buf, UINT Count, BOOL Address ) {
149 UINT Lock = Address ? 2 : 0;
150 PAFD_MAPBUF Map = (PAFD_MAPBUF)(Buf + Count + Lock);
151 UINT i;
152
153 if( !Buf ) return;
154
155 for( i = 0; i < Count + Lock; i++ ) {
156 if( Map[i].Mdl ) {
157 MmUnlockPages( Map[i].Mdl );
158 IoFreeMdl( Map[i].Mdl );
159 Map[i].Mdl = NULL;
160 }
161 }
162
163 ExFreePool( Buf );
164 Buf = NULL;
165 }
166
167 /* Produce a kernel-land handle array with handles replaced by object
168 * pointers. This will allow the system to do proper alerting */
169 PAFD_HANDLE LockHandles( PAFD_HANDLE HandleArray, UINT HandleCount ) {
170 UINT i;
171 NTSTATUS Status = STATUS_SUCCESS;
172
173 PAFD_HANDLE FileObjects = ExAllocatePool
174 ( NonPagedPool, HandleCount * sizeof(AFD_HANDLE) );
175
176 for( i = 0; FileObjects && i < HandleCount; i++ ) {
177 FileObjects[i].Status = 0;
178 FileObjects[i].Events = HandleArray[i].Events;
179 FileObjects[i].Handle = 0;
180 if( !HandleArray[i].Handle ) continue;
181 if( NT_SUCCESS(Status) ) {
182 Status = ObReferenceObjectByHandle
183 ( (PVOID)(ULONG_PTR)HandleArray[i].Handle,
184 FILE_ALL_ACCESS,
185 NULL,
186 KernelMode,
187 (PVOID*)&FileObjects[i].Handle,
188 NULL );
189 }
190 }
191
192 if( !NT_SUCCESS(Status) ) {
193 UnlockHandles( FileObjects, HandleCount );
194 return NULL;
195 }
196
197 return FileObjects;
198 }
199
200 VOID UnlockHandles( PAFD_HANDLE HandleArray, UINT HandleCount ) {
201 UINT i;
202
203 for( i = 0; i < HandleCount; i++ ) {
204 if( HandleArray[i].Handle )
205 ObDereferenceObject( (PVOID)(ULONG_PTR)HandleArray[i].Handle );
206 }
207
208 ExFreePool( HandleArray );
209 HandleArray = NULL;
210 }
211
212 /* Returns transitioned state or SOCKET_STATE_INVALID_TRANSITION */
213 UINT SocketAcquireStateLock( PAFD_FCB FCB ) {
214 NTSTATUS Status = STATUS_SUCCESS;
215 PVOID CurrentThread = KeGetCurrentThread();
216
217 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
218
219 AFD_DbgPrint(MAX_TRACE,("Called on %x, attempting to lock\n", FCB));
220
221 /* Wait for the previous user to unlock the FCB state. There might be
222 * multiple waiters waiting to change the state. We need to check each
223 * time we get the event whether somebody still has the state locked */
224
225 if( !FCB ) return FALSE;
226
227 if( CurrentThread == FCB->CurrentThread ) {
228 FCB->LockCount++;
229 AFD_DbgPrint(MID_TRACE,
230 ("Same thread, lock count %d\n", FCB->LockCount));
231 return TRUE;
232 } else {
233 AFD_DbgPrint(MID_TRACE,
234 ("Thread %x opposes lock thread %x\n",
235 CurrentThread, FCB->CurrentThread));
236 }
237
238
239 ExAcquireFastMutex( &FCB->Mutex );
240
241 while( FCB->Locked ) {
242 AFD_DbgPrint
243 (MID_TRACE,("FCB %x is locked, waiting for notification\n",
244 FCB));
245 ExReleaseFastMutex( &FCB->Mutex );
246 Status = KeWaitForSingleObject( &FCB->StateLockedEvent,
247 UserRequest,
248 KernelMode,
249 FALSE,
250 NULL );
251 ExAcquireFastMutex( &FCB->Mutex );
252 }
253 FCB->Locked = TRUE;
254 FCB->CurrentThread = CurrentThread;
255 FCB->LockCount++;
256 ExReleaseFastMutex( &FCB->Mutex );
257
258 AFD_DbgPrint(MAX_TRACE,("Got lock (%d).\n", FCB->LockCount));
259
260 return TRUE;
261 }
262
263 VOID SocketStateUnlock( PAFD_FCB FCB ) {
264 #ifdef DBG
265 PVOID CurrentThread = KeGetCurrentThread();
266 #endif
267 ASSERT(FCB->LockCount > 0);
268 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
269
270 ExAcquireFastMutex( &FCB->Mutex );
271 FCB->LockCount--;
272
273 if( !FCB->LockCount ) {
274 FCB->CurrentThread = NULL;
275 FCB->Locked = FALSE;
276
277 AFD_DbgPrint(MAX_TRACE,("Unlocked.\n"));
278 KePulseEvent( &FCB->StateLockedEvent, IO_NETWORK_INCREMENT, FALSE );
279 } else {
280 AFD_DbgPrint(MAX_TRACE,("New lock count: %d (Thr: %x)\n",
281 FCB->LockCount, CurrentThread));
282 }
283 ExReleaseFastMutex( &FCB->Mutex );
284 }
285
286 NTSTATUS NTAPI UnlockAndMaybeComplete
287 ( PAFD_FCB FCB, NTSTATUS Status, PIRP Irp,
288 UINT Information,
289 PIO_COMPLETION_ROUTINE Completion ) {
290
291 Irp->IoStatus.Status = Status;
292 Irp->IoStatus.Information = Information;
293
294 if( Status == STATUS_PENDING ) {
295 /* We should firstly mark this IRP as pending, because
296 otherwise it may be completed by StreamSocketConnectComplete()
297 before we return from SocketStateUnlock(). */
298 IoMarkIrpPending( Irp );
299 SocketStateUnlock( FCB );
300 } else {
301 if ( Irp->MdlAddress ) UnlockRequest( Irp, IoGetCurrentIrpStackLocation( Irp ) );
302 SocketStateUnlock( FCB );
303 if( Completion )
304 Completion( FCB->DeviceExt->DeviceObject, Irp, FCB );
305 IoCompleteRequest( Irp, IO_NETWORK_INCREMENT );
306 }
307 return Status;
308 }
309
310
311 NTSTATUS LostSocket( PIRP Irp ) {
312 NTSTATUS Status = STATUS_FILE_CLOSED;
313 AFD_DbgPrint(MIN_TRACE,("Called.\n"));
314 Irp->IoStatus.Information = 0;
315 Irp->IoStatus.Status = Status;
316 if ( Irp->MdlAddress ) UnlockRequest( Irp, IoGetCurrentIrpStackLocation( Irp ) );
317 IoCompleteRequest( Irp, IO_NO_INCREMENT );
318 return Status;
319 }
320
321 NTSTATUS LeaveIrpUntilLater( PAFD_FCB FCB, PIRP Irp, UINT Function ) {
322 InsertTailList( &FCB->PendingIrpList[Function],
323 &Irp->Tail.Overlay.ListEntry );
324 return UnlockAndMaybeComplete( FCB, STATUS_PENDING, Irp, 0, NULL );
325 }
326
327 VOID SocketCalloutEnter( PAFD_FCB FCB ) {
328 ASSERT(FCB->Locked);
329 FCB->Critical = TRUE;
330 SocketStateUnlock( FCB );
331 }
332
333 VOID SocketCalloutLeave( PAFD_FCB FCB ) {
334 FCB->Critical = FALSE;
335 SocketAcquireStateLock( FCB );
336 }