kvm-guest-drivers-windows
356 строк · 10.7 Кб
1/*
2* This file contains balloon driver routines
3*
4* Copyright (c) 2009-2017 Red Hat, Inc.
5*
6* Author(s):
7* Vadim Rozenfeld <vrozenfe@redhat.com>
8*
9* Redistribution and use in source and binary forms, with or without
10* modification, are permitted provided that the following conditions
11* are met :
12* 1. Redistributions of source code must retain the above copyright
13* notice, this list of conditions and the following disclaimer.
14* 2. Redistributions in binary form must reproduce the above copyright
15* notice, this list of conditions and the following disclaimer in the
16* documentation and / or other materials provided with the distribution.
17* 3. Neither the names of the copyright holders nor the names of their contributors
18* may be used to endorse or promote products derived from this software
19* without specific prior written permission.
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30* SUCH DAMAGE.
31*/
32#include "precomp.h"33
34#if defined(EVENT_TRACING)35#include "balloon.tmh"36#endif37
38NTSTATUS
39BalloonInit(40IN WDFOBJECT WdfDevice
41)42{
43NTSTATUS status = STATUS_SUCCESS;44PDEVICE_CONTEXT devCtx = GetDeviceContext(WdfDevice);45u64 u64HostFeatures;46u64 u64GuestFeatures = 0;47bool notify_stat_queue = false;48VIRTIO_WDF_QUEUE_PARAM params[3];49PVIOQUEUE vqs[3];50ULONG nvqs;51
52TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> BalloonInit\n");53
54WdfObjectAcquireLock(WdfDevice);55
56// inflate57params[0].Interrupt = devCtx->WdfInterrupt;58
59// deflate60params[1].Interrupt = devCtx->WdfInterrupt;61
62// stats63params[2].Interrupt = devCtx->WdfInterrupt;64
65u64HostFeatures = VirtIOWdfGetDeviceFeatures(&devCtx->VDevice);66
67if (virtio_is_feature_enabled(u64HostFeatures, VIRTIO_BALLOON_F_STATS_VQ))68{69TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,70"Enable stats feature.\n");71
72virtio_feature_enable(u64GuestFeatures, VIRTIO_BALLOON_F_STATS_VQ);73nvqs = 3;74}75else76{77nvqs = 2;78}79
80status = VirtIOWdfSetDriverFeatures(&devCtx->VDevice, u64GuestFeatures, 0);81if (NT_SUCCESS(status))82{83// initialize 2 or 3 queues84status = VirtIOWdfInitQueues(&devCtx->VDevice, nvqs, vqs, params);85if (NT_SUCCESS(status))86{87devCtx->InfVirtQueue = vqs[0];88devCtx->DefVirtQueue = vqs[1];89
90if (nvqs == 3)91{92VIO_SG sg;93
94devCtx->StatVirtQueue = vqs[2];95
96sg.physAddr = VirtIOWdfDeviceGetPhysicalAddress(&devCtx->VDevice.VIODevice, devCtx->MemStats);97sg.length = sizeof (BALLOON_STAT) * VIRTIO_BALLOON_S_NR;98
99if (virtqueue_add_buf(100devCtx->StatVirtQueue, &sg, 1, 0, devCtx, NULL, 0) >= 0)101{102notify_stat_queue = true;103}104else105{106TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,107"Failed to add buffer to stats queue.\n");108}109}110VirtIOWdfSetDriverOK(&devCtx->VDevice);111}112else113{114TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,115"VirtIOWdfInitQueues failed with %x\n", status);116VirtIOWdfSetDriverFailed(&devCtx->VDevice);117}118}119else120{121TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,122"VirtIOWdfSetDriverFeatures failed with %x\n", status);123VirtIOWdfSetDriverFailed(&devCtx->VDevice);124}125
126// notify the stat queue only after the virtual device has been fully initialized127if (notify_stat_queue)128{129virtqueue_kick(devCtx->StatVirtQueue);130}131
132WdfObjectReleaseLock(WdfDevice);133
134TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- BalloonInit\n");135return status;136}
137
138VOID
139BalloonFill(140IN WDFOBJECT WdfDevice,141IN size_t num)142{
143PDEVICE_CONTEXT ctx = GetDeviceContext(WdfDevice);144PHYSICAL_ADDRESS LowAddress;145PHYSICAL_ADDRESS HighAddress;146PHYSICAL_ADDRESS SkipBytes;147PPAGE_LIST_ENTRY pNewPageListEntry;148PMDL pPageMdl;149
150TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);151
152ctx->num_pfns = 0;153
154#ifndef BALLOON_INFLATE_IGNORE_LOWMEM155if (IsLowMemory(WdfDevice))156{157TraceEvents(TRACE_LEVEL_WARNING, DBG_HW_ACCESS,158"Low memory. Allocated pages: %d\n", ctx->num_pages);159return;160}161#endif // !BALLOON_INFLATE_IGNORE_LOWMEM162
163num = min(num, PAGE_SIZE / sizeof(PFN_NUMBER));164TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS,165"Inflate balloon with %d pages.\n", num);166
167LowAddress.QuadPart = 0;168HighAddress.QuadPart = (ULONGLONG)-1;169SkipBytes.QuadPart = 0;170
171pPageMdl = MmAllocatePagesForMdlEx(LowAddress, HighAddress, SkipBytes,172num * PAGE_SIZE, MmNonCached, MM_DONT_ZERO_ALLOCATION);173
174if (pPageMdl == NULL)175{176TraceEvents(TRACE_LEVEL_WARNING, DBG_HW_ACCESS,177"Failed to allocate pages.\n");178return;179}180
181if (MmGetMdlByteCount(pPageMdl) != (num * PAGE_SIZE))182{183TraceEvents(TRACE_LEVEL_WARNING, DBG_HW_ACCESS,184"Not all requested memory was allocated (%d/%d).\n",185MmGetMdlByteCount(pPageMdl), num * PAGE_SIZE);186MmFreePagesFromMdl(pPageMdl);187ExFreePool(pPageMdl);188return;189}190
191pNewPageListEntry = (PPAGE_LIST_ENTRY)ExAllocateFromNPagedLookasideList(192&ctx->LookAsideList);193
194if (pNewPageListEntry == NULL)195{196TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,197"Failed to allocate list entry.\n");198MmFreePagesFromMdl(pPageMdl);199ExFreePool(pPageMdl);200return;201}202
203pNewPageListEntry->PageMdl = pPageMdl;204PushEntryList(&ctx->PageListHead, &(pNewPageListEntry->SingleListEntry));205
206ctx->num_pfns = (ULONG)num;207ctx->num_pages += ctx->num_pfns;208
209RtlCopyMemory(ctx->pfns_table, MmGetMdlPfnArray(pPageMdl),210ctx->num_pfns * sizeof(PFN_NUMBER));211
212BalloonTellHost(WdfDevice, ctx->InfVirtQueue);213
214TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);215}
216
217VOID
218BalloonLeak(219IN WDFOBJECT WdfDevice,220IN size_t num221)222{
223PDEVICE_CONTEXT ctx = GetDeviceContext(WdfDevice);224PPAGE_LIST_ENTRY pPageListEntry;225PMDL pPageMdl;226
227TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);228
229pPageListEntry = (PPAGE_LIST_ENTRY)PopEntryList(&ctx->PageListHead);230if (pPageListEntry == NULL)231{232TraceEvents(TRACE_LEVEL_WARNING, DBG_HW_ACCESS, "No list entries.\n");233return;234}235
236pPageMdl = pPageListEntry->PageMdl;237
238num = MmGetMdlByteCount(pPageMdl) / PAGE_SIZE;239TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS,240"Deflate balloon with %d pages.\n", num);241
242ctx->num_pfns = (ULONG)num;243ctx->num_pages -= ctx->num_pfns;244
245RtlCopyMemory(ctx->pfns_table, MmGetMdlPfnArray(pPageMdl),246ctx->num_pfns * sizeof(PFN_NUMBER));247
248MmFreePagesFromMdl(pPageMdl);249ExFreePool(pPageMdl);250ExFreeToNPagedLookasideList(&ctx->LookAsideList, pPageListEntry);251
252BalloonTellHost(WdfDevice, ctx->DefVirtQueue);253
254TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);255}
256
257VOID
258BalloonTellHost(259IN WDFOBJECT WdfDevice,260IN PVIOQUEUE vq
261)262{
263VIO_SG sg;264PDEVICE_CONTEXT devCtx = GetDeviceContext(WdfDevice);265NTSTATUS status;266LARGE_INTEGER timeout = {0};267bool do_notify;268
269TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);270if (devCtx->SurpriseRemoval)271{272TraceEvents(TRACE_LEVEL_WARNING, DBG_HW_ACCESS, "<-- %s Skipped\n", __FUNCTION__);273return;274}275
276sg.physAddr = VirtIOWdfDeviceGetPhysicalAddress(&devCtx->VDevice.VIODevice, devCtx->pfns_table);277sg.length = sizeof(devCtx->pfns_table[0]) * devCtx->num_pfns;278
279WdfSpinLockAcquire(devCtx->InfDefQueueLock);280if (virtqueue_add_buf(vq, &sg, 1, 0, devCtx, NULL, 0) < 0)281{282TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "<-> %s :: Cannot add buffer\n", __FUNCTION__);283WdfSpinLockRelease(devCtx->InfDefQueueLock);284return;285}286do_notify = virtqueue_kick_prepare(vq);287WdfSpinLockRelease(devCtx->InfDefQueueLock);288
289if (do_notify)290{291virtqueue_notify(vq);292}293
294timeout.QuadPart = Int32x32To64(1000, -10000);295status = KeWaitForSingleObject (296&devCtx->HostAckEvent,297Executive,298KernelMode,299FALSE,300&timeout);301ASSERT(NT_SUCCESS(status));302if(STATUS_TIMEOUT == status)303{304TraceEvents(TRACE_LEVEL_WARNING, DBG_HW_ACCESS, "<--> TimeOut\n");305}306}
307
308
309VOID
310BalloonTerm(311IN WDFOBJECT WdfDevice
312)313{
314PDEVICE_CONTEXT devCtx = GetDeviceContext(WdfDevice);315
316TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> BalloonTerm\n");317
318WdfObjectAcquireLock(WdfDevice);319
320VirtIOWdfDestroyQueues(&devCtx->VDevice);321devCtx->StatVirtQueue = NULL;322
323WdfObjectReleaseLock(WdfDevice);324
325TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- BalloonTerm\n");326}
327
328VOID
329BalloonMemStats(330IN WDFOBJECT WdfDevice
331)332{
333VIO_SG sg;334PDEVICE_CONTEXT devCtx = GetDeviceContext(WdfDevice);335bool do_notify;336
337TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);338
339sg.physAddr = VirtIOWdfDeviceGetPhysicalAddress(&devCtx->VDevice.VIODevice, devCtx->MemStats);340sg.length = sizeof(BALLOON_STAT) * VIRTIO_BALLOON_S_NR;341
342WdfSpinLockAcquire(devCtx->StatQueueLock);343if (virtqueue_add_buf(devCtx->StatVirtQueue, &sg, 1, 0, devCtx, NULL, 0) < 0)344{345TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "<-> %s :: Cannot add buffer\n", __FUNCTION__);346}347do_notify = virtqueue_kick_prepare(devCtx->StatVirtQueue);348WdfSpinLockRelease(devCtx->StatQueueLock);349
350if (do_notify)351{352virtqueue_notify(devCtx->StatVirtQueue);353}354
355TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);356}
357