eal/windows: add interrupt thread skeleton
Windows interrupt support is based on IO completion ports (IOCP). Interrupt thread would send the devices requests to notify about interrupts and then wait for any request completion. Add skeleton code of this model without any hardware support. Another way to wake up the interrupt thread is APC (asynchronous procedure call), scheduled by any other thread via eal_intr_thread_schedule(). This internal API is intended for alarm implementation. Signed-off-by: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com> Acked-by: Narcisa Vasile <navasile@linux.microsoft.com>
This commit is contained in:
parent
c76ec01b45
commit
5c016fc020
@ -69,10 +69,18 @@ struct rte_epoll_event {
|
||||
struct rte_intr_handle {
|
||||
RTE_STD_C11
|
||||
union {
|
||||
int vfio_dev_fd; /**< VFIO device file descriptor */
|
||||
int uio_cfg_fd; /**< UIO cfg file desc for uio_pci_generic */
|
||||
struct {
|
||||
RTE_STD_C11
|
||||
union {
|
||||
/** VFIO device file descriptor */
|
||||
int vfio_dev_fd;
|
||||
/** UIO cfg file desc for uio_pci_generic */
|
||||
int uio_cfg_fd;
|
||||
};
|
||||
int fd; /**< interrupt event file descriptor */
|
||||
};
|
||||
void *handle; /**< device driver handle (Windows) */
|
||||
};
|
||||
int fd; /**< interrupt event file descriptor */
|
||||
enum rte_intr_handle_type type; /**< handle type */
|
||||
uint32_t max_intr; /**< max interrupt requested */
|
||||
uint32_t nb_efd; /**< number of available efd(event fd) */
|
||||
|
@ -253,6 +253,8 @@ EXPORTS
|
||||
rte_mcfg_timer_unlock
|
||||
rte_mcfg_get_single_file_segments
|
||||
|
||||
rte_thread_is_intr
|
||||
|
||||
__rte_eal_trace_alarm_cancel
|
||||
__rte_eal_trace_alarm_set
|
||||
__rte_eal_trace_generic_double
|
||||
|
@ -344,6 +344,11 @@ rte_eal_init(int argc, char **argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rte_eal_intr_init() < 0) {
|
||||
rte_eal_init_alert("Cannot init interrupt-handling thread");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rte_eal_timer_init() < 0) {
|
||||
rte_eal_init_alert("Cannot init TSC timer");
|
||||
rte_errno = EFAULT;
|
||||
|
@ -4,6 +4,81 @@
|
||||
|
||||
#include <rte_interrupts.h>
|
||||
|
||||
#include "eal_private.h"
|
||||
#include "eal_windows.h"
|
||||
|
||||
static pthread_t intr_thread;
|
||||
|
||||
static HANDLE intr_iocp;
|
||||
|
||||
static void
|
||||
eal_intr_process(const OVERLAPPED_ENTRY *event)
|
||||
{
|
||||
RTE_SET_USED(event);
|
||||
}
|
||||
|
||||
static void *
|
||||
eal_intr_thread_main(LPVOID arg __rte_unused)
|
||||
{
|
||||
while (1) {
|
||||
OVERLAPPED_ENTRY events[16];
|
||||
ULONG event_count, i;
|
||||
BOOL result;
|
||||
|
||||
result = GetQueuedCompletionStatusEx(
|
||||
intr_iocp, events, RTE_DIM(events), &event_count,
|
||||
INFINITE, /* no timeout */
|
||||
TRUE); /* alertable wait for alarm APCs */
|
||||
|
||||
if (!result) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != WAIT_IO_COMPLETION) {
|
||||
RTE_LOG_WIN32_ERR("GetQueuedCompletionStatusEx()");
|
||||
RTE_LOG(ERR, EAL, "Failed waiting for interrupts\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* No I/O events, all work is done in completed APCs. */
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < event_count; i++)
|
||||
eal_intr_process(&events[i]);
|
||||
}
|
||||
|
||||
CloseHandle(intr_iocp);
|
||||
intr_iocp = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
rte_eal_intr_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
intr_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
|
||||
if (intr_iocp == NULL) {
|
||||
RTE_LOG_WIN32_ERR("CreateIoCompletionPort()");
|
||||
RTE_LOG(ERR, EAL, "Cannot create interrupt IOCP\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = rte_ctrl_thread_create(&intr_thread, "eal-intr-thread", NULL,
|
||||
eal_intr_thread_main, NULL);
|
||||
if (ret != 0) {
|
||||
rte_errno = -ret;
|
||||
RTE_LOG(ERR, EAL, "Cannot create interrupt thread\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
rte_thread_is_intr(void)
|
||||
{
|
||||
return pthread_equal(intr_thread, pthread_self());
|
||||
}
|
||||
|
||||
int
|
||||
rte_intr_rx_ctl(__rte_unused struct rte_intr_handle *intr_handle,
|
||||
__rte_unused int epfd, __rte_unused int op,
|
||||
@ -11,3 +86,22 @@ rte_intr_rx_ctl(__rte_unused struct rte_intr_handle *intr_handle,
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int
|
||||
eal_intr_thread_schedule(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
HANDLE handle;
|
||||
|
||||
handle = OpenThread(THREAD_ALL_ACCESS, FALSE, intr_thread);
|
||||
if (handle == NULL) {
|
||||
RTE_LOG_WIN32_ERR("OpenThread(%llu)", intr_thread);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (!QueueUserAPC((PAPCFUNC)(ULONG_PTR)func, handle, (ULONG_PTR)arg)) {
|
||||
RTE_LOG_WIN32_ERR("QueueUserAPC()");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -55,6 +55,18 @@ int eal_thread_create(pthread_t *thread);
|
||||
*/
|
||||
unsigned int eal_socket_numa_node(unsigned int socket_id);
|
||||
|
||||
/**
|
||||
* Schedule code for execution in the interrupt thread.
|
||||
*
|
||||
* @param func
|
||||
* Function to call.
|
||||
* @param arg
|
||||
* Argument to the called function.
|
||||
* @return
|
||||
* 0 on success, netagive error code on failure.
|
||||
*/
|
||||
int eal_intr_thread_schedule(void (*func)(void *arg), void *arg);
|
||||
|
||||
/**
|
||||
* Open virt2phys driver interface device.
|
||||
*
|
||||
|
@ -42,6 +42,13 @@ typedef SYNCHRONIZATION_BARRIER pthread_barrier_t;
|
||||
#define pthread_self() \
|
||||
((pthread_t)GetCurrentThreadId())
|
||||
|
||||
|
||||
static inline int
|
||||
pthread_equal(pthread_t t1, pthread_t t2)
|
||||
{
|
||||
return t1 == t2;
|
||||
}
|
||||
|
||||
static inline int
|
||||
pthread_setaffinity_np(pthread_t threadid, size_t cpuset_size,
|
||||
rte_cpuset_t *cpuset)
|
||||
|
Loading…
Reference in New Issue
Block a user