Files
yttrium-virtio-gpu/NetKVM/Common/ParaNdis_CX.cpp
T
Yuri Benditovich 95918ea892 netkvm: ensure the ctrl queue is valid when sending commands
https://bugzilla.redhat.com/show_bug.cgi?id=1872384
At least one dump when IOCTL sent from work item in
tcpip protocol stack when the control queue is deleted
(actually all the queues are deleted).

Signed-off-by: Yuri Benditovich <[email protected]>
2020-08-26 17:19:23 +03:00

160 lines
4.6 KiB
C++

#include "ndis56common.h"
#include "virtio_net.h"
#include "kdebugprint.h"
#include "Trace.h"
#ifdef NETKVM_WPP_ENABLED
#include "ParaNdis_CX.tmh"
#endif
CParaNdisCX::CParaNdisCX(PPARANDIS_ADAPTER Context)
{
m_Context = Context;
m_ControlData.Virtual = nullptr;
KeInitializeDpc(&m_DPC, MiniportMSIInterruptCXDpc, m_Context);
}
CParaNdisCX::~CParaNdisCX()
{
if (m_ControlData.Virtual != nullptr)
{
ParaNdis_FreePhysicalMemory(m_Context, &m_ControlData);
}
}
bool CParaNdisCX::Create(UINT DeviceQueueIndex)
{
m_queueIndex = (u16)DeviceQueueIndex;
if (!ParaNdis_InitialAllocatePhysicalMemory(m_Context, 512, &m_ControlData))
{
DPrintf(0, "CParaNdisCX::Create - ParaNdis_InitialAllocatePhysicalMemory failed for %u\n",
DeviceQueueIndex);
m_ControlData.Virtual = nullptr;
return false;
}
m_Context->m_CxStateMachine.Start();
CreatePath();
return m_VirtQueue.Create(DeviceQueueIndex,
&m_Context->IODevice,
m_Context->MiniportHandle);
}
BOOLEAN CParaNdisCX::SendControlMessage(
UCHAR cls,
UCHAR cmd,
PVOID buffer1,
ULONG size1,
PVOID buffer2,
ULONG size2,
int levelIfOK
)
{
BOOLEAN bOK = FALSE;
CLockedContext<CNdisSpinLock> autoLock(m_Lock);
if (m_ControlData.Virtual && m_ControlData.size > (size1 + size2 + 16) &&
m_VirtQueue.IsValid() && m_VirtQueue.CanTouchHardware())
{
struct VirtIOBufferDescriptor sg[4];
PUCHAR pBase = (PUCHAR)m_ControlData.Virtual;
PHYSICAL_ADDRESS phBase = m_ControlData.Physical;
ULONG offset = 0;
UINT nOut = 1;
((virtio_net_ctrl_hdr *)pBase)->class_of_command = cls;
((virtio_net_ctrl_hdr *)pBase)->cmd = cmd;
sg[0].physAddr = phBase;
sg[0].length = sizeof(virtio_net_ctrl_hdr);
offset += sg[0].length;
offset = (offset + 3) & ~3;
if (size1)
{
NdisMoveMemory(pBase + offset, buffer1, size1);
sg[nOut].physAddr = phBase;
sg[nOut].physAddr.QuadPart += offset;
sg[nOut].length = size1;
offset += size1;
offset = (offset + 3) & ~3;
nOut++;
}
if (size2)
{
NdisMoveMemory(pBase + offset, buffer2, size2);
sg[nOut].physAddr = phBase;
sg[nOut].physAddr.QuadPart += offset;
sg[nOut].length = size2;
offset += size2;
offset = (offset + 3) & ~3;
nOut++;
}
sg[nOut].physAddr = phBase;
sg[nOut].physAddr.QuadPart += offset;
sg[nOut].length = sizeof(virtio_net_ctrl_ack);
*(virtio_net_ctrl_ack *)(pBase + offset) = VIRTIO_NET_ERR;
if (0 <= m_VirtQueue.AddBuf(sg, nOut, 1, (PVOID)1, NULL, 0))
{
UINT len;
void *p;
m_Context->m_CxStateMachine.RegisterOutstandingItem();
m_VirtQueue.Kick();
p = m_VirtQueue.GetBuf(&len);
for (int i = 0; i < 500000 && !p; ++i)
{
UINT interval = 1;
NdisStallExecution(interval);
p = m_VirtQueue.GetBuf(&len);
}
m_Context->m_CxStateMachine.UnregisterOutstandingItem();
if (!p)
{
DPrintf(0, "%s - ERROR: get_buf failed\n", __FUNCTION__);
}
else if (len != sizeof(virtio_net_ctrl_ack))
{
DPrintf(0, "%s - ERROR: wrong len %d\n", __FUNCTION__, len);
}
else if (*(virtio_net_ctrl_ack *)(pBase + offset) != VIRTIO_NET_OK)
{
DPrintf(0, "%s - ERROR: error %d returned for class %d\n", __FUNCTION__, *(virtio_net_ctrl_ack *)(pBase + offset), cls);
}
else
{
// everything is OK
DPrintf(levelIfOK, "%s OK(%d.%d,buffers of %d and %d) \n", __FUNCTION__, cls, cmd, size1, size2);
bOK = TRUE;
}
}
else
{
DPrintf(0, "%s - ERROR: add_buf failed\n", __FUNCTION__);
}
}
else
{
DPrintf(0, "%s (buffer %d,%d) - ERROR: message too LARGE\n", __FUNCTION__, size1, size2);
}
return bOK;
}
NDIS_STATUS CParaNdisCX::SetupMessageIndex(u16 vector)
{
DPrintf(0, "[%s] Using message %u for controls\n", __FUNCTION__, vector);
virtio_set_config_vector(&m_Context->IODevice, vector);
return CParaNdisAbstractPath::SetupMessageIndex(vector);
}
bool CParaNdisCX::FireDPC(ULONG messageId)
{
DPrintf(0, "[%s] message %u\n", __FUNCTION__, messageId);
KeInsertQueueDpc(&m_DPC, NULL, NULL);
return TRUE;
}