[virtio-serial] Add a support for message-signaled interrupts.

This commit is contained in:
Gal Hammer authored and Yan Vugenfirer committed 2013-02-25 16:40:55 +02:00
1 parent ebe2f4715a
commit 8fe2811b21
4 files changed
+177 -78

No files matched your search

+60 -13
View File
@@ -70,7 +70,22 @@ VIOSerialInitInterruptHandling(
if (!NT_SUCCESS (status))
{
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "WdfInterruptCreate failed: %x\n", status);
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,
"Failed to create control queue interrupt: %x\n", status);
return status;
}
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
VIOSerialInterruptIsr, VIOSerialQueuesInterruptDpc);
status = WdfInterruptCreate(hDevice, &interruptConfig, &attributes,
&pContext->QueuesInterrupt);
if (!NT_SUCCESS (status))
{
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,
"Failed to create general queue interrupt: %x\n", status);
return status;
}
@@ -174,17 +189,18 @@ VIOSerialEvtDevicePrepareHardware(
{
int nListSize = 0;
PCM_PARTIAL_RESOURCE_DESCRIPTOR pResDescriptor;
WDF_INTERRUPT_INFO interruptInfo;
int i = 0;
PPORTS_DEVICE pContext = GetPortsDevice(Device);
bool bPortFound = FALSE;
NTSTATUS status = STATUS_SUCCESS;
UINT nr_ports, max_queues, size_to_allocate;
BOOLEAN MessageSignaled = FALSE;
USHORT Interrupts = 0;
UNREFERENCED_PARAMETER(ResourcesRaw);
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<--> %s\n", __FUNCTION__);
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);
max_queues = 64; // 2 for each of max 32 ports
size_to_allocate = VirtIODeviceSizeRequired((USHORT)max_queues);
@@ -235,6 +251,13 @@ VIOSerialEvtDevicePrepareHardware(
break;
case CmResourceTypeInterrupt:
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
"Interrupt Level: 0x%08x, Vector: 0x%08x\n",
pResDescriptor->u.Interrupt.Level,
pResDescriptor->u.Interrupt.Vector);
Interrupts += 1;
MessageSignaled = !!(pResDescriptor->Flags &
(CM_RESOURCE_INTERRUPT_LATCHED | CM_RESOURCE_INTERRUPT_MESSAGE));
break;
}
}
@@ -245,16 +268,23 @@ VIOSerialEvtDevicePrepareHardware(
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "%s>>> %s", __FUNCTION__, "IO port wasn't found!\n");
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
WDF_INTERRUPT_INFO_INIT(&interruptInfo);
WdfInterruptGetInfo(pContext->WdfInterrupt, &interruptInfo);
VirtIODeviceInitialize(pContext->pIODevice, (ULONG_PTR)pContext->pPortBase, size_to_allocate);
VirtIODeviceSetMSIXUsed(pContext->pIODevice, interruptInfo.MessageSignaled);
VirtIODeviceSetMSIXUsed(pContext->pIODevice, MessageSignaled);
VirtIODeviceReset(pContext->pIODevice);
VirtIODeviceAddStatus(pContext->pIODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE);
if (MessageSignaled)
{
WriteVirtIODeviceWord(
pContext->pIODevice->addr + VIRTIO_MSI_CONFIG_VECTOR, Interrupts);
Interrupts = ReadVirtIODeviceWord(
pContext->pIODevice->addr + VIRTIO_MSI_CONFIG_VECTOR);
}
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
"MessageSignaled: %d Interrupts: %x.\n",
MessageSignaled, Interrupts);
pContext->consoleConfig.max_nr_ports = 1;
if(pContext->isHostMultiport = VirtIODeviceGetHostFeature(pContext->pIODevice, VIRTIO_CONSOLE_F_MULTIPORT))
{
@@ -366,7 +396,7 @@ VIOSerialEvtDeviceReleaseHardware(
return STATUS_SUCCESS;
}
static struct virtqueue * FindVirtualQueue(VirtIODevice *dev, ULONG index)
static struct virtqueue * FindVirtualQueue(VirtIODevice *dev, ULONG index, USHORT vector)
{
struct virtqueue *pq = NULL;
PVOID p;
@@ -380,6 +410,12 @@ static struct virtqueue * FindVirtualQueue(VirtIODevice *dev, ULONG index)
if (p)
{
pq = VirtIODevicePrepareQueue(dev, index, MmGetPhysicalAddress(p), p, allocSize, p, FALSE);
if (vector != VIRTIO_MSI_NO_VECTOR)
{
WriteVirtIODeviceWord(dev->addr + VIRTIO_MSI_QUEUE_VECTOR, vector);
vector = ReadVirtIODeviceWord(dev->addr + VIRTIO_MSI_QUEUE_VECTOR);
}
}
}
return pq;
@@ -413,9 +449,20 @@ VIOSerialInitAllQueues(
NTSTATUS status = STATUS_SUCCESS;
PPORTS_DEVICE pContext = GetPortsDevice(Device);
UINT nr_ports, i, j;
USHORT ControlVector, QueuesVector;
WDF_INTERRUPT_INFO info;
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
WDF_INTERRUPT_INFO_INIT(&info);
WdfInterruptGetInfo(pContext->WdfInterrupt, &info);
ControlVector = info.MessageSignaled ? 0 : VIRTIO_MSI_NO_VECTOR;
WDF_INTERRUPT_INFO_INIT(&info);
WdfInterruptGetInfo(pContext->QueuesInterrupt, &info);
QueuesVector = (ControlVector != VIRTIO_MSI_NO_VECTOR) ?
(info.Vector ? 1 : 0) : VIRTIO_MSI_NO_VECTOR;
nr_ports = pContext->consoleConfig.max_nr_ports;
if(pContext->isHostMultiport)
{
@@ -428,22 +475,22 @@ VIOSerialInitAllQueues(
if (pContext->c_ivq)
VirtIODeviceRenewQueue(pContext->c_ivq);
else
pContext->c_ivq = FindVirtualQueue(pContext->pIODevice, 2);
pContext->c_ivq = FindVirtualQueue(pContext->pIODevice, 2, ControlVector);
if (pContext->c_ovq)
VirtIODeviceRenewQueue(pContext->c_ovq);
else
pContext->c_ovq = FindVirtualQueue(pContext->pIODevice, 3);
pContext->c_ovq = FindVirtualQueue(pContext->pIODevice, 3, ControlVector);
}
else
{
if (pContext->in_vqs[j])
VirtIODeviceRenewQueue(pContext->in_vqs[j]);
else
pContext->in_vqs[j] = FindVirtualQueue(pContext->pIODevice, i * 2);
pContext->in_vqs[j] = FindVirtualQueue(pContext->pIODevice, i * 2, QueuesVector);
if (pContext->out_vqs[j])
VirtIODeviceRenewQueue(pContext->out_vqs[j]);
else
pContext->out_vqs[j] = FindVirtualQueue(pContext->pIODevice, (i * 2) + 1);
pContext->out_vqs[j] = FindVirtualQueue(pContext->pIODevice, (i * 2) + 1, QueuesVector);
++j;
}
}
+108 -64
View File
@@ -10,20 +10,27 @@ VIOSerialInterruptIsr(
IN WDFINTERRUPT Interrupt,
IN ULONG MessageID)
{
ULONG ret;
PPORTS_DEVICE pContext = GetPortsDevice(WdfInterruptGetDevice(Interrupt));
UNREFERENCED_PARAMETER(MessageID);
PPORTS_DEVICE pContext = GetPortsDevice(WdfInterruptGetDevice(Interrupt));
BOOLEAN serviced;
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "--> %s\n", __FUNCTION__);
if((ret = VirtIODeviceISR(pContext->pIODevice)) > 0)
// Schedule a DPC if the device is using message-signaled interrupts, or
// if the device ISR status is enabled.
if (MessageID || VirtIODeviceISR(pContext->pIODevice))
{
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, "Got ISR - it is ours %d!\n", ret);
WdfInterruptQueueDpcForIsr(Interrupt);
return TRUE;
serviced = TRUE;
}
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "WRONG ISR!\n");
return FALSE;
else
{
serviced = FALSE;
}
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT,
"<-- %s Serviced: %d\n", __FUNCTION__, serviced);
return serviced;
}
VOID
@@ -31,73 +38,110 @@ VIOSerialInterruptDpc(
IN WDFINTERRUPT Interrupt,
IN WDFOBJECT AssociatedObject)
{
UINT i;
PPORTS_DEVICE pContext;
PVIOSERIAL_PORT port;
WDFDEVICE Device;
WDFDEVICE Device = WdfInterruptGetDevice(Interrupt);
PPORTS_DEVICE Context = GetPortsDevice(Device);
WDF_INTERRUPT_INFO info;
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "--> %s\n", __FUNCTION__);
ULONG information;
NTSTATUS status;
PUCHAR systemBuffer;
size_t Length;
WDFREQUEST request;
VIOSerialCtrlWorkHandler(Device);
WDF_INTERRUPT_INFO_INIT(&info);
WdfInterruptGetInfo(Context->QueuesInterrupt, &info);
// Using the queues' DPC if only one interrupt is availble.
if (info.Vector == 0)
{
VIOSerialQueuesInterruptDpc(Interrupt, AssociatedObject);
}
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<-- %s\n", __FUNCTION__);
}
VOID VIOSerialQueuesInterruptDpc(IN WDFINTERRUPT Interrupt,
IN WDFOBJECT AssociatedObject)
{
NTSTATUS status;
WDFCHILDLIST PortList;
WDFDEVICE Device;
WDF_CHILD_LIST_ITERATOR iterator;
WDF_CHILD_RETRIEVE_INFO PortInfo;
VIOSERIAL_PORT VirtPort;
VIOSERIAL_PORT *Port;
UNREFERENCED_PARAMETER(AssociatedObject);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "--> %s\n", __FUNCTION__);
if(!Interrupt)
{
TraceEvents(TRACE_LEVEL_ERROR, DBG_DPC, "Got NULL interrupt object DPC!\n");
return;
}
Device = WdfInterruptGetDevice(Interrupt);
pContext = GetPortsDevice(Device);
PortList = WdfFdoGetDefaultChildList(WdfInterruptGetDevice(Interrupt));
WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren);
VIOSerialCtrlWorkHandler(Device);
for (i = 0; i < pContext->consoleConfig.max_nr_ports; ++i)
WdfChildListBeginIteration(PortList, &iterator);
for (;;)
{
port = VIOSerialFindPortById(Device, i);
if (port)
WDF_CHILD_RETRIEVE_INFO_INIT(&PortInfo, &VirtPort.Header);
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&VirtPort.Header,
sizeof(VirtPort));
status = WdfChildListRetrieveNextDevice(PortList, &iterator, &Device,
&PortInfo);
if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES)
{
WdfSpinLockAcquire(port->InBufLock);
if (!port->InBuf)
{
port->InBuf = VIOSerialGetInBuf(port);
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "%s::%d port->InBuf = %p\n", __FUNCTION__, __LINE__, port->InBuf);
}
if (!port->GuestConnected)
{
VIOSerialDiscardPortDataLocked(port);
}
if (port->InBuf)
{
if (port->PendingReadRequest)
{
request = port->PendingReadRequest;
status = WdfRequestUnmarkCancelable(request);
if (status != STATUS_CANCELLED)
{
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,"Got available read request\n");
status = WdfRequestRetrieveOutputBuffer(request, 0, &systemBuffer, &Length);
if (NT_SUCCESS(status))
{
port->PendingReadRequest = NULL;
information = (ULONG)VIOSerialFillReadBufLocked(port, systemBuffer, Length);
WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, information);
}
}
else
{
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "Request = %p was cancelled\n", request);
}
}
}
WdfSpinLockRelease(port->InBufLock);
break;
}
Port = RawPdoSerialPortGetData(Device)->port;
WdfSpinLockAcquire(Port->InBufLock);
if (!Port->InBuf)
{
Port->InBuf = (PPORT_BUFFER)VIOSerialGetInBuf(Port);
}
if (!Port->GuestConnected)
{
VIOSerialDiscardPortDataLocked(Port);
}
if (Port->InBuf && Port->PendingReadRequest)
{
WDFREQUEST Request;
Request = Port->PendingReadRequest;
status = WdfRequestUnmarkCancelable(Request);
if (status != STATUS_CANCELLED)
{
PVOID Buffer;
size_t Length;
status = WdfRequestRetrieveOutputBuffer(Request, 0, &Buffer, &Length);
if (NT_SUCCESS(status))
{
ULONG Read;
Port->PendingReadRequest = NULL;
Read = (ULONG)VIOSerialFillReadBufLocked(Port, Buffer, Length);
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS,
Read);
}
else
{
TraceEvents(TRACE_LEVEL_ERROR, DBG_DPC,
"Failed to retrieve output buffer (Status: %x Request: %p).\n",
status, Request);
}
}
else
{
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,
"Request %p was cancelled.\n", Request);
}
}
WdfSpinLockRelease(Port->InBufLock);
}
WdfChildListEndIteration(PortList, &iterator);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<-- %s\n", __FUNCTION__);
}
+2
View File
@@ -20,6 +20,7 @@ EVT_WDF_DRIVER_DEVICE_ADD VIOSerialEvtDeviceAdd;
EVT_WDF_INTERRUPT_ISR VIOSerialInterruptIsr;
EVT_WDF_INTERRUPT_DPC VIOSerialInterruptDpc;
EVT_WDF_INTERRUPT_DPC VIOSerialQueuesInterruptDpc;
EVT_WDF_INTERRUPT_ENABLE VIOSerialInterruptEnable;
EVT_WDF_INTERRUPT_DISABLE VIOSerialInterruptDisable;
@@ -82,6 +83,7 @@ typedef struct _tagPortDevice
bool bPortMapped;
WDFINTERRUPT WdfInterrupt;
WDFINTERRUPT QueuesInterrupt;
int isHostMultiport;
+7 -1
View File
@@ -46,8 +46,14 @@ vioser.sys = 1,,
[VirtioSerial_Device.NT]
CopyFiles=Drivers_Dir
[VirtioPort_Device.NT]
[VirtioSerial_Device.NT.HW]
AddReg=VirtioSerial_AddReg
[VirtioSerial_AddReg]
HKR,Interrupt Management,,0x00000010
HKR,Interrupt Management\MessageSignaledInterruptProperties,,0x00000010
HKR,Interrupt Management\MessageSignaledInterruptProperties,MSISupported,0x00010001,1
HKR,Interrupt Management\MessageSignaledInterruptProperties,MessageNumberLimit,0x00010001,2
[Drivers_Dir]
vioser.sys