kvm-guest-drivers-windows
1840 строк · 53.5 Кб
1/*
2* Placeholder for the Recv path functions
3*
4* Copyright (c) 2020 Virtuozzo International GmbH
5*
6* Redistribution and use in source and binary forms, with or without
7* modification, are permitted provided that the following conditions
8* are met :
9* 1. Redistributions of source code must retain the above copyright
10* notice, this list of conditions and the following disclaimer.
11* 2. Redistributions in binary form must reproduce the above copyright
12* notice, this list of conditions and the following disclaimer in the
13* documentation and / or other materials provided with the distribution.
14* 3. Neither the names of the copyright holders nor the names of their contributors
15* may be used to endorse or promote products derived from this software
16* without specific prior written permission.
17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27* SUCH DAMAGE.
28*/
29
30#include "precomp.h"31#include "viosock.h"32
33#if defined(EVENT_TRACING)34#include "Rx.tmh"35#endif36
37#define VIOSOCK_DMA_RX_PAGES 1 //contiguous buffer38
39#define VIOSOCK_BYTES_TO_MERGE 128 //max bytes to merge with prev buffer40
41#define VIOSOCK_CB_ENTRIES(n) ((n)+(n>>1)) //default chained buffer queue size42
43//Chained Buffer entry44typedef struct _VIOSOCK_RX_CB45{
46union {47SINGLE_LIST_ENTRY FreeListEntry;48LIST_ENTRY ListEntry; //Request buffer list49};50
51PVOID BufferVA; //common buffer of VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE bytes52PHYSICAL_ADDRESS BufferPA; //common buffer PA53
54PCHAR ReadPtr;55ULONG BytesToRead;56
57ULONG DataLen; //Valid data len (pkt.header.len)58WDFREQUEST Request; //Write request for loopback59WDFMEMORY Memory;60}VIOSOCK_RX_CB, *PVIOSOCK_RX_CB;61
62WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(VIOSOCK_RX_CB, GetRequestRxCb);63
64typedef struct _VIOSOCK_RX_PKT65{
66VIRTIO_VSOCK_HDR Header;67PVIOSOCK_RX_CB Buffer; //Chained buffer68union {69BYTE IndirectDescs[SIZE_OF_SINGLE_INDIRECT_DESC * (1 + VIOSOCK_DMA_RX_PAGES)]; //Header + buffer70SINGLE_LIST_ENTRY RxPktListEntry;71LIST_ENTRY CompletionListEntry;72};73}VIOSOCK_RX_PKT, *PVIOSOCK_RX_PKT;74
75typedef struct _VIOSOCK_RX_CONTEXT {76ULONG BufferLen;77ULONG FreeBytes;78union {79struct80{81LONGLONG Timeout; //100ns82LONG Counter;83ULONG Flags;84PCHAR FreePtr;85};86struct87{88LIST_ENTRY ListEntry;89WDFREQUEST ThisRequest;90};91};92}VIOSOCK_RX_CONTEXT, *PVIOSOCK_RX_CONTEXT;93
94WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(VIOSOCK_RX_CONTEXT, GetRequestRxContext);95
96BOOLEAN
97VIOSockRxCbInit(98IN PDEVICE_CONTEXT pContext
99);100
101BOOLEAN
102VIOSockRxCbAdd(103IN PDEVICE_CONTEXT pContext
104);105
106VOID
107VIOSockRxCbCleanup(108IN PDEVICE_CONTEXT pContext
109);110
111EVT_WDF_IO_QUEUE_IO_READ VIOSockRead;112EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE VIOSockReadIoCanceledOnQueue;113EVT_WDF_IO_QUEUE_STATE VIOSockReadSocketState;114EVT_WDF_REQUEST_CANCEL VIOSockRxRequestCancelCb;115EVT_WDF_TIMER VIOSockReadTimerFunc;116
117
118#ifdef ALLOC_PRAGMA119#pragma alloc_text (PAGE, VIOSockRxCbInit)120#pragma alloc_text (PAGE, VIOSockRxCbAdd)121#pragma alloc_text (PAGE, VIOSockRxCbCleanup)122#pragma alloc_text (PAGE, VIOSockReadQueueInit)123#pragma alloc_text (PAGE, VIOSockReadSocketQueueInit)124#endif125
126//////////////////////////////////////////////////////////////////////////
127_Requires_lock_held_(pContext->RxLock)128__inline
129VOID
130VIOSockRxCbPush(131PDEVICE_CONTEXT pContext,132PVIOSOCK_RX_CB pCb
133)
134{
135PushEntryList(&pContext->RxCbBuffers, &pCb->FreeListEntry);136}
137
138_Requires_lock_not_held_(pContext->RxLock)139__inline
140VOID
141VIOSockRxCbPushLocked(142PDEVICE_CONTEXT pContext,143PVIOSOCK_RX_CB pCb
144)
145{
146WdfSpinLockAcquire(pContext->RxLock);147VIOSockRxCbPush(pContext, pCb);148WdfSpinLockRelease(pContext->RxLock);149}
150
151_Requires_lock_held_(pContext->RxLock)152__inline
153PVIOSOCK_RX_CB
154VIOSockRxCbPop(155IN PDEVICE_CONTEXT pContext
156)
157{
158PSINGLE_LIST_ENTRY pListEntry = PopEntryList(&pContext->RxCbBuffers);159
160if (pListEntry)161{162return CONTAINING_RECORD(pListEntry, VIOSOCK_RX_CB, FreeListEntry);163}164
165return NULL;166}
167
168__inline
169VOID
170VIOSockRxCbFree(171IN PDEVICE_CONTEXT pContext,172IN PVIOSOCK_RX_CB pCb
173)
174{
175ASSERT(pCb && pCb->BufferVA);176
177ASSERT(pCb->BufferPA.QuadPart && pCb->Request == WDF_NO_HANDLE);178
179if (pCb->BufferPA.QuadPart)180VirtIOWdfDeviceFreeDmaMemory(&pContext->VDevice.VIODevice, pCb->BufferVA);181
182WdfObjectDelete(pCb->Memory);183}
184
185static
186BOOLEAN
187VIOSockRxCbAdd(188IN PDEVICE_CONTEXT pContext
189)
190{
191NTSTATUS status;192PVIOSOCK_RX_CB pCb;193WDFMEMORY Memory;194BOOLEAN bRes = FALSE;195
196PAGED_CODE();197
198status = WdfMemoryCreateFromLookaside(pContext->RxCbBufferMemoryList, &Memory);199if (NT_SUCCESS(status))200{201pCb = WdfMemoryGetBuffer(Memory, NULL);202
203RtlZeroMemory(pCb, sizeof(*pCb));204
205pCb->Memory = Memory;206pCb->BufferVA = VirtIOWdfDeviceAllocDmaMemory(&pContext->VDevice.VIODevice,207VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE, VIOSOCK_DRIVER_MEMORY_TAG);208
209if (!pCb->BufferVA)210{211TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,212"VirtIOWdfDeviceAllocDmaMemory(%u bytes for Rx buffer) failed\n", VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE);213WdfObjectDelete(pCb->Memory);214}215else216{217pCb->BufferPA = VirtIOWdfDeviceGetPhysicalAddress(&pContext->VDevice.VIODevice,218pCb->BufferVA);219
220ASSERT(pCb->BufferPA.QuadPart);221
222if (!pCb->BufferPA.QuadPart)223{224TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "VirtIOWdfDeviceGetPhysicalAddress failed\n");225VIOSockRxCbFree(pContext, pCb);226}227else228{229//no need to lock, init call230VIOSockRxCbPush(pContext, pCb);231bRes = TRUE;232}233}234}235else236{237TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "WdfMemoryCreateFromLookaside failed: 0x%x\n", status);238}239
240return bRes;241}
242
243static
244PVIOSOCK_RX_CB
245VIOSockRxCbEntryForRequest(246IN PDEVICE_CONTEXT pContext,247IN WDFREQUEST Request,248IN ULONG Length
249)
250{
251NTSTATUS status;252WDF_OBJECT_ATTRIBUTES attributes;253PVIOSOCK_RX_CB pCb = NULL;254
255ASSERT(Request != WDF_NO_HANDLE);256
257TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);258
259WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(260&attributes,261VIOSOCK_RX_CB
262);263status = WdfObjectAllocateContext(264Request,265&attributes,266&pCb267);268if (!NT_SUCCESS(status))269{270TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfObjectAllocateContext failed: 0x%x\n", status);271return NULL;272}273
274pCb->Memory = WDF_NO_HANDLE;275pCb->BufferPA.QuadPart = 0;276InitializeListHead(&pCb->ListEntry);277
278status = WdfRequestRetrieveInputBuffer(Request, 0, &pCb->BufferVA, NULL);279if (NT_SUCCESS(status))280{281pCb->Request = Request;282pCb->BytesToRead = pCb->DataLen = (ULONG)Length;283pCb->ReadPtr = pCb->BufferVA;284}285else286{287TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfRequestRetrieveOutputBuffer failed: 0x%x\n", status);288pCb = NULL;289}290
291return pCb;292}
293
294static
295BOOLEAN
296VIOSockRxCbInit(297IN PDEVICE_CONTEXT pContext
298)
299{
300ULONG i;301BOOLEAN bRes = TRUE;302WDF_OBJECT_ATTRIBUTES lockAttributes, memAttributes;303NTSTATUS status;304
305PAGED_CODE();306
307TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);308
309pContext->RxCbBuffersNum = VIOSOCK_CB_ENTRIES(pContext->RxPktNum);310
311if (pContext->RxCbBuffersNum < pContext->RxPktNum)312pContext->RxCbBuffersNum = pContext->RxPktNum;313
314WDF_OBJECT_ATTRIBUTES_INIT(&memAttributes);315memAttributes.ParentObject = pContext->ThisDevice;316
317status = WdfLookasideListCreate(&memAttributes,318sizeof(VIOSOCK_RX_CB), NonPagedPoolNx,319&memAttributes, VIOSOCK_DRIVER_MEMORY_TAG,320&pContext->RxCbBufferMemoryList);321
322if (!NT_SUCCESS(status))323{324TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,325"WdfLookasideListCreate failed: 0x%x\n", status);326return FALSE;327}328
329TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ,330"Initialize chained buffer with %u entries\n", pContext->RxCbBuffersNum);331
332pContext->RxCbBuffers.Next = NULL;333
334for (i = 0; i < pContext->RxCbBuffersNum; ++i)335{336if (!VIOSockRxCbAdd(pContext))337{338TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,339"VIOSockRxCbAdd failed, cleanup chained buffer\n");340bRes = FALSE;341break;342}343}344
345if (!bRes)346VIOSockRxCbCleanup(pContext);347
348return bRes;349}
350
351static
352VOID
353VIOSockRxCbCleanup(354IN PDEVICE_CONTEXT pContext
355)
356{
357PVIOSOCK_RX_CB pCb;358
359PAGED_CODE();360
361TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);362
363//no need to lock364while (pCb = VIOSockRxCbPop(pContext))365{366VIOSockRxCbFree(pContext, pCb);367}368
369}
370
371//////////////////////////////////////////////////////////////////////////
372__inline
373VOID
374VIOSockRxPktCleanup(375IN PDEVICE_CONTEXT pContext,376IN PVIOSOCK_RX_PKT pPkt
377)
378{
379ASSERT(pPkt);380
381if (pPkt->Buffer)382{383VIOSockRxCbPush(pContext, pPkt->Buffer);384pPkt->Buffer = NULL;385}386}
387
388C_ASSERT((VIOSOCK_DMA_RX_PAGES + 1) == 2);389
390#define VIOSOCK_PKT_PA_FROM_VA(ctx,pva) (((ctx)->RxPktPA.QuadPart) + (ULONGLONG)((PCHAR)(pva) - (PCHAR)((ctx)->RxPktVA)))391
392_Requires_lock_held_(pContext->RxLock)393static
394BOOLEAN
395VIOSockRxPktInsert(396IN PDEVICE_CONTEXT pContext,397IN PVIOSOCK_RX_PKT pPkt
398)
399{
400BOOLEAN bRes = TRUE;401VIOSOCK_SG_DESC sg[VIOSOCK_DMA_RX_PAGES + 1];402PHYSICAL_ADDRESS pPKtPA;403int ret;404
405TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);406
407if (!pPkt->Buffer)408{409pPkt->Buffer = VIOSockRxCbPop(pContext);410if (!pPkt->Buffer)411{412TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "VIOSockRxCbPop returns NULL\n");413return FALSE;414}415}416
417ASSERT(pPkt->Buffer->Request == WDF_NO_HANDLE);418
419pPKtPA.QuadPart = VIOSOCK_PKT_PA_FROM_VA(pContext, pPkt);420
421sg[0].length = sizeof(VIRTIO_VSOCK_HDR);422sg[0].physAddr.QuadPart = pPKtPA.QuadPart + FIELD_OFFSET(VIOSOCK_RX_PKT, Header);423
424sg[1].length = VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE;425sg[1].physAddr.QuadPart = pPkt->Buffer->BufferPA.QuadPart;426
427ret = virtqueue_add_buf(pContext->RxVq, sg, 0, 2, pPkt, &pPkt->IndirectDescs,428pPKtPA.QuadPart + FIELD_OFFSET(VIOSOCK_RX_PKT, IndirectDescs));429
430ASSERT(ret >= 0);431if (ret < 0)432{433TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,434"Error adding buffer to Rx queue (ret = %d)\n", ret);435VIOSockRxPktCleanup(pContext, pPkt);436return FALSE;437}438
439TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);440return TRUE;441}
442
443_Requires_lock_held_(pContext->RxLock)444__inline
445VOID
446VIOSockRxPktListProcess(447IN PDEVICE_CONTEXT pContext
448)
449{
450PSINGLE_LIST_ENTRY pListEntry;451
452while (pListEntry = PopEntryList(&pContext->RxPktList))453{454PVIOSOCK_RX_PKT pPkt = CONTAINING_RECORD(pListEntry, VIOSOCK_RX_PKT, RxPktListEntry);455
456if (!VIOSockRxPktInsert(pContext, pPkt))457{458PushEntryList(&pContext->RxPktList, &pPkt->RxPktListEntry);459break;460}461}462}
463
464_Requires_lock_not_held_(pContext->RxLock)465__inline
466VOID
467VIOSockRxPktListProcessLocked(468IN PDEVICE_CONTEXT pContext
469)
470{
471WdfSpinLockAcquire(pContext->RxLock);472VIOSockRxPktListProcess(pContext);473bool bNotify = virtqueue_kick_prepare(pContext->RxVq);474WdfSpinLockRelease(pContext->RxLock);475
476if (bNotify)477virtqueue_notify(pContext->RxVq);478}
479
480_Requires_lock_not_held_(pContext->RxLock)481__inline
482VOID
483VIOSockRxPktInsertOrPostpone(484IN PDEVICE_CONTEXT pContext,485IN PVIOSOCK_RX_PKT pPkt
486)
487{
488bool bNotify = false;489WdfSpinLockAcquire(pContext->RxLock);490if (!VIOSockRxPktInsert(pContext, pPkt))491{492//postpone packet493TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Postpone packet\n");494PushEntryList(&pContext->RxPktList, &pPkt->RxPktListEntry);495}496else497{498VIOSockRxPktListProcess(pContext);499bNotify = virtqueue_kick_prepare(pContext->RxVq);500}501WdfSpinLockRelease(pContext->RxLock);502
503if (bNotify)504virtqueue_notify(pContext->RxVq);505}
506
507_Requires_lock_held_(pSocket->RxLock)508__inline
509BOOLEAN
510VIOSockRxPktInc(511IN PSOCKET_CONTEXT pSocket,512IN ULONG uPktLen
513)
514{
515if (pSocket->RxBytes + uPktLen > pSocket->buf_alloc)516return FALSE;517
518pSocket->RxBytes += uPktLen;519return TRUE;520
521}
522
523_Requires_lock_held_(pSocket->RxLock)524__inline
525VOID
526VIOSockRxPktDec(527IN PSOCKET_CONTEXT pSocket,528IN ULONG uPktLen
529)
530{
531pSocket->RxBytes -= uPktLen;532pSocket->fwd_cnt += uPktLen;533}
534
535_Requires_lock_not_held_(pContext->RxLock)536VOID
537VIOSockRxVqCleanup(538IN PDEVICE_CONTEXT pContext
539)
540{
541PVIOSOCK_RX_PKT pPkt;542PSINGLE_LIST_ENTRY pListEntry;543
544TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);545
546ASSERT(pContext->RxVq && pContext->RxPktVA);547
548//drain queue549WdfSpinLockAcquire(pContext->RxLock);550while (pPkt = (PVIOSOCK_RX_PKT)virtqueue_detach_unused_buf(pContext->RxVq))551{552VIOSockRxPktCleanup(pContext, pPkt);553}554
555while (pListEntry = PopEntryList(&pContext->RxPktList))556{557VIOSockRxPktCleanup(pContext, CONTAINING_RECORD(pListEntry, VIOSOCK_RX_PKT, RxPktListEntry));558}559WdfSpinLockRelease(pContext->RxLock);560
561VIOSockRxCbCleanup(pContext);562
563if (pContext->RxPktVA)564{565VirtIOWdfDeviceFreeDmaMemory(&pContext->VDevice.VIODevice, pContext->RxPktVA);566pContext->RxPktVA = NULL;567}568
569pContext->RxVq = NULL;570TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);571}
572
573NTSTATUS
574VIOSockRxVqInit(575IN PDEVICE_CONTEXT pContext
576)
577{
578NTSTATUS status = STATUS_SUCCESS;579USHORT uNumEntries;580ULONG uRingSize, uHeapSize, uBufferSize;581
582TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);583
584pContext->RxPktList.Next = NULL;585
586status = virtio_query_queue_allocation(&pContext->VDevice.VIODevice, VIOSOCK_VQ_RX,587&uNumEntries, &uRingSize, &uHeapSize);588if (!NT_SUCCESS(status))589{590TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "virtio_query_queue_allocation(VIOSOCK_VQ_RX) failed\n");591pContext->RxVq = NULL;592return status;593}594
595pContext->RxPktNum = uNumEntries;596
597uBufferSize = sizeof(VIOSOCK_RX_PKT) * uNumEntries;598
599TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "Allocating common buffer of %u bytes for %u Rx packets\n",600uBufferSize, uNumEntries);601
602pContext->RxPktVA = (PVIOSOCK_RX_PKT)VirtIOWdfDeviceAllocDmaMemory(&pContext->VDevice.VIODevice,603uBufferSize, VIOSOCK_DRIVER_MEMORY_TAG);604
605ASSERT(pContext->RxPktVA);606if (!pContext->RxPktVA)607{608TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,609"VirtIOWdfDeviceAllocDmaMemory(%u bytes for RxPackets) failed\n", uBufferSize);610status = STATUS_INSUFFICIENT_RESOURCES;611}612else if (VIOSockRxCbInit(pContext))613{614ULONG i;615PVIOSOCK_RX_PKT RxPktVA = (PVIOSOCK_RX_PKT)pContext->RxPktVA;616
617pContext->RxPktPA = VirtIOWdfDeviceGetPhysicalAddress(&pContext->VDevice.VIODevice, pContext->RxPktVA);618ASSERT(pContext->RxPktPA.QuadPart);619
620//fill queue, no lock621for (i = 0; i < uNumEntries; i++)622{623if (!VIOSockRxPktInsert(pContext, &RxPktVA[i]))624{625TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "VIOSockRxPktInsert[%u] failed\n", i);626status = STATUS_UNSUCCESSFUL;627break;628}629}630}631else632{633TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "VIOSockRxCbInit failed\n");634status = STATUS_UNSUCCESSFUL;635}636
637if (!NT_SUCCESS(status))638{639VIOSockRxVqCleanup(pContext);640}641
642return status;643}
644
645_Requires_lock_not_held_(pSocket->StateLock)646static
647VOID
648VIOSockRxPktHandleConnecting(649IN PSOCKET_CONTEXT pSocket,650IN PVIOSOCK_RX_PKT pPkt,651IN BOOLEAN bTxHasSpace
652)
653{
654WDFREQUEST PendedRequest;655NTSTATUS status;656
657TraceEvents(TRACE_LEVEL_VERBOSE, DBG_SOCKET, "--> %s\n", __FUNCTION__);658
659status = VIOSockPendedRequestGetLocked(pSocket, &PendedRequest);660
661if (NT_SUCCESS(status))662{663if (PendedRequest == WDF_NO_HANDLE &&664!VIOSockIsNonBlocking(pSocket))665{666TraceEvents(TRACE_LEVEL_ERROR, DBG_SOCKET, "No PendedRequest found\n");667status = STATUS_CANCELLED;668}669else670{671switch (pPkt->Header.op)672{673case VIRTIO_VSOCK_OP_RESPONSE:674WdfSpinLockAcquire(pSocket->StateLock);675VIOSockStateSet(pSocket, VIOSOCK_STATE_CONNECTED);676VIOSockEventSetBit(pSocket, FD_CONNECT_BIT, STATUS_SUCCESS);677if (bTxHasSpace)678VIOSockEventSetBit(pSocket, FD_WRITE_BIT, STATUS_SUCCESS);679WdfSpinLockRelease(pSocket->StateLock);680status = STATUS_SUCCESS;681break;682case VIRTIO_VSOCK_OP_INVALID:683if (PendedRequest != WDF_NO_HANDLE)684{685status = VIOSockPendedRequestSetResumeLocked(pSocket, PendedRequest);686if (NT_SUCCESS(status))687PendedRequest = WDF_NO_HANDLE;688}689break;690case VIRTIO_VSOCK_OP_RST:691status = STATUS_CONNECTION_REFUSED;692break;693default:694status = STATUS_CONNECTION_INVALID;695}696}697}698
699if (!NT_SUCCESS(status))700{701WdfSpinLockAcquire(pSocket->StateLock);702VIOSockEventSetBit(pSocket, FD_CONNECT_BIT, status);703VIOSockStateSet(pSocket, VIOSOCK_STATE_CLOSE);704WdfSpinLockRelease(pSocket->StateLock);705if (pPkt->Header.op != VIRTIO_VSOCK_OP_RST)706VIOSockSendReset(pSocket, TRUE);707}708
709if (PendedRequest)710{711WdfRequestComplete(PendedRequest, status);712}713
714TraceEvents(TRACE_LEVEL_VERBOSE, DBG_SOCKET, "<-- %s\n", __FUNCTION__);715}
716
717_Requires_lock_not_held_(pSocket->RxLock)718static
719BOOLEAN
720VIOSockRxPktEnqueueCb(721IN PSOCKET_CONTEXT pSocket,722IN PVIOSOCK_RX_PKT pPkt
723)
724{
725PDEVICE_CONTEXT pContext = GetDeviceContextFromSocket(pSocket);726PVIOSOCK_RX_CB pCurrentCb = NULL;727ULONG BufferFree, PktLen;728BOOLEAN bRes = FALSE;729
730TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);731
732ASSERT(pPkt && pPkt->Buffer && pPkt->Header.len);733
734PktLen = pPkt->Header.len;735
736//Merge buffers737WdfSpinLockAcquire(pSocket->RxLock);738if (!IsListEmpty(&pSocket->RxCbList) && PktLen <= VIOSOCK_BYTES_TO_MERGE)739{740pCurrentCb = CONTAINING_RECORD(pSocket->RxCbList.Blink, VIOSOCK_RX_CB, ListEntry);741
742BufferFree = VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE - pCurrentCb->DataLen;743
744if (BufferFree >= PktLen)745{746if (VIOSockRxPktInc(pSocket, PktLen))747{748TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "RxCb merged: %d + %d bytes\n", pCurrentCb->DataLen, PktLen);749memcpy((PCHAR)pCurrentCb->BufferVA + pCurrentCb->DataLen, pPkt->Buffer->BufferVA, PktLen);750pCurrentCb->DataLen += PktLen;751pCurrentCb->BytesToRead += PktLen;752}753else754{755TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Rx buffer full, drop packet\n");756}757//just leave buffer with pkt758WdfSpinLockRelease(pSocket->RxLock);759return FALSE;760}761}762else763{764bRes = TRUE; //Scan read queue765}766
767//Enqueue buffer768if (VIOSockRxPktInc(pSocket, PktLen))769{770pPkt->Buffer->DataLen = PktLen;771pPkt->Buffer->BytesToRead = PktLen;772pPkt->Buffer->ReadPtr = pPkt->Buffer->BufferVA;773InsertTailList(&pSocket->RxCbList, &pPkt->Buffer->ListEntry);774pSocket->RxBuffers++;775pPkt->Buffer = NULL; //remove buffer from pkt776TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "RxCb enqueued: %d bytes\n", PktLen);777}778else779{780TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Rx buffer full, drop packet\n");781bRes = FALSE;782}783
784WdfSpinLockRelease(pSocket->RxLock);785
786TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s\n", __FUNCTION__);787return bRes;788}
789
790static
791VOID
792VIOSockRxRequestCancelCb(793IN WDFREQUEST Request
794)
795{
796PSOCKET_CONTEXT pSocket = GetSocketContextFromRequest(Request);797PVIOSOCK_RX_CB pCb = GetRequestRxCb(Request);798NTSTATUS status = STATUS_CANCELLED;;799
800TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);801
802WdfSpinLockAcquire(pSocket->RxLock);803VIOSockRxPktDec(pSocket, pCb->BytesToRead);804RemoveEntryList(&pCb->ListEntry);805WdfSpinLockRelease(pSocket->RxLock);806
807if (pCb->BytesToRead != pCb->DataLen)808status = STATUS_SUCCESS;809
810WdfRequestCompleteWithInformation(Request, status, pCb->DataLen - pCb->BytesToRead);811}
812
813_Requires_lock_not_held_(pSocket->RxLock)814NTSTATUS
815VIOSockRxRequestEnqueueCb(816IN PSOCKET_CONTEXT pSocket,817IN WDFREQUEST Request,818IN ULONG Length
819)
820{
821PDEVICE_CONTEXT pContext = GetDeviceContextFromSocket(pSocket);822PVIOSOCK_RX_CB pCurrentCb = NULL;823ULONG BufferFree;824NTSTATUS status;825
826TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);827
828pCurrentCb = VIOSockRxCbEntryForRequest(pContext, Request, Length);829if (!pCurrentCb)830return STATUS_INSUFFICIENT_RESOURCES;831
832WdfSpinLockAcquire(pSocket->RxLock);833
834//Enqueue buffer835if (VIOSockRxPktInc(pSocket, pCurrentCb->DataLen))836{837status = WdfRequestMarkCancelableEx(Request, VIOSockRxRequestCancelCb);838if (NT_SUCCESS(status))839{840InsertTailList(&pSocket->RxCbList, &pCurrentCb->ListEntry);841pSocket->RxBuffers++;842status = STATUS_SUCCESS;843}844else845{846ASSERT(status == STATUS_CANCELLED);847TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Loopback request canceled: 0x%x\n", status);848}849}850else851{852TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Rx buffer full, drop packet\n");853status = STATUS_BUFFER_TOO_SMALL;854}855
856WdfSpinLockRelease(pSocket->RxLock);857
858return status;859}
860
861static
862VOID
863VIOSockRxPktHandleConnected(864IN PSOCKET_CONTEXT pSocket,865IN PVIOSOCK_RX_PKT pPkt,866IN BOOLEAN bTxHasSpace
867)
868{
869TraceEvents(TRACE_LEVEL_VERBOSE, DBG_SOCKET, "--> %s\n", __FUNCTION__);870
871switch (pPkt->Header.op)872{873case VIRTIO_VSOCK_OP_RW:874if (VIOSockRxPktEnqueueCb(pSocket, pPkt))875{876VIOSockEventSetBitLocked(pSocket, FD_READ_BIT, STATUS_SUCCESS);877VIOSockReadProcessDequeueCb(pSocket);878}879break;880case VIRTIO_VSOCK_OP_CREDIT_UPDATE:881if (bTxHasSpace)882{883VIOSockEventSetBitLocked(pSocket, FD_WRITE_BIT, STATUS_SUCCESS);884}885break;886case VIRTIO_VSOCK_OP_SHUTDOWN:887if (VIOSockShutdownFromPeer(pSocket,888pPkt->Header.flags & VIRTIO_VSOCK_SHUTDOWN_MASK) &&889!VIOSockRxHasData(pSocket) &&890!VIOSockIsDone(pSocket))891{892VIOSockSendReset(pSocket, FALSE);893VIOSockDoClose(pSocket);894}895break;896case VIRTIO_VSOCK_OP_RST:897VIOSockDoClose(pSocket);898VIOSockEventSetBitLocked(pSocket, FD_CLOSE_BIT, STATUS_CONNECTION_RESET);899break;900}901
902TraceEvents(TRACE_LEVEL_VERBOSE, DBG_SOCKET, "<-- %s\n", __FUNCTION__);903}
904
905static
906VOID
907VIOSockRxPktHandleDisconnecting(908IN PSOCKET_CONTEXT pSocket,909IN PVIOSOCK_RX_PKT pPkt
910)
911{
912TraceEvents(TRACE_LEVEL_VERBOSE, DBG_SOCKET, "--> %s\n", __FUNCTION__);913
914if (pPkt->Header.op == VIRTIO_VSOCK_OP_RST)915{916VIOSockDoClose(pSocket);917
918// if (pSocket->PeerShutdown & VIRTIO_VSOCK_SHUTDOWN_MASK != VIRTIO_VSOCK_SHUTDOWN_MASK)
919// {
920// VIOSockEventSetBit(pSocket, FD_CLOSE_BIT, STATUS_CONNECTION_RESET);
921// }
922}923}
924
925static
926VOID
927VIOSockRxPktHandleListen(928IN PSOCKET_CONTEXT pSocket,929IN PVIOSOCK_RX_PKT pPkt
930)
931{
932PDEVICE_CONTEXT pContext = GetDeviceContextFromSocket(pSocket);933NTSTATUS status;934
935TraceEvents(TRACE_LEVEL_VERBOSE, DBG_SOCKET, "--> %s\n", __FUNCTION__);936
937if (pPkt->Header.op == VIRTIO_VSOCK_OP_RST)938{939//remove pended accept940VIOSockAcceptRemovePkt(pSocket, &pPkt->Header);941return;942}943else if (pPkt->Header.op != VIRTIO_VSOCK_OP_REQUEST)944{945TraceEvents(TRACE_LEVEL_ERROR, DBG_SOCKET, "Invalid packet: %u\n", pPkt->Header.op);946VIOSockSendResetNoSock(pContext, &pPkt->Header);947return;948}949
950status = VIOSockAcceptEnqueuePkt(pSocket, &pPkt->Header);951if (!NT_SUCCESS(status))952{953TraceEvents(TRACE_LEVEL_ERROR, DBG_SOCKET, "VIOSockAcceptEnqueuePkt failed: 0x%x\n", status);954VIOSockSendResetNoSock(pContext, &pPkt->Header);955}956}
957
958_Requires_lock_not_held_(pContext->RxLock)959VOID
960VIOSockRxVqProcess(961IN PDEVICE_CONTEXT pContext
962)
963{
964PVIOSOCK_RX_PKT pPkt;965UINT len;966LIST_ENTRY CompletionList;967PSINGLE_LIST_ENTRY pCurrentEntry;968PSOCKET_CONTEXT pSocket = NULL;969BOOLEAN bStop = FALSE;970
971NTSTATUS status;972
973TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);974
975InitializeListHead(&CompletionList);976
977WdfSpinLockAcquire(pContext->RxLock);978do979{980virtqueue_disable_cb(pContext->RxVq);981
982while (TRUE)983{984if (!VIOSockTxMoreReplies(pContext)) {985/* Stop rx until the device processes already986* pending replies. Leave rx virtqueue
987* callbacks disabled.
988*/
989TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "Stop rx\n");990
991bStop = TRUE;992break;993}994
995pPkt = (PVIOSOCK_RX_PKT)virtqueue_get_buf(pContext->RxVq, &len);996if (!pPkt)997break;998
999/* Drop short/long packets */1000
1001if (len < sizeof(pPkt->Header) ||1002len > sizeof(pPkt->Header) + pPkt->Header.len)1003{1004TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "Short/Long packet (%d)\n", len);1005VIOSockRxPktInsert(pContext, pPkt);1006continue;1007}1008
1009ASSERT(pPkt->Header.len == len - sizeof(pPkt->Header));1010
1011TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "Recv packet %!Op! (%d:%d <-- %d:%d), len: %d, flags: %d, buf_alloc: %d, fwd_cnt: %d\n",1012pPkt->Header.op,1013(ULONG)pPkt->Header.dst_cid, pPkt->Header.dst_port,1014(ULONG)pPkt->Header.src_cid, pPkt->Header.src_port,1015pPkt->Header.len, pPkt->Header.flags, pPkt->Header.buf_alloc, pPkt->Header.fwd_cnt);1016
1017//"complete" buffers later1018InsertTailList(&CompletionList, &pPkt->CompletionListEntry);1019}1020} while (!virtqueue_enable_cb(pContext->RxVq) && !bStop);1021
1022WdfSpinLockRelease(pContext->RxLock);1023
1024//complete buffers1025while (!IsListEmpty(&CompletionList))1026{1027BOOLEAN bTxHasSpace;1028
1029pPkt = CONTAINING_RECORD(RemoveHeadList(&CompletionList), VIOSOCK_RX_PKT, CompletionListEntry);1030
1031//find socket1032pSocket = VIOSockConnectedFindByRxPkt(pContext, &pPkt->Header);1033if (!pSocket)1034{1035//no connected socket for incoming packet1036TraceEvents(TRACE_LEVEL_INFORMATION, DBG_SOCKET, "No connected socket found\n");1037pSocket = VIOSockBoundFindByPort(pContext, pPkt->Header.dst_port);1038}1039
1040if (pSocket && pSocket->type != pPkt->Header.type)1041{1042TraceEvents(TRACE_LEVEL_WARNING, DBG_SOCKET, "Invalid socket %d type\n", pSocket->SocketId);1043pSocket = NULL;1044}1045if (!pSocket)1046{1047TraceEvents(TRACE_LEVEL_WARNING, DBG_SOCKET, "Socket for packet is not exists\n");1048VIOSockSendResetNoSock(pContext, &pPkt->Header);1049VIOSockRxPktInsertOrPostpone(pContext, pPkt);1050continue;1051}1052
1053
1054ASSERT(pContext->Config.guest_cid == (ULONG32)pPkt->Header.dst_cid);1055
1056TraceEvents(TRACE_LEVEL_INFORMATION, DBG_SOCKET, "Socket %d found, state %!State!\n",1057pSocket->SocketId, VIOSockStateGet(pSocket));1058
1059bTxHasSpace = !!VIOSockTxSpaceUpdate(pSocket, &pPkt->Header);1060
1061switch (VIOSockStateGet(pSocket))1062{1063case VIOSOCK_STATE_CONNECTING:1064VIOSockRxPktHandleConnecting(pSocket, pPkt, bTxHasSpace);1065break;1066case VIOSOCK_STATE_CONNECTED:1067VIOSockRxPktHandleConnected(pSocket, pPkt, bTxHasSpace);1068break;1069case VIOSOCK_STATE_CLOSING:1070VIOSockRxPktHandleDisconnecting(pSocket, pPkt);1071break;1072case VIOSOCK_STATE_LISTEN:1073VIOSockRxPktHandleListen(pSocket, pPkt);1074break;1075default:1076TraceEvents(TRACE_LEVEL_WARNING, DBG_SOCKET,1077"Invalid socket %d state for Rx packet %d\n", pSocket->SocketId, pPkt->Header.op);1078}1079
1080//reinsert handled packet1081VIOSockRxPktInsertOrPostpone(pContext, pPkt);1082};1083
1084TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);1085}
1086
1087//////////////////////////////////////////////////////////////////////////
1088_Requires_lock_not_held_(pSocket->RxLock)1089static
1090NTSTATUS
1091VIOSockReadForward(1092IN PSOCKET_CONTEXT pSocket,1093IN WDFREQUEST Request,1094IN ULONG Flags
1095)
1096{
1097PVIOSOCK_RX_CONTEXT pRequest;1098WDF_OBJECT_ATTRIBUTES attributes;1099NTSTATUS status;1100BOOLEAN bTimer = FALSE;1101SIZE_T Length;1102
1103TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);1104
1105WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(1106&attributes,1107VIOSOCK_RX_CONTEXT
1108);1109status = WdfObjectAllocateContext(1110Request,1111&attributes,1112&pRequest1113);1114if (!NT_SUCCESS(status))1115{1116TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfObjectAllocateContext failed: 0x%x\n", status);1117return status;1118}1119
1120status = WdfRequestRetrieveOutputBuffer(Request, 0, &pRequest->FreePtr, &Length);1121if (!NT_SUCCESS(status))1122{1123TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfRequestRetrieveOutputBuffer failed: 0x%x\n", status);1124return status;1125}1126
1127
1128pRequest->BufferLen = pRequest->FreeBytes = (ULONG)Length;1129pRequest->Flags = Flags;1130
1131if (!VIOSockIsNonBlocking(pSocket) &&1132pSocket->RecvTimeout != LONG_MAX)1133{1134pRequest->Timeout = WDF_ABS_TIMEOUT_IN_MS(pSocket->RecvTimeout);1135pRequest->Counter = 0;1136
1137WdfSpinLockAcquire(pSocket->RxLock);1138VIOSockTimerStart(&pSocket->ReadTimer, pRequest->Timeout);1139WdfSpinLockRelease(pSocket->RxLock);1140
1141bTimer = TRUE;1142}1143
1144VIOSockEventClearBit(pSocket, FD_READ_BIT);1145
1146status = WdfRequestForwardToIoQueue(Request, pSocket->ReadQueue);1147if (!NT_SUCCESS(status))1148{1149
1150TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfRequestForwardToIoQueue failed: 0x%x\n", status);1151if (bTimer)1152{1153WdfSpinLockAcquire(pSocket->RxLock);1154VIOSockTimerDeref(&pSocket->ReadTimer, TRUE);1155WdfSpinLockRelease(pSocket->RxLock);1156}1157}1158
1159return status;1160}
1161
1162static
1163VOID
1164VIOSockRead(1165IN WDFQUEUE Queue,1166IN WDFREQUEST Request,1167IN size_t Length1168)
1169{
1170PSOCKET_CONTEXT pSocket = GetSocketContextFromRequest(Request);1171NTSTATUS status;1172
1173TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);1174//check Request1175if (VIOSockIsFlag(pSocket, SOCK_CONTROL))1176{1177TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Invalid socket %d for read\n", pSocket->SocketId);1178WdfRequestComplete(Request, STATUS_NOT_SOCKET);1179return;1180}1181
1182status = VIOSockReadForward(pSocket, Request, 0);1183
1184if (!NT_SUCCESS(status))1185WdfRequestComplete(Request, status);1186}
1187
1188NTSTATUS
1189VIOSockReadWithFlags(1190IN WDFREQUEST Request
1191)
1192{
1193PSOCKET_CONTEXT pSocket = GetSocketContextFromRequest(Request);1194NTSTATUS status;1195PVIRTIO_VSOCK_READ_PARAMS pReadParams;1196ULONG Flags = 0;1197
1198TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);1199
1200//validate request1201status = WdfRequestRetrieveInputBuffer(Request, sizeof(*pReadParams), &pReadParams, NULL);1202if (!NT_SUCCESS(status))1203{1204TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,1205"WdfRequestRetrieveInputBuffer failed: 0x%x\n", status);1206}1207else if (pReadParams->Flags & ~(MSG_PEEK | MSG_WAITALL))1208{1209
1210TraceEvents(TRACE_LEVEL_WARNING, DBG_READ,1211"Unsupported flags: 0x%x\n", pReadParams->Flags & ~(MSG_PEEK | MSG_WAITALL));1212status = STATUS_NOT_SUPPORTED;1213}1214else if ((pReadParams->Flags & (MSG_PEEK | MSG_WAITALL)) == (MSG_PEEK | MSG_WAITALL))1215{1216TraceEvents(TRACE_LEVEL_WARNING, DBG_READ,1217"Incompatible flags: 0x%x\n", MSG_PEEK | MSG_WAITALL);1218status = STATUS_NOT_SUPPORTED;1219}1220else1221{1222Flags = pReadParams->Flags;1223}1224
1225if (NT_SUCCESS(status))1226{1227if (NT_SUCCESS(VIOSockReadForward(pSocket, Request, Flags)))1228status = STATUS_PENDING;1229}1230
1231TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s, status: 0x%08x\n", __FUNCTION__, status);1232return status;1233}
1234
1235NTSTATUS
1236VIOSockReadQueueInit(1237IN WDFDEVICE hDevice
1238)
1239{
1240PDEVICE_CONTEXT pContext = GetDeviceContext(hDevice);1241WDF_IO_QUEUE_CONFIG queueConfig;1242NTSTATUS status;1243WDF_OBJECT_ATTRIBUTES attributes;1244
1245PAGED_CODE();1246
1247TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);1248
1249WDF_OBJECT_ATTRIBUTES_INIT(&attributes);1250attributes.ParentObject = pContext->ThisDevice;1251
1252status = WdfSpinLockCreate(1253&attributes,1254&pContext->RxLock1255);1256
1257if (!NT_SUCCESS(status))1258{1259TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfSpinLockCreate failed: 0x%x\n", status);1260return status;1261}1262
1263WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,1264WdfIoQueueDispatchParallel
1265);1266
1267queueConfig.EvtIoRead = VIOSockRead;1268queueConfig.AllowZeroLengthRequests = WdfFalse;1269
1270//1271// By default, Static Driver Verifier (SDV) displays a warning if it1272// doesn't find the EvtIoStop callback on a power-managed queue.1273// The 'assume' below causes SDV to suppress this warning.1274//1275// No need to handle EvtIoStop:1276
1277__analysis_assume(queueConfig.EvtIoStop != 0);1278status = WdfIoQueueCreate(hDevice,1279&queueConfig,1280WDF_NO_OBJECT_ATTRIBUTES,1281&pContext->ReadQueue1282);1283__analysis_assume(queueConfig.EvtIoStop == 0);1284
1285if (!NT_SUCCESS(status))1286{1287TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,1288"WdfIoQueueCreate failed (Read Queue): 0x%x\n", status);1289return status;1290}1291
1292status = WdfDeviceConfigureRequestDispatching(hDevice,1293pContext->ReadQueue,1294WdfRequestTypeRead);1295
1296if (!NT_SUCCESS(status))1297{1298TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,1299"WdfDeviceConfigureRequestDispatching failed (Read Queue): 0x%x\n", status);1300return status;1301}1302
1303return STATUS_SUCCESS;1304}
1305
1306
1307//only loopback buffers have request assigned
1308#define VIOSockIsLoopbackCb(cb) ((cb)->Request != WDF_NO_HANDLE)1309
1310_Requires_lock_not_held_(pSocket->RxLock)1311BOOLEAN
1312VIOSockReadDequeueCb(1313IN PSOCKET_CONTEXT pSocket
1314)
1315{
1316PDEVICE_CONTEXT pContext = GetDeviceContextFromSocket(pSocket);1317WDFREQUEST ReadRequest = WDF_NO_HANDLE;1318NTSTATUS status = STATUS_SUCCESS;1319PVIOSOCK_RX_CB pCurrentCb;1320LIST_ENTRY LoopbackList, *pCurrentItem;1321ULONG FreeSpace;1322BOOLEAN bSetBit, bStop = FALSE, bRequeue = FALSE, bRestart = TRUE, bAlwaysTrue = TRUE, bProcessRxPktList = FALSE;1323PVIOSOCK_RX_CONTEXT pRequest = NULL;1324LONGLONG llTimeout = 0;1325
1326TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);1327
1328if (pSocket->RxProcessingThreadId == (LONG64)PsGetCurrentThreadId())1329{1330TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "Another instance of VIOSockReadDequeueCb already running, stop Cb dequeue\n");1331return FALSE; //one running instance allowed1332}1333
1334InitializeListHead(&LoopbackList);1335
1336status = WdfIoQueueRetrieveNextRequest(pSocket->ReadQueue, &ReadRequest);1337if (!NT_SUCCESS(status))1338{1339TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "The read queue is empty, exiting from VIOSockReadDequeueCb\n");1340return FALSE;1341}1342
1343pRequest = GetRequestRxContext(ReadRequest);1344
1345WdfSpinLockAcquire(pSocket->RxLock);1346
1347if (pRequest->Timeout)1348{1349llTimeout = VIOSockTimerPassed(&pSocket->ReadTimer);1350VIOSockTimerDeref(&pSocket->ReadTimer, TRUE);1351if (llTimeout < pRequest->Timeout)1352llTimeout = pRequest->Timeout - llTimeout;1353else1354llTimeout = VIOSOCK_TIMER_TOLERANCE;1355}1356
1357if (!VIOSockRxHasData(pSocket))1358{1359if (VIOSockIsNonBlocking(pSocket) ||1360VIOSockStateGet(pSocket) != VIOSOCK_STATE_CONNECTED)1361{1362// VIOSockEventClearBit(pSocket, FD_READ_BIT);
1363
1364//complete request1365bStop = TRUE;1366
1367status = VIOSockStateValidate(pSocket, FALSE);1368
1369if (NT_SUCCESS(status))1370{1371if (VIOSockIsNonBlocking(pSocket))1372{1373status = STATUS_CANT_WAIT;1374}1375else1376{1377ASSERT(FALSE);1378bRequeue = TRUE;1379}1380}1381else if (status == STATUS_REMOTE_DISCONNECT)1382status = STATUS_SUCCESS; //return zero bytes on peer shutdown1383}1384else1385{1386// The socket is connected, however, it might1387// receive a shutdown notification from the other end.1388// In such a case, we need to complete the recv request with success,1389// possibly reporting zero data received. If the shutdown is here,1390// the request must not be requeued since that may cause it being1391// in the queue forever.1392status = VIOSockStateValidate(pSocket, FALSE);1393if (!NT_SUCCESS(status))1394{1395bStop = TRUE;1396if (status == STATUS_LOCAL_DISCONNECT ||1397status == STATUS_REMOTE_DISCONNECT)1398status = STATUS_SUCCESS;1399}1400else bRequeue = TRUE;1401}1402}1403
1404if (bRequeue)1405{1406bStop = TRUE;1407if (llTimeout && llTimeout <= VIOSOCK_TIMER_TOLERANCE)1408{1409status = STATUS_IO_TIMEOUT;1410}1411else1412{1413pSocket->RxProcessingThreadId = (LONG64)PsGetCurrentThreadId();1414status = WdfRequestRequeue(ReadRequest);1415pSocket->RxProcessingThreadId = INVALID_THREAD_ID;1416if (NT_SUCCESS(status))1417{1418//continue timer1419if (llTimeout)1420VIOSockTimerStart(&pSocket->ReadTimer, llTimeout);1421ReadRequest = WDF_NO_HANDLE;1422status = STATUS_SUCCESS;1423}1424}1425}1426
1427if (bStop)1428{1429WdfSpinLockRelease(pSocket->RxLock);1430
1431if (ReadRequest != WDF_NO_HANDLE)1432{1433TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "Complete request without CB dequeue: 0x%08x\n", status);1434WdfRequestCompleteWithInformation(ReadRequest, status, pRequest->BufferLen - pRequest->FreeBytes);1435}1436else1437{1438TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "No read request to complete\n");1439}1440return FALSE;1441}1442
1443//process chained buffer1444for (pCurrentItem = pSocket->RxCbList.Flink;1445pCurrentItem != &pSocket->RxCbList;1446pCurrentItem = pCurrentItem->Flink)1447{1448//peek the first buffer1449pCurrentCb = CONTAINING_RECORD(pCurrentItem, VIOSOCK_RX_CB, ListEntry);1450
1451//can we copy the whole CB?1452if (pRequest->FreeBytes >= pCurrentCb->BytesToRead)1453{1454memcpy(pRequest->FreePtr, pCurrentCb->ReadPtr, pCurrentCb->BytesToRead);1455
1456//update request buffer data ptr1457pRequest->FreePtr += pCurrentCb->BytesToRead;1458pRequest->FreeBytes -= pCurrentCb->BytesToRead;1459
1460if (!(pRequest->Flags & MSG_PEEK))1461{1462ULONG uBytesToRead = pCurrentCb->BytesToRead;1463pCurrentCb->BytesToRead = 0;1464pCurrentItem = pCurrentItem->Blink;1465RemoveEntryList(&pCurrentCb->ListEntry);1466
1467if (VIOSockIsLoopbackCb(pCurrentCb))1468{1469status = WdfRequestUnmarkCancelable(pCurrentCb->Request);1470
1471if (NT_SUCCESS(status))1472{1473InsertTailList(&LoopbackList, &pCurrentCb->ListEntry); //complete loopback requests later1474VIOSockRxPktDec(pSocket, uBytesToRead);1475}1476else1477{1478ASSERT(status == STATUS_CANCELLED);1479TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Loopback request is canceling: 0x%x\n", status);1480status = STATUS_SUCCESS;1481InitializeListHead(&pCurrentCb->ListEntry);//cancellation routine removes element from the list1482}1483}1484else1485{1486VIOSockRxCbPushLocked(pContext, pCurrentCb);1487VIOSockRxPktDec(pSocket, uBytesToRead);1488bProcessRxPktList = TRUE;1489}1490}1491}1492else //request buffer is not big enough1493{1494memcpy(pRequest->FreePtr, pCurrentCb->ReadPtr, pRequest->FreeBytes);1495
1496if (!(pRequest->Flags & MSG_PEEK))1497{1498//update current CB data ptr1499pCurrentCb->ReadPtr += pRequest->FreeBytes;1500pCurrentCb->BytesToRead -= pRequest->FreeBytes;1501VIOSockRxPktDec(pSocket, pRequest->FreeBytes);1502}1503pRequest->FreeBytes = 0;1504
1505break;1506}1507}1508
1509if (bProcessRxPktList)1510{1511VIOSockRxPktListProcessLocked(pContext);1512}1513
1514if (!pRequest->FreeBytes || pRequest->Flags & MSG_PEEK)1515{1516TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Should complete read request\n");1517}1518else if (pRequest->BufferLen == pRequest->FreeBytes || pRequest->Flags & MSG_WAITALL)1519{1520if (llTimeout && llTimeout <= VIOSOCK_TIMER_TOLERANCE)1521{1522status = STATUS_IO_TIMEOUT;1523}1524else1525{1526//requeue request1527pSocket->RxProcessingThreadId = (LONG64)PsGetCurrentThreadId();1528status = WdfRequestRequeue(ReadRequest);1529pSocket->RxProcessingThreadId = INVALID_THREAD_ID;1530if (NT_SUCCESS(status))1531{1532//continue timer1533if (llTimeout)1534VIOSockTimerStart(&pSocket->ReadTimer, llTimeout);1535ReadRequest = WDF_NO_HANDLE;1536status = STATUS_SUCCESS;1537bRestart = FALSE;1538}1539}1540}1541
1542bSetBit = (ReadRequest != WDF_NO_HANDLE && VIOSockRxHasData(pSocket));1543FreeSpace = pSocket->buf_alloc - (pSocket->fwd_cnt - pSocket->last_fwd_cnt);1544
1545WdfSpinLockRelease(pSocket->RxLock);1546
1547if (bSetBit)1548VIOSockEventSetBit(pSocket, FD_READ_BIT, STATUS_SUCCESS);1549else1550VIOSockEventClearBit(pSocket, FD_READ_BIT);1551
1552if (FreeSpace < VIRTIO_VSOCK_MAX_PKT_BUF_SIZE)1553VIOSockSendCreditUpdate(pSocket);1554
1555if (ReadRequest != WDF_NO_HANDLE)1556{1557ASSERT(pSocket->PendedRequest == WDF_NO_HANDLE);1558
1559WdfRequestCompleteWithInformation(ReadRequest, status, pRequest->BufferLen - pRequest->FreeBytes);1560}1561
1562//Static Driver Verifier(SDV) tracks only one request, and when SDV unwinds the loop,1563//it treats completion of the second request as another completion of the first request.1564//The 'assume' below causes SDV to skip loop analysis.1565
1566__analysis_assume(bAlwaysTrue == FALSE);1567
1568//complete loopback requests (succeed and canceled)1569if (bAlwaysTrue)1570{1571while (!IsListEmpty(&LoopbackList))1572{1573pCurrentCb = CONTAINING_RECORD(RemoveHeadList(&LoopbackList), VIOSOCK_RX_CB, ListEntry);1574
1575//NOTE! SDV thinks we are completing INVALID request marked as cancelable,1576//but request is appeared in this list only if WdfRequestMarkCancelableEx failed1577WdfRequestCompleteWithInformation(pCurrentCb->Request,1578pCurrentCb->DataLen ? STATUS_SUCCESS : STATUS_CANCELLED,1579pCurrentCb->DataLen);1580}1581}1582
1583TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s\n", __FUNCTION__);1584
1585return bRestart;1586}
1587
1588_Requires_lock_not_held_(pSocket->RxLock)1589VOID
1590VIOSockReadCleanupCb(1591IN PSOCKET_CONTEXT pSocket
1592)
1593{
1594PDEVICE_CONTEXT pContext = GetDeviceContextFromSocket(pSocket);1595PVIOSOCK_RX_CB pCurrentCb;1596LIST_ENTRY LoopbackList;1597BOOLEAN bAlwaysTrue = TRUE;1598
1599TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);1600
1601InitializeListHead(&LoopbackList);1602
1603WdfSpinLockAcquire(pSocket->RxLock);1604
1605//process chained buffer1606while (!IsListEmpty(&pSocket->RxCbList))1607{1608//peek the first buffer1609pCurrentCb = CONTAINING_RECORD(RemoveHeadList(&pSocket->RxCbList), VIOSOCK_RX_CB, ListEntry);1610
1611if (VIOSockIsLoopbackCb(pCurrentCb))1612{1613NTSTATUS status = WdfRequestUnmarkCancelable(pCurrentCb->Request);1614if (!NT_SUCCESS(status))1615{1616ASSERT(status == STATUS_CANCELLED);1617TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Loopback request canceled\n");1618InitializeListHead(&pCurrentCb->ListEntry);//cancellation routine removes element from the list1619}1620else1621InsertTailList(&LoopbackList, &pCurrentCb->ListEntry); //complete loopback requests later1622}1623else1624VIOSockRxCbPushLocked(pContext, pCurrentCb);1625}1626
1627WdfSpinLockRelease(pSocket->RxLock);1628
1629VIOSockRxPktListProcessLocked(pContext);1630
1631//Static Driver Verifier(SDV) tracks only one request, and when SDV unwinds the loop,1632//it treats completion of the second request as another completion of the first request.1633//The 'assume' below causes SDV to skip loop analysis.1634__analysis_assume(bAlwaysTrue == FALSE);1635
1636//complete loopback1637if (bAlwaysTrue)1638{1639while (!IsListEmpty(&LoopbackList))1640{1641pCurrentCb = CONTAINING_RECORD(RemoveHeadList(&LoopbackList), VIOSOCK_RX_CB, ListEntry);1642WdfRequestComplete(pCurrentCb->Request, STATUS_CANCELLED);1643}1644}1645
1646}
1647
1648static
1649VOID
1650VIOSockReadSocketState(1651WDFQUEUE Queue,1652WDFCONTEXT Context
1653)
1654{
1655VIOSockReadProcessDequeueCb((PSOCKET_CONTEXT)Context);1656}
1657
1658VOID
1659VIOSockReadIoCanceledOnQueue(1660IN WDFQUEUE Queue,1661IN WDFREQUEST Request
1662)
1663{
1664PVIOSOCK_RX_CONTEXT pRequest = GetRequestRxContext(Request);1665
1666ASSERT(pRequest);1667if (pRequest->BufferLen != pRequest->FreeBytes)1668WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, pRequest->BufferLen - pRequest->FreeBytes);1669else1670WdfRequestComplete(Request, STATUS_CANCELLED);1671}
1672
1673NTSTATUS
1674VIOSockReadSocketQueueInit(1675IN PSOCKET_CONTEXT pSocket
1676)
1677{
1678WDFDEVICE hDevice = WdfFileObjectGetDevice(pSocket->ThisSocket);1679PDEVICE_CONTEXT pContext = GetDeviceContext(hDevice);1680WDF_OBJECT_ATTRIBUTES queueAttributes;1681WDF_IO_QUEUE_CONFIG queueConfig;1682NTSTATUS status;1683
1684PAGED_CODE();1685
1686TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);1687
1688WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,1689WdfIoQueueDispatchManual
1690);1691
1692queueConfig.EvtIoCanceledOnQueue = VIOSockReadIoCanceledOnQueue;1693queueConfig.AllowZeroLengthRequests = WdfFalse;1694
1695WDF_OBJECT_ATTRIBUTES_INIT(&queueAttributes);1696queueAttributes.ParentObject = pSocket->ThisSocket;1697
1698//1699// By default, Static Driver Verifier (SDV) displays a warning if it1700// doesn't find the EvtIoStop callback on a power-managed queue.1701// The 'assume' below causes SDV to suppress this warning.1702//1703// No need to handle EvtIoStop:1704
1705__analysis_assume(queueConfig.EvtIoStop != 0);1706status = WdfIoQueueCreate(hDevice,1707&queueConfig,1708&queueAttributes,1709&pSocket->ReadQueue1710);1711__analysis_assume(queueConfig.EvtIoStop == 0);1712
1713if (!NT_SUCCESS(status))1714{1715TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,1716"WdfIoQueueCreate failed (Socket Read Queue): 0x%x\n", status);1717return status;1718}1719
1720status = WdfIoQueueReadyNotify(pSocket->ReadQueue, VIOSockReadSocketState, pSocket);1721if (!NT_SUCCESS(status))1722{1723TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,1724"WdfIoQueueReadyNotify failed (Socket Read Queue): 0x%x\n", status);1725return status;1726}1727
1728status = VIOSockTimerCreate(&pSocket->ReadTimer, pSocket->ThisSocket, VIOSockReadTimerFunc);1729if (!NT_SUCCESS(status))1730{1731TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,1732"VIOSockTimerCreate failed (Socket Read Queue): 0x%x\n", status);1733return status;1734}1735
1736return STATUS_SUCCESS;1737}
1738
1739//////////////////////////////////////////////////////////////////////////
1740VOID
1741VIOSockReadTimerFunc(1742WDFTIMER Timer
1743)
1744{
1745static LONG lCounter;1746PSOCKET_CONTEXT pSocket = GetSocketContext(WdfTimerGetParentObject(Timer));1747LONGLONG Timeout = LONGLONG_MAX;1748WDFREQUEST PrevTagRequest = WDF_NO_HANDLE, TagRequest = WDF_NO_HANDLE, Request;1749NTSTATUS status;1750LIST_ENTRY CompletionList;1751PVIOSOCK_RX_CONTEXT pRequest;1752
1753TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> %s\n", __FUNCTION__);1754
1755InterlockedIncrement(&lCounter);1756InitializeListHead(&CompletionList);1757
1758do1759{1760status = WdfIoQueueFindRequest(pSocket->ReadQueue, PrevTagRequest, WDF_NO_HANDLE, NULL, &TagRequest);1761
1762if (PrevTagRequest != WDF_NO_HANDLE)1763{1764WdfObjectDereference(PrevTagRequest);1765}1766
1767if (NT_SUCCESS(status))1768{1769pRequest = GetRequestRxContext(TagRequest);1770
1771WdfSpinLockAcquire(pSocket->RxLock);1772
1773if (pRequest->Timeout && pRequest->Counter < lCounter)1774{1775if (pRequest->Timeout <= pSocket->ReadTimer.Timeout + VIOSOCK_TIMER_TOLERANCE)1776{1777status = WdfIoQueueRetrieveFoundRequest(pSocket->ReadQueue, TagRequest, &Request);1778
1779WdfObjectDereference(TagRequest);1780
1781if (status == STATUS_NOT_FOUND)1782{1783TagRequest = PrevTagRequest = WDF_NO_HANDLE;1784status = STATUS_SUCCESS;1785}1786else if (!NT_SUCCESS(status))1787{1788TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfIoQueueRetrieveFoundRequest failed: 0x%08x\n", status);1789WdfSpinLockRelease(pSocket->RxLock);1790break;1791}1792else1793{1794TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Complete expired queued Rx request %p\n", Request);1795
1796VIOSockTimerDeref(&pSocket->ReadTimer, FALSE);1797InsertTailList(&CompletionList, &pRequest->ListEntry);1798pRequest->ThisRequest = Request;1799}1800}1801else1802{1803pRequest->Counter = lCounter;1804pRequest->Timeout -= pSocket->ReadTimer.Timeout;1805
1806if (pRequest->Timeout < Timeout)1807Timeout = pRequest->Timeout;1808
1809PrevTagRequest = TagRequest;1810}1811}1812
1813WdfSpinLockRelease(pSocket->RxLock);1814}1815else if (status == STATUS_NO_MORE_ENTRIES)1816{1817break;1818}1819else if (status == STATUS_NOT_FOUND)1820{1821TagRequest = PrevTagRequest = WDF_NO_HANDLE;1822status = STATUS_SUCCESS;1823}1824} while (NT_SUCCESS(status));1825
1826if (Timeout != LONGLONG_MAX)1827{1828WdfSpinLockAcquire(pSocket->RxLock);1829VIOSockTimerSet(&pSocket->ReadTimer, Timeout);1830WdfSpinLockRelease(pSocket->RxLock);1831}1832
1833while (!IsListEmpty(&CompletionList))1834{1835pRequest = CONTAINING_RECORD(RemoveHeadList(&CompletionList), VIOSOCK_RX_CONTEXT, ListEntry);1836WdfRequestCompleteWithInformation(pRequest->ThisRequest, STATUS_IO_TIMEOUT, pRequest->BufferLen - pRequest->FreeBytes);1837}1838
1839TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- %s, status: 0x%08x, counter: %u\n", __FUNCTION__, status, lCounter);1840}
1841