aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/transport/uhd-dpdk/uhd_dpdk_wait.c
diff options
context:
space:
mode:
authorAlex Williams <alex.williams@ni.com>2018-08-31 11:35:07 -0700
committerBrent Stapleton <brent.stapleton@ettus.com>2019-01-15 17:14:57 -0800
commite2195ac505bd423d3d2f973bbe94da1c78296aa6 (patch)
tree296ffd98c620c4ad3e313cd697891418af26cc94 /host/lib/transport/uhd-dpdk/uhd_dpdk_wait.c
parente2cde21ceb7497dcc1ef25156afa6472fe64f009 (diff)
downloaduhd-e2195ac505bd423d3d2f973bbe94da1c78296aa6.tar.gz
uhd-e2195ac505bd423d3d2f973bbe94da1c78296aa6.tar.bz2
uhd-e2195ac505bd423d3d2f973bbe94da1c78296aa6.zip
transport: Add blocking recv calls to uhd-dpdk
This adds an internal wait queue API to uhd-dpdk. Socket configuration requests had their blocking calls re-implemented on top of this API, and it is also used to service requests to wait on RX packets (w/ timeout). The wait API involves a multi-producer, single-consumer queue per I/O thread (waiter_ring), with a condition variable used for sleeping. The data structure is shared between user thread and I/O thread, and because timeouts make resource release time non-deterministic, we use reference counting on the shared resource. One reference is generated by the user thread and passed to the I/O thread to consume. A user thread that still needs the data after waking must get() another reference, to postpone the destruction of the resource until it is done. Timeouts are based on CLOCK_MONOTONIC. For recv, a timeout of 0 indicates blocking indefinitely, and a negative timeout indicates no timeout is desired. Also drop timeout for closing sockets in uhd-dpdk. The timeout would allow a user thread to pre-empt the I/O thread's cleanup process. The user thread would free data structures the I/O thread still needed to function. Since this timeout is superfluous anyway, let's just get rid of it. Also add some more input checking and error reporting.
Diffstat (limited to 'host/lib/transport/uhd-dpdk/uhd_dpdk_wait.c')
-rw-r--r--host/lib/transport/uhd-dpdk/uhd_dpdk_wait.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/host/lib/transport/uhd-dpdk/uhd_dpdk_wait.c b/host/lib/transport/uhd-dpdk/uhd_dpdk_wait.c
new file mode 100644
index 000000000..c00eaa3c4
--- /dev/null
+++ b/host/lib/transport/uhd-dpdk/uhd_dpdk_wait.c
@@ -0,0 +1,114 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+#include "uhd_dpdk_wait.h"
+
+int _uhd_dpdk_waiter_wake(struct uhd_dpdk_wait_req *req,
+ struct uhd_dpdk_thread *t)
+{
+ int stat = pthread_mutex_trylock(&req->mutex);
+ if (stat) {
+ if (rte_ring_full(t->waiter_ring)) {
+ RTE_LOG(ERR, USER2, "%s: Could not lock req mutex\n", __func__);
+ return -ENOBUFS;
+ } else {
+ req->reason = UHD_DPDK_WAIT_SIMPLE;
+ rte_ring_enqueue(t->waiter_ring, req);
+ return -EAGAIN;
+ }
+ }
+ stat = pthread_cond_signal(&req->cond);
+ if (stat)
+ RTE_LOG(ERR, USER2, "%s: Could not signal req cond\n", __func__);
+ pthread_mutex_unlock(&req->mutex);
+ uhd_dpdk_waiter_put(req);
+ return stat;
+}
+
+struct uhd_dpdk_wait_req *uhd_dpdk_waiter_alloc(enum uhd_dpdk_wait_type reason)
+{
+ struct uhd_dpdk_wait_req *req;
+ req = (struct uhd_dpdk_wait_req *) rte_zmalloc(NULL, sizeof(*req), 0);
+ if (!req)
+ return NULL;
+
+ pthread_mutex_init(&req->mutex, NULL);
+ pthread_condattr_t condattr;
+ pthread_condattr_init(&condattr);
+ pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
+ pthread_cond_init(&req->cond, &condattr);
+ rte_atomic32_set(&req->refcnt, 1);
+ req->reason = reason;
+ return req;
+}
+
+static inline void uhd_dpdk_waiter_prepare(struct uhd_dpdk_wait_req *req)
+{
+ pthread_mutex_lock(&req->mutex);
+ /* Get a reference here, to be consumed by other thread (handshake) */
+ uhd_dpdk_waiter_get(req);
+}
+
+static inline int uhd_dpdk_waiter_submit(struct uhd_dpdk_wait_req *req,
+ int timeout)
+{
+ int retval = 0;
+ if (timeout < 0) {
+ retval = pthread_cond_wait(&req->cond, &req->mutex);
+ } else {
+ struct timespec timeout_spec;
+ clock_gettime(CLOCK_MONOTONIC, &timeout_spec);
+ timeout_spec.tv_sec += timeout/1000000;
+ timeout_spec.tv_nsec += (timeout % 1000000)*1000;
+ if (timeout_spec.tv_nsec > 1000000000) {
+ timeout_spec.tv_sec++;
+ timeout_spec.tv_nsec -= 1000000000;
+ }
+ retval = pthread_cond_timedwait(&req->cond, &req->mutex, &timeout_spec);
+ }
+ return retval;
+}
+
+int uhd_dpdk_waiter_wait(struct uhd_dpdk_wait_req *req, int timeout,
+ struct uhd_dpdk_thread *t)
+{
+ int ret;
+ if (!req || !t)
+ return -EINVAL;
+
+ uhd_dpdk_waiter_prepare(req);
+
+ ret = rte_ring_enqueue(t->waiter_ring, req);
+ if (ret) {
+ uhd_dpdk_waiter_put(req);
+ pthread_mutex_unlock(&req->mutex);
+ return ret;
+ }
+
+ uhd_dpdk_waiter_submit(req, timeout);
+ pthread_mutex_unlock(&req->mutex);
+ return 0;
+}
+
+int uhd_dpdk_config_req_submit(struct uhd_dpdk_config_req *req,
+ int timeout, struct uhd_dpdk_thread *t)
+{
+ int ret;
+ if (!req || !t)
+ return -EINVAL;
+
+ uhd_dpdk_waiter_prepare(req->waiter);
+
+ ret = rte_ring_enqueue(t->sock_req_ring, req);
+ if (ret) {
+ uhd_dpdk_waiter_put(req->waiter);
+ pthread_mutex_unlock(&req->waiter->mutex);
+ return ret;
+ }
+
+ uhd_dpdk_waiter_submit(req->waiter, timeout);
+ pthread_mutex_unlock(&req->waiter->mutex);
+ return 0;
+}