// // Copyright 2018 Ettus Research, a National Instruments Company // // SPDX-License-Identifier: GPL-3.0-or-later // #include "uhd_dpdk_fops.h" #include "uhd_dpdk_udp.h" #include #include /************************************************ * I/O thread ONLY * * TODO: Decide whether to allow blocking on mutex * This would cause the I/O thread to sleep, which isn't desireable * Could throw in a "request completion cleanup" section in I/O thread's * main loop, though. Just keep trying until the requesting thred is woken * up. This would be to handle the case where the thread hadn't finished * setting itself up to wait on the condition variable, but the I/O thread * still got the request. */ int _uhd_dpdk_config_req_compl(struct uhd_dpdk_config_req *req, int retval) { req->retval = retval; int stat = pthread_mutex_trylock(&req->mutex); if (stat) { RTE_LOG(ERR, USER1, "%s: Could not lock req mutex\n", __func__); return stat; } stat = pthread_cond_signal(&req->cond); pthread_mutex_unlock(&req->mutex); if (stat) { RTE_LOG(ERR, USER1, "%s: Could not signal req cond\n", __func__); return stat; } return 0; } int _uhd_dpdk_sock_setup(struct uhd_dpdk_config_req *req) { int stat = 0; switch (req->sock_type) { case UHD_DPDK_SOCK_UDP: stat = _uhd_dpdk_udp_setup(req); break; default: stat = -EINVAL; _uhd_dpdk_config_req_compl(req, -EINVAL); } return stat; } int _uhd_dpdk_sock_release(struct uhd_dpdk_config_req *req) { int stat = 0; switch (req->sock_type) { case UHD_DPDK_SOCK_UDP: stat = _uhd_dpdk_udp_release(req); break; default: stat = -EINVAL; _uhd_dpdk_config_req_compl(req, -EINVAL); } return stat; } /************************************************ * API calls */ struct uhd_dpdk_socket* uhd_dpdk_sock_open(unsigned int portid, enum uhd_dpdk_sock_type t, void *sockarg) { if (!ctx || (t >= UHD_DPDK_SOCK_TYPE_COUNT)) { return NULL; } struct uhd_dpdk_port *port = find_port(portid); if (!port) { return NULL; } if (!port->ipv4_addr) { RTE_LOG(WARNING, EAL, "Please set IPv4 address for port %u before opening socket\n", portid); return NULL; } struct uhd_dpdk_config_req *req = (struct uhd_dpdk_config_req *) rte_zmalloc(NULL, sizeof(*req), 0); if (!req) { return NULL; } struct uhd_dpdk_socket *s = (struct uhd_dpdk_socket *) rte_zmalloc(NULL, sizeof(*s), 0); if (!s) { goto sock_open_end; } s->port = port; req->sock = s; req->req_type = UHD_DPDK_SOCK_OPEN; req->sock_type = t; req->retval = -ETIMEDOUT; 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); switch (t) { case UHD_DPDK_SOCK_UDP: uhd_dpdk_udp_open(req, sockarg); break; default: break; } if (req->retval) { rte_free(s); s = NULL; } sock_open_end: rte_free(req); return s; } int uhd_dpdk_sock_close(struct uhd_dpdk_socket *sock) { if (!ctx || !sock) return -EINVAL; struct uhd_dpdk_config_req *req = (struct uhd_dpdk_config_req *) rte_zmalloc(NULL, sizeof(*req), 0); if (!req) return -ENOMEM; req->sock = sock; req->req_type = UHD_DPDK_SOCK_CLOSE; req->sock_type = sock->sock_type; req->retval = -ETIMEDOUT; pthread_mutex_init(&req->mutex, NULL); pthread_cond_init(&req->cond, NULL); switch (sock->sock_type) { case UHD_DPDK_SOCK_UDP: uhd_dpdk_udp_close(req); break; default: break; } if (req->retval) { rte_free(req); return req->retval; } rte_free(sock); return 0; } /* * TODO: * Add blocking calls with timeout * Implementation would involve a condition variable, like config reqs * Also would create a cleanup section in I/O main loop (like config reqs) */ int uhd_dpdk_request_tx_bufs(struct uhd_dpdk_socket *sock, struct rte_mbuf **bufs, unsigned int num_bufs) { if (!sock || !bufs || !num_bufs) { return -EINVAL; } *bufs = NULL; if (!sock->tx_ring) return -EINVAL; unsigned int num_tx = rte_ring_count(sock->rx_ring); num_tx = (num_tx < num_bufs) ? num_tx : num_bufs; if (rte_ring_dequeue_bulk(sock->rx_ring, (void **) bufs, num_tx, NULL) == 0) return -ENOENT; sock->tx_buf_count += num_tx; return num_tx; } int uhd_dpdk_send(struct uhd_dpdk_socket *sock, struct rte_mbuf **bufs, unsigned int num_bufs) { if (!sock || !bufs || !num_bufs) return -EINVAL; if (!sock->tx_ring) return -EINVAL; unsigned int num_tx = rte_ring_free_count(sock->tx_ring); num_tx = (num_tx < num_bufs) ? num_tx : num_bufs; switch (sock->sock_type) { case UHD_DPDK_SOCK_UDP: for (unsigned int i = 0; i < num_tx; i++) { uhd_dpdk_udp_prep(sock, bufs[i]); } break; default: RTE_LOG(ERR, USER1, "%s: Unsupported sock type\n", __func__); return -EINVAL; } int status = rte_ring_enqueue_bulk(sock->tx_ring, (void **) bufs, num_tx, NULL); if (status == 0) { RTE_LOG(ERR, USER1, "Invalid shared usage of TX ring detected\n"); return status; } sock->tx_buf_count -= num_tx; return num_tx; } /* * TODO: * Add blocking calls with timeout */ int uhd_dpdk_recv(struct uhd_dpdk_socket *sock, struct rte_mbuf **bufs, unsigned int num_bufs, int timeout) { if (!sock || !bufs || !num_bufs) return -EINVAL; if (!sock->rx_ring) return -EINVAL; unsigned int num_rx = rte_ring_count(sock->rx_ring); num_rx = (num_rx < num_bufs) ? num_rx : num_bufs; /* if ((timeout > 0) && (num_rx != num_bufs)) { // Wait for enough bufs } else*/ if (num_rx) { unsigned int avail = 0; unsigned int status = rte_ring_dequeue_bulk(sock->rx_ring, (void **) bufs, num_rx, &avail); if (status == 0) { RTE_LOG(ERR, USER1, "Invalid shared usage of RX ring detected\n"); RTE_LOG(ERR, USER1, "Requested %u, but %u available\n", num_rx, avail); return -ENOENT; } } return num_rx; } void uhd_dpdk_free_buf(struct rte_mbuf *buf) { rte_pktmbuf_free(buf); } void * uhd_dpdk_buf_to_data(struct uhd_dpdk_socket *sock, struct rte_mbuf *buf) { if (!sock || !buf) return NULL; /* TODO: Support for more types? */ switch (sock->sock_type) { case UHD_DPDK_SOCK_UDP: return rte_pktmbuf_mtod_offset(buf, void *, sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr)); default: return NULL; } } int uhd_dpdk_get_len(struct uhd_dpdk_socket *sock, struct rte_mbuf *buf) { if (!sock || !buf) return -EINVAL; if (sock->sock_type != UHD_DPDK_SOCK_UDP) return -EINVAL; struct udp_hdr *hdr = (struct udp_hdr *) ((uint8_t *) uhd_dpdk_buf_to_data(sock, buf) - sizeof(struct udp_hdr)); if (!hdr) return -EINVAL; /* Report dgram length - header */ return ntohs(hdr->dgram_len) - 8; } int uhd_dpdk_get_src_ipv4(struct uhd_dpdk_socket *sock, struct rte_mbuf *buf, uint32_t *ipv4_addr) { if (!sock || !buf || !ipv4_addr) return -EINVAL; if (sock->sock_type != UHD_DPDK_SOCK_UDP) return -EINVAL; struct ipv4_hdr *hdr = rte_pktmbuf_mtod_offset(buf, struct ipv4_hdr *, sizeof(struct ether_hdr)); *ipv4_addr = hdr->src_addr; return 0; } int uhd_dpdk_get_drop_count(struct uhd_dpdk_socket *sock, uint32_t *count) { if (!sock) return -EINVAL; if (sock->sock_type != UHD_DPDK_SOCK_UDP) return -EINVAL; if (!sock->priv) return -ENODEV; struct uhd_dpdk_udp_priv *pdata = (struct uhd_dpdk_udp_priv *) sock->priv; *count = pdata->dropped_pkts; return 0; }