/*-
* BSD LICENSE
*
- * Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
* All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
* are met:
*
- * * Redistributions of source code must retain the above copyright
+ * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
* distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
*/
#include <stdio.h>
TAILQ_ENTRY(rte_intr_source) next;
struct rte_intr_handle intr_handle; /**< interrupt handle */
struct rte_intr_cb_list callbacks; /**< user callbacks */
+ uint32_t active;
};
/* global spinlock for interrupt data operation */
rte_intr_callback_register(struct rte_intr_handle *intr_handle,
rte_intr_callback_fn cb, void *cb_arg)
{
- int ret = -1;
+ int ret, wake_thread;
struct rte_intr_source *src;
- int wake_thread = 0;
+ struct rte_intr_callback *callback;
+
+ wake_thread = 0;
/* first do parameter checking */
if (intr_handle == NULL || intr_handle->fd < 0 || cb == NULL) {
}
/* allocate a new interrupt callback entity */
- struct rte_intr_callback *callback =
- rte_zmalloc("interrupt callback list",
+ callback = rte_zmalloc("interrupt callback list",
sizeof(*callback), 0);
if (callback == NULL) {
RTE_LOG(ERR, EAL, "Can not allocate memory\n");
rte_spinlock_lock(&intr_lock);
/* check if there is at least one callback registered for the fd */
- TAILQ_FOREACH(src, &intr_sources, next)
- if (src->intr_handle.fd == intr_handle->fd) {
- if (src->callbacks.tqh_first == NULL)
+ TAILQ_FOREACH(src, &intr_sources, next) {
+ if (src->intr_handle.fd == intr_handle->fd) {
/* we had no interrupts for this */
- wake_thread = 1;
+ if TAILQ_EMPTY(&src->callbacks)
+ wake_thread = 1;
- TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
- break;
+ TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
+ ret = 0;
+ break;
+ }
}
- /* No callback registered for this fd */
- if (src == NULL){
- /* no existing callbacks for this - add new source */
- src = rte_zmalloc("interrupt source list", sizeof(*src), 0);
- if (src == NULL){
+ /* no existing callbacks for this - add new source */
+ if (src == NULL) {
+ if ((src = rte_zmalloc("interrupt source list",
+ sizeof(*src), 0)) == NULL) {
RTE_LOG(ERR, EAL, "Can not allocate memory\n");
+ rte_free(callback);
ret = -ENOMEM;
- goto error;
+ } else {
+ src->intr_handle = *intr_handle;
+ TAILQ_INIT(&src->callbacks);
+ TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
+ TAILQ_INSERT_TAIL(&intr_sources, src, next);
+ wake_thread = 1;
+ ret = 0;
}
- src->intr_handle = *intr_handle;
- TAILQ_INIT(&src->callbacks);
-
- TAILQ_INSERT_TAIL(&intr_sources, src, next);
- TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
- wake_thread = 1;
}
rte_spinlock_unlock(&intr_lock);
+
/**
* check if need to notify the pipe fd waited by epoll_wait to
* rebuild the wait list.
if (write(intr_pipe.writefd, "1", 1) < 0)
return -EPIPE;
- return 0;
-
-error:
- rte_spinlock_unlock(&intr_lock);
-
- return ret;
+ return (ret);
}
int
rte_intr_callback_unregister(struct rte_intr_handle *intr_handle,
rte_intr_callback_fn cb_fn, void *cb_arg)
{
- int ret = -1;
+ int ret;
struct rte_intr_source *src;
- struct rte_intr_callback *cb;
+ struct rte_intr_callback *cb, *next;
/* do parameter checking first */
if (intr_handle == NULL || intr_handle->fd < 0) {
/* No interrupt source registered for the fd */
if (src == NULL) {
ret = -ENOENT;
- goto error;
- }
- ret = 0;
- TAILQ_FOREACH(cb, &src->callbacks, next) {
- if (cb->cb_fn != cb_fn)
- continue;
- if (cb_arg == (void *)-1 || cb->cb_arg == cb_arg) {
- TAILQ_REMOVE(&src->callbacks, cb, next);
- rte_free(cb);
- ret ++;
+ /* interrupt source has some active callbacks right now. */
+ } else if (src->active != 0) {
+ ret = -EAGAIN;
+
+ /* ok to remove. */
+ } else {
+ ret = 0;
+
+ /*walk through the callbacks and remove all that match. */
+ for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
+
+ next = TAILQ_NEXT(cb, next);
+
+ if (cb->cb_fn == cb_fn && (cb_arg == (void *)-1 ||
+ cb->cb_arg == cb_arg)) {
+ TAILQ_REMOVE(&src->callbacks, cb, next);
+ rte_free(cb);
+ ret++;
+ }
}
- if (src->callbacks.tqh_first == NULL) {
+ /* all callbacks for that source are removed. */
+ if (TAILQ_EMPTY(&src->callbacks)) {
TAILQ_REMOVE(&intr_sources, src, next);
rte_free(src);
}
}
+ rte_spinlock_unlock(&intr_lock);
+
/* notify the pipe fd waited by epoll_wait to rebuild the wait list */
- if (write(intr_pipe.writefd, "1", 1) < 0) {
+ if (ret >= 0 && write(intr_pipe.writefd, "1", 1) < 0) {
ret = -EPIPE;
- goto error;
}
- rte_spinlock_unlock(&intr_lock);
-
- return ret;
-
-error:
- rte_spinlock_unlock(&intr_lock);
-
- return ret;
+ return (ret);
}
int
static int
eal_intr_process_interrupts(struct epoll_event *events, int nfds)
{
- int n, i, active_cb, bytes_read;
+ int n, bytes_read;
struct rte_intr_source *src;
struct rte_intr_callback *cb;
union rte_intr_read_buffer buf;
- struct rte_intr_callback active_cbs[32];
+ struct rte_intr_callback active_cb;
for (n = 0; n < nfds; n++) {
+
/**
* if the pipe fd is ready to read, return out to
* rebuild the wait list.
continue;
}
- /* for this source, make a copy of all the callbacks,
- * then unlock the lock, so the callbacks can
- * themselves manipulate the list for future
- * instances.
- */
- active_cb = 0;
- memset(active_cbs, 0, sizeof(active_cbs));
- TAILQ_FOREACH(cb, &src->callbacks, next)
- active_cbs[active_cb++] = *cb;
+ /* mark this interrupt source as active and release the lock. */
+ src->active = 1;
rte_spinlock_unlock(&intr_lock);
/* set the length to be read dor different handle type */
bytes_read = 1;
break;
}
+
/**
* read out to clear the ready-to-be-read flag
* for epoll_wait.
*/
bytes_read = read(events[n].data.fd, &buf, bytes_read);
- if (bytes_read < 0) {
- RTE_LOG(ERR, EAL, "Error reading from file descriptor"
- " %d, error: %d\n", events[n].data.fd, errno);
- continue;
- }
- else if (bytes_read == 0) {
- RTE_LOG(ERR, EAL,
- "Read nothing from file descriptor %d.\n",
- events[n].data.fd);
- continue;
- }
- /**
- * Finally, call all callbacks from the copy
- * we made earlier.
- */
- for (i = 0; i < active_cb; i++) {
- if (active_cbs[i].cb_fn == NULL)
- continue;
- active_cbs[i].cb_fn(&src->intr_handle,
- active_cbs[i].cb_arg);
+
+ if (bytes_read < 0)
+ RTE_LOG(ERR, EAL, "Error reading from file "
+ "descriptor %d: %s\n", events[n].data.fd,
+ strerror(errno));
+ else if (bytes_read == 0)
+ RTE_LOG(ERR, EAL, "Read nothing from file "
+ "descriptor %d\n", events[n].data.fd);
+
+ /* grab a lock, again to call callbacks and update status. */
+ rte_spinlock_lock(&intr_lock);
+
+ if (bytes_read > 0) {
+
+ /* Finally, call all callbacks. */
+ TAILQ_FOREACH(cb, &src->callbacks, next) {
+
+ /* make a copy and unlock. */
+ active_cb = *cb;
+ rte_spinlock_unlock(&intr_lock);
+
+ /* call the actual callback */
+ active_cb.cb_fn(&src->intr_handle,
+ active_cb.cb_arg);
+
+ /*get the lcok back. */
+ rte_spinlock_lock(&intr_lock);
+ }
}
+
+ /* we done with that interrupt source, release it. */
+ src->active = 0;
+ rte_spinlock_unlock(&intr_lock);
}
return 0;