eal/linux: add epoll wrappers

The patch adds 'rte_epoll_wait' and 'rte_epoll_ctl' for async event wakeup.
It defines 'struct rte_epoll_event' as the event param.
When the event fds add to a specified epoll instance, 'eptrs' will hold
the rte_epoll_event object pointer.
The 'op' uses the same enum as epoll_wait/ctl does.
The epoll event support to carry a raw user data and to register a callback
which is executed during wakeup.

Signed-off-by: Cunming Liang <cunming.liang@intel.com>
This commit is contained in:
Cunming Liang 2015-07-20 11:02:18 +08:00 committed by Thomas Monjalon
parent d37641029a
commit 9efe9c6cdc
3 changed files with 222 additions and 0 deletions

View File

@ -69,6 +69,8 @@
#define EAL_INTR_EPOLL_WAIT_FOREVER (-1)
static RTE_DEFINE_PER_LCORE(int, _epfd) = -1; /**< epoll fd per thread */
/**
* union for pipe fds.
*/
@ -896,3 +898,140 @@ rte_eal_intr_init(void)
return -ret;
}
static int
eal_epoll_process_event(struct epoll_event *evs, unsigned int n,
struct rte_epoll_event *events)
{
unsigned int i, count = 0;
struct rte_epoll_event *rev;
for (i = 0; i < n; i++) {
rev = evs[i].data.ptr;
if (!rev || !rte_atomic32_cmpset(&rev->status, RTE_EPOLL_VALID,
RTE_EPOLL_EXEC))
continue;
events[count].status = RTE_EPOLL_VALID;
events[count].fd = rev->fd;
events[count].epfd = rev->epfd;
events[count].epdata.event = rev->epdata.event;
events[count].epdata.data = rev->epdata.data;
if (rev->epdata.cb_fun)
rev->epdata.cb_fun(rev->fd,
rev->epdata.cb_arg);
rte_compiler_barrier();
rev->status = RTE_EPOLL_VALID;
count++;
}
return count;
}
static inline int
eal_init_tls_epfd(void)
{
int pfd = epoll_create(255);
if (pfd < 0) {
RTE_LOG(ERR, EAL,
"Cannot create epoll instance\n");
return -1;
}
return pfd;
}
int
rte_intr_tls_epfd(void)
{
if (RTE_PER_LCORE(_epfd) == -1)
RTE_PER_LCORE(_epfd) = eal_init_tls_epfd();
return RTE_PER_LCORE(_epfd);
}
int
rte_epoll_wait(int epfd, struct rte_epoll_event *events,
int maxevents, int timeout)
{
struct epoll_event evs[maxevents];
int rc;
if (!events) {
RTE_LOG(ERR, EAL, "rte_epoll_event can't be NULL\n");
return -1;
}
/* using per thread epoll fd */
if (epfd == RTE_EPOLL_PER_THREAD)
epfd = rte_intr_tls_epfd();
while (1) {
rc = epoll_wait(epfd, evs, maxevents, timeout);
if (likely(rc > 0)) {
/* epoll_wait has at least one fd ready to read */
rc = eal_epoll_process_event(evs, rc, events);
break;
} else if (rc < 0) {
if (errno == EINTR)
continue;
/* epoll_wait fail */
RTE_LOG(ERR, EAL, "epoll_wait returns with fail %s\n",
strerror(errno));
rc = -1;
break;
}
}
return rc;
}
static inline void
eal_epoll_data_safe_free(struct rte_epoll_event *ev)
{
while (!rte_atomic32_cmpset(&ev->status, RTE_EPOLL_VALID,
RTE_EPOLL_INVALID))
while (ev->status != RTE_EPOLL_VALID)
rte_pause();
memset(&ev->epdata, 0, sizeof(ev->epdata));
ev->fd = -1;
ev->epfd = -1;
}
int
rte_epoll_ctl(int epfd, int op, int fd,
struct rte_epoll_event *event)
{
struct epoll_event ev;
if (!event) {
RTE_LOG(ERR, EAL, "rte_epoll_event can't be NULL\n");
return -1;
}
/* using per thread epoll fd */
if (epfd == RTE_EPOLL_PER_THREAD)
epfd = rte_intr_tls_epfd();
if (op == EPOLL_CTL_ADD) {
event->status = RTE_EPOLL_VALID;
event->fd = fd; /* ignore fd in event */
event->epfd = epfd;
ev.data.ptr = (void *)event;
}
ev.events = event->epdata.event;
if (epoll_ctl(epfd, op, fd, &ev) < 0) {
RTE_LOG(ERR, EAL, "Error op %d fd %d epoll_ctl, %s\n",
op, fd, strerror(errno));
if (op == EPOLL_CTL_ADD)
/* rollback status when CTL_ADD fail */
event->status = RTE_EPOLL_INVALID;
return -1;
}
if (op == EPOLL_CTL_DEL && event->status != RTE_EPOLL_INVALID)
eal_epoll_data_safe_free(event);
return 0;
}

