From patchwork Tue Oct 12 01:11:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Kozlyuk X-Patchwork-Id: 101135 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 0DB68A034F; Tue, 12 Oct 2021 03:11:15 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id CD65B410E5; Tue, 12 Oct 2021 03:11:14 +0200 (CEST) Received: from mail-lf1-f41.google.com (mail-lf1-f41.google.com [209.85.167.41]) by mails.dpdk.org (Postfix) with ESMTP id D81A5410E2 for ; Tue, 12 Oct 2021 03:11:12 +0200 (CEST) Received: by mail-lf1-f41.google.com with SMTP id u18so80418770lfd.12 for ; Mon, 11 Oct 2021 18:11:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=C3O2agP6JOnZEtNqR6Ae3K8Y0StTOrARx1f+Ff5GhFw=; b=fMYAGFnmLkTR/uUn4XQjq09Hf7okoGNE+6FRMhUzOg4SQBzI11pgupv9lOm/JQQeJT a2rQsTTdECXIq3PhzeO35AzfSLBng6OCVZpy3WiUvF+haOvPAtKwwTNUp+zWj8UGBXp/ 7DYorG6GWkUVcdN+MNog6Ullm0rHaWmHhPsVt62f4LPOHhmYj9DVOX2MyPd5yVF/hT0L l9qI9tNuQT2n8k1f5c0U4H8GpAfvlIno2ySSrojLThqNVWadCS2Y89V0QjsZ3iNfNIKw EPeat/00tEppRd7BW6DSGJfrdOhOoA46NBNly6leNOHfrC4m9C8ketvd3SfSraCesAE2 rSgQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=C3O2agP6JOnZEtNqR6Ae3K8Y0StTOrARx1f+Ff5GhFw=; b=BtfkoKW6BCSCzaGFqA+V90HHSbYsECW7GSuWXF5TPj+XrwYuNP6Pggu0mmqM8BRUvd yq/kPjMcMS+/TuQassbcFmhrOEyaoELu86ZDQSF/BQXgheJBudxJ120Zyj6EjoevyT7d nZZQvZo+bP8q1eyFZbz8kDC5XKyGPvfX/SzstfXqF3a5E8q1LjdkJ0byzTvC/X3+vsl8 d6hztRjSz/X42AfWV9HRc5Jrov1tbnSYWTRyAiQmRESIknrQyN0VE7gaNAlvyQNhmZO6 2Jw4Jxa4zldB3WmeBSgGJsqxSZW4MDunGFszdphlSJmD5L4bEisO2ZBY3O3gtAoC2ltO ppjA== X-Gm-Message-State: AOAM533AX9gkPJD7EDAhChfTWtU3fzdNOQaOjw94bUgnajWpBmWzXOfg m0/cmcwTnRcRtYtwdDsV3Xie5cVfX3U= X-Google-Smtp-Source: ABdhPJy0AhDWuO4i4GG/K0Aa/ZMkog36m7CLmgQNpJ/aEenxjedjYzIAI3r18zzHQ0Ds4w0PUHEM4A== X-Received: by 2002:ac2:4824:: with SMTP id 4mr9485084lft.634.1634001072142; Mon, 11 Oct 2021 18:11:12 -0700 (PDT) Received: from sovereign.. (broadband-37-110-65-23.ip.moscow.rt.ru. [37.110.65.23]) by smtp.gmail.com with ESMTPSA id i15sm876631lfc.11.2021.10.11.18.11.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 11 Oct 2021 18:11:11 -0700 (PDT) From: Dmitry Kozlyuk To: dev@dpdk.org Cc: William Tu , Dmitry Kozlyuk , Narcisa Ana Maria Vasile , Dmitry Malloy , Pallavi Kadam , Ranjit Menon Date: Tue, 12 Oct 2021 04:11:07 +0300 Message-Id: <20211012011107.431188-1-dmitry.kozliuk@gmail.com> X-Mailer: git-send-email 2.29.3 MIME-Version: 1.0 Subject: [dpdk-dev] [kmods PATCH] windows/netuio: add interrupt support X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Add IOCTL for interrupt event delivery to user space. * IOCTL_NETUIO_INTR_CONTROL: Enable/disable the delivery of interrupt events. This is a software switch to ignore received interrupts. Programming the HW to stop issuing interrupts is up to the user. * IOCTL_NETUIO_INTR_QUERY: Wait for an interrupt event on the specified vector. More than one outstanding request may be issued for the same vector. This can be used to balance the interrupt servicing if the requests are issued using distinct handles waited by different threads. If N identical requests are issued using the same handle, one request will be completed each time the interrupt is triggered. MSI-X support is advertised for the driver, but the implementation works with any type of interrupts assigned by the kernel. Signed-off-by: Dmitry Kozlyuk --- windows/netuio/netuio.inf | 3 + windows/netuio/netuio_dev.c | 109 +++++++++++++++++++++++++++++- windows/netuio/netuio_dev.h | 10 +++ windows/netuio/netuio_interface.h | 22 ++++++ windows/netuio/netuio_queue.c | 102 ++++++++++++++++++++-------- 5 files changed, 214 insertions(+), 32 deletions(-) diff --git a/windows/netuio/netuio.inf b/windows/netuio/netuio.inf index d166868..cac378d 100644 --- a/windows/netuio/netuio.inf +++ b/windows/netuio/netuio.inf @@ -63,6 +63,9 @@ AddReg=Device.HW.Registry [Device.HW.Registry] ; Ensure that only administrators can access our device object. HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" +HKR,Interrupt Management,,0x00000010 +HKR,Interrupt Management\MessageSignaledInterruptProperties,,0x00000010 +HKR,Interrupt Management\MessageSignaledInterruptProperties,MSISupported,0x00010001,1 ;-------------- Service installation [netuio_Device.NT.Services] diff --git a/windows/netuio/netuio_dev.c b/windows/netuio/netuio_dev.c index b2deb10..d4662b6 100644 --- a/windows/netuio/netuio_dev.c +++ b/windows/netuio/netuio_dev.c @@ -135,6 +135,109 @@ netuio_create_device(PWDFDEVICE_INIT DeviceInit) return status; } +static EVT_WDF_INTERRUPT_ISR netuio_intr_service; +static EVT_WDF_INTERRUPT_WORKITEM netuio_intr_deliver; + +_Use_decl_annotations_ +static BOOLEAN +netuio_intr_service(WDFINTERRUPT intr, ULONG message_id) +{ + WDFDEVICE device = WdfInterruptGetDevice(intr); + NETUIO_CONTEXT_DATA *ctx = netuio_get_context_data(device); + BOOLEAN enabled = ctx->intr_enabled; + + UNREFERENCED_PARAMETER(message_id); + if (enabled) + WdfInterruptQueueWorkItemForIsr(intr); + return enabled; +} + +_Use_decl_annotations_ +static void +netuio_intr_deliver(WDFINTERRUPT intr, WDFOBJECT device) +{ + NETUIO_INTR_CTX *intr_ctx = netuio_intr_ctx_get(intr); + WDFREQUEST request; + NTSTATUS status; + + UNREFERENCED_PARAMETER(device); + PAGED_CODE(); + status = WdfIoQueueRetrieveNextRequest(intr_ctx->queue, &request); + if (NT_SUCCESS(status)) + WdfRequestComplete(request, STATUS_SUCCESS); +} + +static NTSTATUS +netuio_intr_alloc(WDFDEVICE device, WDFCMRESLIST raw, WDFCMRESLIST translated) +{ + NETUIO_CONTEXT_DATA *ctx; + CM_PARTIAL_RESOURCE_DESCRIPTOR *desc; + WDFINTERRUPT* intrs; + WDF_OBJECT_ATTRIBUTES intr_attr; + WDF_INTERRUPT_CONFIG intr_config; + WDF_OBJECT_ATTRIBUTES queue_attr; + WDF_IO_QUEUE_CONFIG queue_config; + ULONG i, resource_n, intr_n = 0, intr_created = 0; + NTSTATUS status; + + resource_n = WdfCmResourceListGetCount(translated); + for (i = 0; i < resource_n; i++) { + desc = WdfCmResourceListGetDescriptor(translated, i); + if (desc->Type == CmResourceTypeInterrupt) + intr_n++; + } + if (intr_n == 0) + return STATUS_SUCCESS; + + intrs = ExAllocatePoolZero(PagedPool, sizeof(intrs[0]) * intr_n, 'rtni'); + if (intrs == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + WDF_OBJECT_ATTRIBUTES_INIT(&queue_attr); + WDF_IO_QUEUE_CONFIG_INIT(&queue_config, WdfIoQueueDispatchManual); + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&intr_attr, NETUIO_INTR_CTX); + WDF_INTERRUPT_CONFIG_INIT(&intr_config, netuio_intr_service, NULL); + intr_config.EvtInterruptWorkItem = netuio_intr_deliver; + for (i = 0; i < resource_n; i++) { + NETUIO_INTR_CTX *intr_ctx; + WDFINTERRUPT intr; + WDFQUEUE queue; + + desc = WdfCmResourceListGetDescriptor(translated, i); + if (desc->Type != CmResourceTypeInterrupt) + continue; + + intr_config.InterruptRaw = WdfCmResourceListGetDescriptor(raw, i); + intr_config.InterruptTranslated = desc; + status = WdfInterruptCreate(device, &intr_config, &intr_attr, &intr); + if (!NT_SUCCESS(status)) + goto error; + + queue_attr.ParentObject = intr; + status = WdfIoQueueCreate(device, &queue_config, &queue_attr, &queue); + if (!NT_SUCCESS(status)) { + WdfObjectDelete(intr); + goto error; + } + + intr_ctx = netuio_intr_ctx_get(intr); + intr_ctx->queue = queue; + intrs[intr_created] = intr; + intr_created++; + } + + ctx = netuio_get_context_data(device); + ctx->intr = intrs; + ctx->intr_n = intr_n; + return STATUS_SUCCESS; + +error: + for (i = 0; i < intr_created; i++) + WdfObjectDelete(intrs[i]); + ExFreePool(intrs); + return status; +} + _Use_decl_annotations_ NTSTATUS netuio_map_hw_resources(WDFDEVICE Device, WDFCMRESLIST Resources, WDFCMRESLIST ResourcesTranslated) @@ -208,7 +311,7 @@ netuio_map_hw_resources(WDFDEVICE Device, WDFCMRESLIST Resources, WDFCMRESLIST R if (descriptor == NULL) { status = STATUS_DEVICE_CONFIGURATION_ERROR; - goto end; + goto end_of_loop; } } while ((descriptor->Type != CmResourceTypeMemory) || !(descriptor->Flags & CM_RESOURCE_MEMORY_BAR)); @@ -237,8 +340,9 @@ netuio_map_hw_resources(WDFDEVICE Device, WDFCMRESLIST Resources, WDFCMRESLIST R ctx->dpdk_hw[bar_index].mem.size = ctx->bar[bar_index].size; } // for bar_index +end_of_loop: - status = STATUS_SUCCESS; + status = netuio_intr_alloc(Device, Resources, ResourcesTranslated); end: if (status != STATUS_SUCCESS) { @@ -267,6 +371,7 @@ netuio_free_hw_resources(WDFDEVICE Device) MmUnmapIoSpace(ctx->bar[bar_index].virt_addr, ctx->bar[bar_index].size); } } + /* Interrupt objects are released with the device. */ RtlZeroMemory(ctx->dpdk_hw, sizeof(ctx->dpdk_hw)); RtlZeroMemory(ctx->bar, sizeof(ctx->bar)); diff --git a/windows/netuio/netuio_dev.h b/windows/netuio/netuio_dev.h index 6285114..1c8a6fa 100644 --- a/windows/netuio/netuio_dev.h +++ b/windows/netuio/netuio_dev.h @@ -24,6 +24,13 @@ struct dev_addr { USHORT func_num; }; +/** Interrupt vector associated data. */ +typedef struct NETUIO_INTR_CTX { + WDFQUEUE queue; /**< Queue of requests for this interrupt. */ +} NETUIO_INTR_CTX; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(NETUIO_INTR_CTX, netuio_intr_ctx_get); + /** * The device context performs the same job as a WDM device extension in the driver frameworks */ @@ -34,6 +41,9 @@ typedef struct _NETUIO_CONTEXT_DATA struct pci_bar bar[PCI_MAX_BAR]; /**< device BARs */ struct dev_addr addr; /**< B:D:F details of device */ struct mem_map_region dpdk_hw[PCI_MAX_BAR]; /**< mapped region for the device's register space */ + BOOLEAN intr_enabled; /**< Whether interrupt delivery is enabled. */ + WDFINTERRUPT *intr; /**< Interrupt objects indexed by vector. */ + size_t intr_n; /**< Number of interrupt objects. */ } NETUIO_CONTEXT_DATA, *PNETUIO_CONTEXT_DATA; diff --git a/windows/netuio/netuio_interface.h b/windows/netuio/netuio_interface.h index 71f5395..b5f936f 100644 --- a/windows/netuio/netuio_interface.h +++ b/windows/netuio/netuio_interface.h @@ -50,6 +50,20 @@ DEFINE_GUID (GUID_DEVINTERFACE_netUIO, 0x08336f60,0x0679,0x4c6c,0x85,0xd2,0xae,0 */ #define IOCTL_NETUIO_PCI_CONFIG_IO CTL_CODE(FILE_DEVICE_NETWORK, 52, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) +/** + * Control whether interrupts are enabled. + * Input: struct netuio_intr_control + * Output: N/A + */ +#define IOCTL_NETUIO_INTR_CONTROL CTL_CODE(FILE_DEVICE_NETWORK, 53, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/** + * Wait until an interrupt occurs. + * Input: struct netuio_intr_query + * Output: N/A + */ +#define IOCTL_NETUIO_INTR_QUERY CTL_CODE(FILE_DEVICE_NETWORK, 54, METHOD_BUFFERED, FILE_ANY_ACCESS) + struct mem_region { UINT64 size; /**< memory region size */ LARGE_INTEGER phys_addr; /**< physical address of the memory region */ @@ -83,6 +97,14 @@ struct dpdk_pci_config_io } data; /**< Data to be written, in case of write operations */ }; +struct netuio_intr_control { + UINT32 enable; /**< 1 to enable interrupts, 0 to disable */ +}; + +struct netuio_intr_query { + UINT32 vector; /**< Vector of the interrupt to wait for. */ +}; + #pragma pack(pop) #endif // NETUIO_INTERFACE_H diff --git a/windows/netuio/netuio_queue.c b/windows/netuio/netuio_queue.c index 0583394..1f9eb9e 100644 --- a/windows/netuio/netuio_queue.c +++ b/windows/netuio/netuio_queue.c @@ -245,37 +245,49 @@ end: return; } -/* -Routine Description: - This event is invoked when the framework receives IRP_MJ_DEVICE_CONTROL request. - -Return Value: - None - */ -_Use_decl_annotations_ -VOID -netuio_evt_IO_device_control(WDFQUEUE Queue, WDFREQUEST Request, - size_t OutputBufferLength, size_t InputBufferLength, - ULONG IoControlCode) +static void +netuio_ioctl_intr_control(NETUIO_CONTEXT_DATA *ctx, WDFREQUEST request) { - UNREFERENCED_PARAMETER(OutputBufferLength); - UNREFERENCED_PARAMETER(InputBufferLength); - - NTSTATUS status = STATUS_SUCCESS; - PVOID input_buf = NULL, output_buf = NULL; - size_t input_buf_size, output_buf_size; - size_t bytes_returned = 0; - - WDFDEVICE device = WdfIoQueueGetDevice(Queue); + struct netuio_intr_control *in; + NTSTATUS status; + + status = WdfRequestRetrieveInputBuffer(request, sizeof(*in), &in, NULL); + if (!NT_SUCCESS(status)) + goto exit; + ctx->intr_enabled = in->enable != 0; + status = STATUS_SUCCESS; +exit: + WdfRequestComplete(request, status); +} - PNETUIO_CONTEXT_DATA ctx; - ctx = netuio_get_context_data(device); +static void +netuio_ioctl_intr_query(NETUIO_CONTEXT_DATA *ctx, WDFREQUEST request) +{ + struct netuio_intr_query *in; + NETUIO_INTR_CTX *intr_ctx; + NTSTATUS status; + + status = WdfRequestRetrieveInputBuffer(request, sizeof(*in), &in, NULL); + if (!NT_SUCCESS(status)) + goto error; + if (in->vector >= ctx->intr_n) { + status = STATUS_INVALID_PARAMETER; + goto error; + } + intr_ctx = netuio_intr_ctx_get(ctx->intr[in->vector]); + WdfRequestForwardToIoQueue(request, intr_ctx->queue); + return; +error: + WdfRequestComplete(request, status); +} - if (IoControlCode != IOCTL_NETUIO_PCI_CONFIG_IO) - { - status = STATUS_INVALID_DEVICE_REQUEST; - goto end; - } +static void +netuio_ioctl_pci_config_io(NETUIO_CONTEXT_DATA *ctx, WDFREQUEST Request) +{ + PVOID input_buf = NULL, output_buf = NULL; + size_t input_buf_size, output_buf_size; + size_t bytes_returned = 0; + NTSTATUS status; // First retrieve the input buffer and see if it matches our device status = WdfRequestRetrieveInputBuffer(Request, sizeof(struct dpdk_pci_config_io), &input_buf, &input_buf_size); @@ -298,7 +310,6 @@ netuio_evt_IO_device_control(WDFQUEUE Queue, WDFREQUEST Request, if (!NT_SUCCESS(status)) { goto end; } - ASSERT(output_buf_size == OutputBufferLength); if (dpdk_pci_io_input->op == PCI_IO_READ) { *(UINT64 *)output_buf = 0; @@ -329,6 +340,37 @@ netuio_evt_IO_device_control(WDFQUEUE Queue, WDFREQUEST Request, end: WdfRequestCompleteWithInformation(Request, status, bytes_returned); +} - return; +/* +Routine Description: + This event is invoked when the framework receives IRP_MJ_DEVICE_CONTROL request. + +Return Value: + None + */ +_Use_decl_annotations_ +VOID +netuio_evt_IO_device_control(WDFQUEUE Queue, WDFREQUEST Request, + size_t OutputBufferLength, size_t InputBufferLength, + ULONG IoControlCode) +{ + WDFDEVICE device = WdfIoQueueGetDevice(Queue); + PNETUIO_CONTEXT_DATA ctx = netuio_get_context_data(device); + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + switch (IoControlCode) { + case IOCTL_NETUIO_PCI_CONFIG_IO: + netuio_ioctl_pci_config_io(ctx, Request); + break; + case IOCTL_NETUIO_INTR_CONTROL: + netuio_ioctl_intr_control(ctx, Request); + break; + case IOCTL_NETUIO_INTR_QUERY: + netuio_ioctl_intr_query(ctx, Request); + break; + default: + WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST); + } }