mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-10-31 02:05:04 +00:00
762 lines
22 KiB
C
Executable File
762 lines
22 KiB
C
Executable File
/*
|
|
* This file contains balloon driver routines
|
|
*
|
|
* Copyright (c) 2009-2017 Red Hat, Inc.
|
|
*
|
|
* Author(s):
|
|
* Vadim Rozenfeld <[email protected]>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met :
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and / or other materials provided with the distribution.
|
|
* 3. Neither the names of the copyright holders nor the names of their contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
#include "precomp.h"
|
|
|
|
#if defined(EVENT_TRACING)
|
|
#include "Device.tmh"
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, BalloonEvtDeviceContextCleanup)
|
|
#pragma alloc_text(PAGE, BalloonEvtDevicePrepareHardware)
|
|
#pragma alloc_text(PAGE, BalloonEvtDeviceReleaseHardware)
|
|
#pragma alloc_text(PAGE, BalloonEvtDeviceD0Exit)
|
|
#pragma alloc_text(PAGE, BalloonEvtDeviceD0ExitPreInterruptsDisabled)
|
|
#pragma alloc_text(PAGE, BalloonDeviceAdd)
|
|
#pragma alloc_text(PAGE, BalloonCloseWorkerThread)
|
|
#pragma alloc_text(PAGE, BalloonEvtDeviceSurpriseRemoval)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
#define LOMEMEVENTNAME L"\\KernelObjects\\LowMemoryCondition"
|
|
DECLARE_CONST_UNICODE_STRING(evLowMemString, LOMEMEVENTNAME);
|
|
|
|
|
|
NTSTATUS
|
|
BalloonDeviceAdd(
|
|
IN WDFDRIVER Driver,
|
|
IN PWDFDEVICE_INIT DeviceInit)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
WDFDEVICE device;
|
|
PDEVICE_CONTEXT devCtx = NULL;
|
|
WDF_INTERRUPT_CONFIG interruptConfig;
|
|
WDF_OBJECT_ATTRIBUTES attributes;
|
|
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
|
|
#ifdef USE_BALLOON_SERVICE
|
|
WDF_FILEOBJECT_CONFIG fileConfig;
|
|
#endif // USE_BALLOON_SERVICE
|
|
|
|
UNREFERENCED_PARAMETER(Driver);
|
|
PAGED_CODE();
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s\n", __FUNCTION__);
|
|
|
|
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
|
|
|
|
pnpPowerCallbacks.EvtDevicePrepareHardware = BalloonEvtDevicePrepareHardware;
|
|
pnpPowerCallbacks.EvtDeviceReleaseHardware = BalloonEvtDeviceReleaseHardware;
|
|
pnpPowerCallbacks.EvtDeviceD0Entry = BalloonEvtDeviceD0Entry;
|
|
pnpPowerCallbacks.EvtDeviceD0Exit = BalloonEvtDeviceD0Exit;
|
|
pnpPowerCallbacks.EvtDeviceD0ExitPreInterruptsDisabled = BalloonEvtDeviceD0ExitPreInterruptsDisabled;
|
|
pnpPowerCallbacks.EvtDeviceSurpriseRemoval = BalloonEvtDeviceSurpriseRemoval;
|
|
|
|
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
|
|
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT);
|
|
attributes.EvtCleanupCallback = BalloonEvtDeviceContextCleanup;
|
|
|
|
/* The driver initializes all the queues under lock of
|
|
* WDF object of the device. If we use default execution
|
|
* level, this lock is spinlock and common blocks required
|
|
* for queues can't be allocated on DISPATCH. So, we change
|
|
* the execution level to PASSIVE -> the lock is fast mutex
|
|
*/
|
|
attributes.ExecutionLevel = WdfExecutionLevelPassive;
|
|
|
|
#ifdef USE_BALLOON_SERVICE
|
|
attributes.SynchronizationScope = WdfSynchronizationScopeDevice;
|
|
|
|
WDF_FILEOBJECT_CONFIG_INIT(
|
|
&fileConfig,
|
|
WDF_NO_EVENT_CALLBACK,
|
|
BalloonEvtFileClose,
|
|
WDF_NO_EVENT_CALLBACK
|
|
);
|
|
|
|
WdfDeviceInitSetFileObjectConfig(DeviceInit,
|
|
&fileConfig,
|
|
WDF_NO_OBJECT_ATTRIBUTES);
|
|
#endif // USE_BALLOON_SERVICE
|
|
|
|
status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
|
|
"WdfDeviceCreate failed with status 0x%08x\n", status);
|
|
return status;
|
|
}
|
|
|
|
devCtx = GetDeviceContext(device);
|
|
|
|
WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
|
|
BalloonInterruptIsr,
|
|
BalloonInterruptDpc);
|
|
|
|
interruptConfig.EvtInterruptEnable = BalloonInterruptEnable;
|
|
interruptConfig.EvtInterruptDisable = BalloonInterruptDisable;
|
|
|
|
#ifndef USE_BALLOON_SERVICE
|
|
// Serialize the DPC routine with queue operations to prevent races
|
|
// around PendingWriteRequest and HandleWriteRequest.
|
|
interruptConfig.AutomaticSerialization = TRUE;
|
|
#endif // !USE_BALLOON_SERVICE
|
|
|
|
status = WdfInterruptCreate(device,
|
|
&interruptConfig,
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
&devCtx->WdfInterrupt);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
|
|
"WdfInterruptCreate failed: 0x%08x\n", status);
|
|
return status;
|
|
}
|
|
|
|
status = WdfDeviceCreateDeviceInterface(device, &GUID_DEVINTERFACE_BALLOON, NULL);
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
|
|
"WdfDeviceCreateDeviceInterface failed with status 0x%08x\n", status);
|
|
return status;
|
|
}
|
|
|
|
devCtx->SurpriseRemoval = FALSE;
|
|
devCtx->bShutDown = FALSE;
|
|
devCtx->num_pages = 0;
|
|
devCtx->PageListHead.Next = NULL;
|
|
ExInitializeNPagedLookasideList(
|
|
&devCtx->LookAsideList,
|
|
NULL,
|
|
NULL,
|
|
#if !defined(NTDDI_WIN8) || (NTDDI_VERSION < NTDDI_WIN8)
|
|
0,
|
|
#else
|
|
POOL_NX_ALLOCATION,
|
|
#endif
|
|
sizeof(PAGE_LIST_ENTRY),
|
|
BALLOON_MGMT_POOL_TAG,
|
|
0
|
|
);
|
|
devCtx->bListInitialized = TRUE;
|
|
devCtx->pfns_table = NULL;
|
|
devCtx->MemStats = NULL;
|
|
|
|
|
|
KeInitializeEvent(&devCtx->HostAckEvent,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
|
attributes.ParentObject = device;
|
|
|
|
status = WdfSpinLockCreate(
|
|
&attributes,
|
|
&devCtx->StatQueueLock
|
|
);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfSpinLockCreate failed 0x%x\n", status);
|
|
return status;
|
|
}
|
|
|
|
status = WdfSpinLockCreate(
|
|
&attributes,
|
|
&devCtx->InfDefQueueLock
|
|
);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfSpinLockCreate failed 0x%x\n", status);
|
|
return status;
|
|
}
|
|
|
|
#ifdef USE_BALLOON_SERVICE
|
|
status = BalloonQueueInitialize(device);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
|
|
"BalloonQueueInitialize failed with status 0x%08x\n", status);
|
|
return status;
|
|
}
|
|
#else // USE_BALLOON_SERVICE
|
|
status = StatInitializeWorkItem(device);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
|
|
"StatInitializeWorkItem failed with status 0x%08x\n", status);
|
|
return status;
|
|
}
|
|
#endif // USE_BALLOON_SERVICE
|
|
|
|
KeInitializeEvent(&devCtx->WakeUpThread,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
BalloonEvtDeviceContextCleanup(
|
|
IN WDFOBJECT Device
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext((WDFDEVICE)Device);
|
|
|
|
PAGED_CODE();
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
|
|
if(devCtx->bListInitialized)
|
|
{
|
|
ExDeleteNPagedLookasideList(&devCtx->LookAsideList);
|
|
devCtx->bListInitialized = FALSE;
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);
|
|
}
|
|
|
|
NTSTATUS
|
|
BalloonEvtDevicePrepareHardware(
|
|
IN WDFDEVICE Device,
|
|
IN WDFCMRESLIST ResourceList,
|
|
IN WDFCMRESLIST ResourceListTranslated
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_CONTEXT devCtx = NULL;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s\n", __FUNCTION__);
|
|
|
|
UNREFERENCED_PARAMETER(ResourceList);
|
|
|
|
PAGED_CODE();
|
|
|
|
devCtx = GetDeviceContext(Device);
|
|
|
|
status = VirtIOWdfInitialize(
|
|
&devCtx->VDevice,
|
|
Device,
|
|
ResourceListTranslated,
|
|
NULL,
|
|
BALLOON_MGMT_POOL_TAG);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_POWER, "VirtIOWdfInitialize failed with %x\n", status);
|
|
return status;
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
devCtx->MemStats = (PBALLOON_STAT)VirtIOWdfDeviceAllocDmaMemory(&devCtx->VDevice.VIODevice, PAGE_SIZE, BALLOON_MGMT_POOL_TAG);
|
|
}
|
|
|
|
if (devCtx->MemStats)
|
|
{
|
|
RtlFillMemory(devCtx->MemStats, sizeof(BALLOON_STAT) * VIRTIO_BALLOON_S_NR, -1);
|
|
}
|
|
else
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_POWER, "Failed to allocate MemStats block\n");
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* use BALLOON_MGMT_POOL_TAG also for tagging common memory blocks */
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
devCtx->pfns_table = (PPFN_NUMBER)VirtIOWdfDeviceAllocDmaMemory(&devCtx->VDevice.VIODevice, PAGE_SIZE, BALLOON_MGMT_POOL_TAG);
|
|
}
|
|
|
|
if (devCtx->pfns_table == NULL)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "Failed to allocate PFNS_TABLE block\n");
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return status;
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
BalloonEvtDeviceReleaseHardware (
|
|
IN WDFDEVICE Device,
|
|
IN WDFCMRESLIST ResourcesTranslated
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = NULL;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s\n", __FUNCTION__);
|
|
|
|
UNREFERENCED_PARAMETER(ResourcesTranslated);
|
|
|
|
PAGED_CODE();
|
|
|
|
WdfObjectAcquireLock(Device);
|
|
|
|
devCtx = GetDeviceContext(Device);
|
|
|
|
VirtIOWdfDeviceFreeDmaMemoryByTag(&devCtx->VDevice.VIODevice, BALLOON_MGMT_POOL_TAG);
|
|
devCtx->MemStats = NULL;
|
|
devCtx->pfns_table = NULL;
|
|
|
|
WdfObjectReleaseLock(Device);
|
|
|
|
VirtIOWdfShutdown(&devCtx->VDevice);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
BalloonCreateWorkerThread(
|
|
IN WDFDEVICE Device
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(Device);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
HANDLE hThread = 0;
|
|
OBJECT_ATTRIBUTES oa;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
devCtx->bShutDown = FALSE;
|
|
|
|
if(NULL == devCtx->Thread)
|
|
{
|
|
InitializeObjectAttributes(&oa, NULL,
|
|
OBJ_KERNEL_HANDLE, NULL, NULL);
|
|
|
|
status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, &oa, NULL, NULL,
|
|
BalloonRoutine, Device);
|
|
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
|
|
"failed to create worker thread status 0x%08x\n", status);
|
|
return status;
|
|
}
|
|
|
|
ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL,
|
|
KernelMode, (PVOID*)&devCtx->Thread, NULL);
|
|
KeSetPriorityThread(devCtx->Thread, LOW_REALTIME_PRIORITY);
|
|
|
|
ZwClose(hThread);
|
|
}
|
|
|
|
KeSetEvent(&devCtx->WakeUpThread, EVENT_INCREMENT, FALSE);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
BalloonCloseWorkerThread(
|
|
IN WDFDEVICE Device
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(Device);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
|
|
PAGED_CODE();
|
|
|
|
if(NULL != devCtx->Thread)
|
|
{
|
|
devCtx->bShutDown = TRUE;
|
|
KeSetEvent(&devCtx->WakeUpThread, EVENT_INCREMENT, FALSE);
|
|
status = KeWaitForSingleObject(devCtx->Thread, Executive, KernelMode, FALSE, NULL);
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
|
|
"KeWaitForSingleObject didn't succeed status 0x%08x\n", status);
|
|
}
|
|
ObDereferenceObject(devCtx->Thread);
|
|
devCtx->Thread = NULL;
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
BalloonEvtDeviceD0Entry(
|
|
IN WDFDEVICE Device,
|
|
IN WDF_POWER_DEVICE_STATE PreviousState
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(Device);
|
|
|
|
UNREFERENCED_PARAMETER(PreviousState);
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
|
|
status = BalloonInit(Device);
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
|
|
"BalloonInit failed with status 0x%08x\n", status);
|
|
BalloonTerm(Device);
|
|
return status;
|
|
}
|
|
|
|
status = BalloonCreateWorkerThread(Device);
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
|
|
"BalloonCreateWorkerThread failed with status 0x%08x\n", status);
|
|
}
|
|
|
|
devCtx->evLowMem = IoCreateNotificationEvent(
|
|
(PUNICODE_STRING)&evLowMemString, &devCtx->hLowMem);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
BalloonEvtDeviceD0Exit(
|
|
IN WDFDEVICE Device,
|
|
IN WDF_POWER_DEVICE_STATE TargetState
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(Device);
|
|
|
|
UNREFERENCED_PARAMETER(TargetState);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<--> %s\n", __FUNCTION__);
|
|
|
|
PAGED_CODE();
|
|
|
|
if (devCtx->evLowMem)
|
|
{
|
|
ZwClose(devCtx->hLowMem);
|
|
devCtx->evLowMem = NULL;
|
|
}
|
|
|
|
#ifndef USE_BALLOON_SERVICE
|
|
/*
|
|
* interrupts were already disabled (between BalloonEvtDeviceD0ExitPreInterruptsDisabled and this call)
|
|
* we should flush StatWorkItem before calling BalloonTerm which will delete virtio queues
|
|
*/
|
|
WdfWorkItemFlush(devCtx->StatWorkItem);
|
|
#endif // !USE_BALLOON_SERVICE
|
|
|
|
BalloonTerm(Device);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
BalloonEvtDeviceD0ExitPreInterruptsDisabled(
|
|
IN WDFDEVICE Device,
|
|
IN WDF_POWER_DEVICE_STATE TargetState
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(Device);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<--> %s\n", __FUNCTION__);
|
|
|
|
PAGED_CODE();
|
|
|
|
BalloonCloseWorkerThread(Device);
|
|
if (TargetState == WdfPowerDeviceD3Final)
|
|
{
|
|
while(devCtx->num_pages)
|
|
{
|
|
BalloonLeak(Device, devCtx->num_pages);
|
|
}
|
|
|
|
BalloonSetSize(Device, devCtx->num_pages);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
BalloonEvtDeviceSurpriseRemoval(IN WDFDEVICE Device)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(Device);
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<--> %s\n", __FUNCTION__);
|
|
|
|
PAGED_CODE();
|
|
|
|
devCtx->SurpriseRemoval = TRUE;
|
|
KeSetEvent(&devCtx->HostAckEvent, EVENT_INCREMENT, FALSE);
|
|
}
|
|
|
|
BOOLEAN
|
|
BalloonInterruptIsr(
|
|
IN WDFINTERRUPT WdfInterrupt,
|
|
IN ULONG MessageID
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = NULL;
|
|
WDFDEVICE Device;
|
|
|
|
UNREFERENCED_PARAMETER( MessageID );
|
|
|
|
Device = WdfInterruptGetDevice(WdfInterrupt);
|
|
devCtx = GetDeviceContext(Device);
|
|
|
|
if (VirtIOWdfGetISRStatus(&devCtx->VDevice) > 0)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, "--> %s\n", __FUNCTION__);
|
|
WdfInterruptQueueDpcForIsr( WdfInterrupt );
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, "--> %s No Isr indicated\n", __FUNCTION__);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
BalloonInterruptDpc(
|
|
IN WDFINTERRUPT WdfInterrupt,
|
|
IN WDFOBJECT WdfDevice
|
|
)
|
|
{
|
|
unsigned int len;
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(WdfDevice);
|
|
PVOID buffer;
|
|
|
|
BOOLEAN bHostAck = FALSE;
|
|
UNREFERENCED_PARAMETER( WdfInterrupt );
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "--> %s\n", __FUNCTION__);
|
|
|
|
WdfSpinLockAcquire(devCtx->InfDefQueueLock);
|
|
if (virtqueue_get_buf(devCtx->InfVirtQueue, &len))
|
|
{
|
|
bHostAck = TRUE;
|
|
}
|
|
if (virtqueue_get_buf(devCtx->DefVirtQueue, &len))
|
|
{
|
|
bHostAck = TRUE;
|
|
}
|
|
WdfSpinLockRelease(devCtx->InfDefQueueLock);
|
|
|
|
if(bHostAck)
|
|
{
|
|
KeSetEvent (&devCtx->HostAckEvent, EVENT_INCREMENT, FALSE);
|
|
}
|
|
|
|
if (devCtx->StatVirtQueue)
|
|
{
|
|
WdfSpinLockAcquire(devCtx->StatQueueLock);
|
|
buffer = virtqueue_get_buf(devCtx->StatVirtQueue, &len);
|
|
WdfSpinLockRelease(devCtx->StatQueueLock);
|
|
|
|
if (buffer)
|
|
{
|
|
#ifdef USE_BALLOON_SERVICE
|
|
WDFREQUEST request = devCtx->PendingWriteRequest;
|
|
|
|
devCtx->HandleWriteRequest = TRUE;
|
|
|
|
if ((request != NULL) &&
|
|
(WdfRequestUnmarkCancelable(request) != STATUS_CANCELLED))
|
|
{
|
|
NTSTATUS status;
|
|
PVOID buffer;
|
|
size_t length = 0;
|
|
|
|
devCtx->PendingWriteRequest = NULL;
|
|
|
|
status = WdfRequestRetrieveInputBuffer(request, 0, &buffer, &length);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
length = 0;
|
|
}
|
|
WdfRequestCompleteWithInformation(request, status, length);
|
|
}
|
|
#else // USE_BALLOON_SERVICE
|
|
/*
|
|
* According to MSDN 'Using Framework Work Items' article:
|
|
* Create one or more work items that your driver requeues as necessary.
|
|
* Subsequently, each time that the driver's EvtInterruptDpc callback
|
|
* function is called it must determine if the EvtWorkItem callback
|
|
* function has run. If the EvtWorkItem callback function has not run,
|
|
* the EvtInterruptDpc callback function does not call WdfWorkItemEnqueue,
|
|
* because the work item is still queued.
|
|
* A few drivers might need to call WdfWorkItemFlush to flush their work
|
|
* items from the work-item queue.
|
|
*
|
|
* For each dpc (i.e. interrupt) we'll push stats exactly that many times.
|
|
*/
|
|
if (1==InterlockedIncrement(&devCtx->WorkCount))
|
|
{
|
|
WdfWorkItemEnqueue(devCtx->StatWorkItem);
|
|
}
|
|
#endif // USE_BALLOON_SERVICE
|
|
}
|
|
}
|
|
|
|
if(devCtx->Thread)
|
|
{
|
|
KeSetEvent(&devCtx->WakeUpThread, EVENT_INCREMENT, FALSE);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
BalloonInterruptEnable(
|
|
IN WDFINTERRUPT WdfInterrupt,
|
|
IN WDFDEVICE WdfDevice
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = NULL;
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> %s\n", __FUNCTION__);
|
|
|
|
devCtx = GetDeviceContext(WdfDevice);
|
|
EnableInterrupt(WdfInterrupt, devCtx);
|
|
BalloonInterruptIsr(WdfInterrupt, 0);
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- %s\n", __FUNCTION__);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
BalloonInterruptDisable(
|
|
IN WDFINTERRUPT WdfInterrupt,
|
|
IN WDFDEVICE WdfDevice
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = NULL;
|
|
UNREFERENCED_PARAMETER( WdfInterrupt );
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> %s\n", __FUNCTION__);
|
|
|
|
devCtx = GetDeviceContext(WdfDevice);
|
|
DisableInterrupt(devCtx);
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- %s\n", __FUNCTION__);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#ifdef USE_BALLOON_SERVICE
|
|
VOID
|
|
BalloonEvtFileClose(
|
|
IN WDFFILEOBJECT FileObject
|
|
)
|
|
{
|
|
WDFDEVICE Device = WdfFileObjectGetDevice(FileObject);
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(Device);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-> %s\n", __FUNCTION__);
|
|
|
|
// synchronize with the device to make sure it doesn't exit D0 from underneath us
|
|
WdfObjectAcquireLock(Device);
|
|
|
|
if (devCtx->MemStats)
|
|
{
|
|
RtlFillMemory(devCtx->MemStats, sizeof(BALLOON_STAT) * VIRTIO_BALLOON_S_NR, -1);
|
|
}
|
|
|
|
if (devCtx->StatVirtQueue && devCtx->MemStats)
|
|
{
|
|
BalloonMemStats(Device);
|
|
}
|
|
|
|
WdfObjectReleaseLock(Device);
|
|
}
|
|
#endif // USE_BALLOON_SERVICE
|
|
|
|
VOID
|
|
BalloonSetSize(
|
|
IN WDFOBJECT WdfDevice,
|
|
IN size_t num
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(WdfDevice);
|
|
u32 actual = (u32)num;
|
|
VirtIOWdfDeviceSet(&devCtx->VDevice, FIELD_OFFSET(VIRTIO_BALLOON_CONFIG, actual), &actual, sizeof(actual));
|
|
}
|
|
|
|
LONGLONG
|
|
BalloonGetSize(
|
|
IN WDFOBJECT WdfDevice
|
|
)
|
|
{
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(WdfDevice);
|
|
|
|
u32 v;
|
|
VirtIOWdfDeviceGet(&devCtx->VDevice, FIELD_OFFSET(VIRTIO_BALLOON_CONFIG, num_pages), &v, sizeof(v));
|
|
return (LONGLONG)v - devCtx->num_pages;
|
|
}
|
|
|
|
VOID
|
|
BalloonRoutine(
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
WDFOBJECT Device = (WDFOBJECT)pContext;
|
|
PDEVICE_CONTEXT devCtx = GetDeviceContext(Device);
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
LONGLONG diff;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Balloon thread started....\n");
|
|
|
|
for (;;)
|
|
{
|
|
status = KeWaitForSingleObject(&devCtx->WakeUpThread, Executive,
|
|
KernelMode, FALSE, NULL);
|
|
if(STATUS_WAIT_0 == status)
|
|
{
|
|
if(devCtx->bShutDown)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Exiting Thread!\n");
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
diff = BalloonGetSize(Device);
|
|
if (diff > 0)
|
|
{
|
|
BalloonFill(Device, (size_t)(diff));
|
|
}
|
|
else if (diff < 0)
|
|
{
|
|
BalloonLeak(Device, (size_t)(-diff));
|
|
}
|
|
BalloonSetSize(Device, devCtx->num_pages);
|
|
}
|
|
}
|
|
}
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Thread about to exit...\n");
|
|
|
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
|
}
|