View File

@ -51,6 +51,32 @@ enum rte_intr_handle_type {
RTE_INTR_HANDLE_MAX
};
#define RTE_INTR_EVENT_ADD 1UL
#define RTE_INTR_EVENT_DEL 2UL
typedef void (*rte_intr_event_cb_t)(int fd, void *arg);
struct rte_epoll_data {
uint32_t event; /**< event type */
void *data; /**< User data */
rte_intr_event_cb_t cb_fun; /**< IN: callback fun */
void *cb_arg; /**< IN: callback arg */
};
enum {
RTE_EPOLL_INVALID = 0,
RTE_EPOLL_VALID,
RTE_EPOLL_EXEC,
};
/** interrupt epoll event obj, taken by epoll_event.ptr */
struct rte_epoll_event {
volatile uint32_t status; /**< OUT: event status */
int fd; /**< OUT: event fd */
int epfd; /**< OUT: epoll instance the ev associated with */
struct rte_epoll_data epdata;
};
/** Handle for interrupts. */
struct rte_intr_handle {
union {
@ -64,8 +90,62 @@ struct rte_intr_handle {
uint32_t max_intr; /**< max interrupt requested */
uint32_t nb_efd; /**< number of available efd(event fd) */
int efds[RTE_MAX_RXTX_INTR_VEC_ID]; /**< intr vectors/efds mapping */
struct rte_epoll_event elist[RTE_MAX_RXTX_INTR_VEC_ID];
/**< intr vector epoll event */
int *intr_vec; /**< intr vector number array */
#endif
};
#define RTE_EPOLL_PER_THREAD -1 /**< to hint using per thread epfd */
/**
* It waits for events on the epoll instance.
*
* @param epfd
* Epoll instance fd on which the caller wait for events.
* @param events
* Memory area contains the events that will be available for the caller.
* @param maxevents
* Up to maxevents are returned, must greater than zero.
* @param timeout
* Specifying a timeout of -1 causes a block indefinitely.
* Specifying a timeout equal to zero cause to return immediately.
* @return
* - On success, returns the number of available event.
* - On failure, a negative value.
*/
int
rte_epoll_wait(int epfd, struct rte_epoll_event *events,
int maxevents, int timeout);
/**
* It performs control operations on epoll instance referred by the epfd.
* It requests that the operation op be performed for the target fd.
*
* @param epfd
* Epoll instance fd on which the caller perform control operations.
* @param op
* The operation be performed for the target fd.
* @param fd
* The target fd on which the control ops perform.
* @param event
* Describes the object linked to the fd.
* Note: The caller must take care the object deletion after CTL_DEL.
* @return
* - On success, zero.
* - On failure, a negative value.
*/
int
rte_epoll_ctl(int epfd, int op, int fd,
struct rte_epoll_event *event);
/**
* The function returns the per thread epoll instance.
*
* @return
* epfd the epoll instance referred to.
*/
int
rte_intr_tls_epfd(void);
#endif /* _RTE_LINUXAPP_INTERRUPTS_H_ */

View File

@ -118,6 +118,9 @@ DPDK_2.1 {
rte_eal_pci_detach;
rte_eal_pci_read_config;
rte_eal_pci_write_config;
rte_epoll_ctl;
rte_epoll_wait;
rte_intr_tls_epfd;
rte_memzone_free;
} DPDK_2.0;