[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCH v2 08/12] plat/xen/drivers/net: Configure netfront rx queue
Netfront rx queues use Xen shared rings. Incoming packets are saved in buffers allocated with user-provided callbacks. The current patch also adds the event handler for rx queue notifications. Signed-off-by: Costin Lupu <costin.lupu@xxxxxxxxx> Signed-off-by: Razvan Cojocaru <razvan.cojocaru93@xxxxxxxxx> --- plat/xen/drivers/net/netfront.c | 211 ++++++++++++++++++++++++++++++++++++++-- plat/xen/drivers/net/netfront.h | 29 ++++++ 2 files changed, 231 insertions(+), 9 deletions(-) diff --git a/plat/xen/drivers/net/netfront.c b/plat/xen/drivers/net/netfront.c index e810aefd..586c0e6a 100644 --- a/plat/xen/drivers/net/netfront.c +++ b/plat/xen/drivers/net/netfront.c @@ -52,6 +52,11 @@ static struct uk_alloc *drv_allocator; +static uint16_t xennet_rxidx(RING_IDX idx) +{ + return (uint16_t) (idx & (NET_RX_RING_SIZE - 1)); +} + static void add_id_to_freelist(uint16_t id, uint16_t *freelist) { freelist[id + 1] = freelist[0]; @@ -67,6 +72,82 @@ static uint16_t get_id_from_freelist(uint16_t *freelist) return id; } +static int netfront_rxq_enqueue(struct uk_netdev_rx_queue *rxq, + struct uk_netbuf *netbuf) +{ + RING_IDX req_prod; + uint16_t id; + netif_rx_request_t *rx_req; + struct netfront_dev *nfdev; + int notify; + + /* buffer must be page aligned */ + UK_ASSERT(((unsigned long) netbuf->data & ~PAGE_MASK) == 0); + + if (RING_FULL(&rxq->ring)) { + uk_pr_debug("rx queue is full\n"); + return -ENOSPC; + } + + /* get request */ + req_prod = rxq->ring.req_prod_pvt; + id = xennet_rxidx(req_prod); + rx_req = RING_GET_REQUEST(&rxq->ring, req_prod); + rx_req->id = id; + + /* save buffer */ + rxq->netbuf[id] = netbuf; + /* setup grant for buffer data */ + nfdev = rxq->netfront_dev; + rxq->gref[id] = rx_req->gref = + gnttab_grant_access(nfdev->xendev->otherend_id, + virt_to_mfn(netbuf->data), 0); + UK_ASSERT(rx_req->gref != GRANT_INVALID_REF); + + wmb(); /* Ensure backend sees requests */ + rxq->ring.req_prod_pvt = req_prod + 1; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&rxq->ring, notify); + if (notify) + notify_remote_via_evtchn(rxq->evtchn); + + return 0; +} + +static int netfront_rx_fillup(struct uk_netdev_rx_queue *rxq, uint16_t nb_desc) +{ + struct uk_netbuf *netbuf[nb_desc]; + int rc, status = 0; + uint16_t cnt; + + cnt = rxq->alloc_rxpkts(rxq->alloc_rxpkts_argp, netbuf, nb_desc); + + for (uint16_t i = 0; i < cnt; i++) { + rc = netfront_rxq_enqueue(rxq, netbuf[i]); + if (unlikely(rc < 0)) { + uk_pr_err("Failed to add a buffer to rx queue %p: %d\n", + rxq, rc); + + /* + * Release netbufs that we are not going + * to use anymore + */ + for (uint16_t j = i; j < cnt; j++) + uk_netbuf_free(netbuf[j]); + + status |= UK_NETDEV_STATUS_UNDERRUN; + + goto out; + } + } + + if (unlikely(cnt < nb_desc)) + status |= UK_NETDEV_STATUS_UNDERRUN; + +out: + return status; +} + static struct uk_netdev_tx_queue *netfront_txq_setup(struct uk_netdev *n, uint16_t queue_id, uint16_t nb_desc __unused, @@ -103,15 +184,19 @@ static struct uk_netdev_tx_queue *netfront_txq_setup(struct uk_netdev *n, UK_ASSERT(txq->ring_ref != GRANT_INVALID_REF); /* Setup event channel */ - rc = evtchn_alloc_unbound(nfdev->xendev->otherend_id, - NULL, NULL, - &txq->evtchn); - if (rc) { - uk_pr_err("Error creating event channel: %d\n", rc); - gnttab_end_access(txq->ring_ref); - uk_free_page(drv_allocator, sring); - return ERR2PTR(-rc); - } + if (nfdev->split_evtchn || !nfdev->rxqs[queue_id].initialized) { + rc = evtchn_alloc_unbound(nfdev->xendev->otherend_id, + NULL, NULL, + &txq->evtchn); + if (rc) { + uk_pr_err("Error creating event channel: %d\n", rc); + gnttab_end_access(txq->ring_ref); + uk_free_page(drv_allocator, sring); + return ERR2PTR(rc); + } + } else + txq->evtchn = nfdev->rxqs[queue_id].evtchn; + /* Events are always disabled for tx queue */ mask_evtchn(txq->evtchn); @@ -128,6 +213,86 @@ static struct uk_netdev_tx_queue *netfront_txq_setup(struct uk_netdev *n, return txq; } +static void netfront_handler(evtchn_port_t port __unused, + struct __regs *regs __unused, void *arg) +{ + struct uk_netdev_rx_queue *rxq = arg; + + /* Indicate to the network stack about an event */ + uk_netdev_drv_rx_event(&rxq->netfront_dev->netdev, rxq->lqueue_id); +} + +static struct uk_netdev_rx_queue *netfront_rxq_setup(struct uk_netdev *n, + uint16_t queue_id, + uint16_t nb_desc __unused, + struct uk_netdev_rxqueue_conf *conf) +{ + int rc; + struct netfront_dev *nfdev; + struct uk_netdev_rx_queue *rxq; + netif_rx_sring_t *sring; + + UK_ASSERT(n != NULL); + UK_ASSERT(conf != NULL); + + nfdev = to_netfront_dev(n); + if (queue_id >= nfdev->max_queue_pairs) { + uk_pr_err("Invalid queue identifier: %"__PRIu16"\n", queue_id); + return ERR2PTR(-EINVAL); + } + + rxq = &nfdev->rxqs[queue_id]; + UK_ASSERT(!rxq->initialized); + rxq->netfront_dev = nfdev; + rxq->lqueue_id = queue_id; + + /* Setup shared ring */ + sring = uk_malloc_page(drv_allocator); + if (!sring) + return ERR2PTR(-ENOMEM); + memset(sring, 0, PAGE_SIZE); + SHARED_RING_INIT(sring); + FRONT_RING_INIT(&rxq->ring, sring, PAGE_SIZE); + rxq->ring_size = NET_RX_RING_SIZE; + rxq->ring_ref = gnttab_grant_access(nfdev->xendev->otherend_id, + virt_to_mfn(sring), 0); + UK_ASSERT(rxq->ring_ref != GRANT_INVALID_REF); + + /* Setup event channel */ + if (nfdev->split_evtchn || !nfdev->txqs[queue_id].initialized) { + rc = evtchn_alloc_unbound(nfdev->xendev->otherend_id, + netfront_handler, rxq, + &rxq->evtchn); + if (rc) { + uk_pr_err("Error creating event channel: %d\n", rc); + gnttab_end_access(rxq->ring_ref); + uk_free_page(drv_allocator, sring); + return ERR2PTR(rc); + } + } else { + rxq->evtchn = nfdev->txqs[queue_id].evtchn; + /* overwriting event handler */ + bind_evtchn(rxq->evtchn, netfront_handler, rxq); + } + /* + * By default, events are disabled and it is up to the user or + * network stack to explicitly enable them. + */ + mask_evtchn(rxq->evtchn); + rxq->intr_enabled = 0; + + rxq->alloc_rxpkts = conf->alloc_rxpkts; + rxq->alloc_rxpkts_argp = conf->alloc_rxpkts_argp; + + /* Allocate receive buffers for this queue */ + netfront_rx_fillup(rxq, rxq->ring_size); + + rxq->initialized = true; + nfdev->rxqs_num++; + + return rxq; +} + static int netfront_rxtx_alloc(struct netfront_dev *nfdev, const struct uk_netdev_conf *conf) { @@ -195,6 +360,32 @@ exit: return rc; } +static int netfront_rxq_info_get(struct uk_netdev *n, + uint16_t queue_id, + struct uk_netdev_queue_info *qinfo) +{ + struct netfront_dev *nfdev; + struct uk_netdev_rx_queue *rxq; + int rc = 0; + + UK_ASSERT(n != NULL); + UK_ASSERT(qinfo != NULL); + + nfdev = to_netfront_dev(n); + if (unlikely(queue_id >= nfdev->rxqs_num)) { + uk_pr_err("Invalid queue id: %"__PRIu16"\n", queue_id); + rc = -EINVAL; + goto exit; + } + rxq = &nfdev->rxqs[queue_id]; + qinfo->nb_min = rxq->ring_size; + qinfo->nb_max = rxq->ring_size; + qinfo->nb_is_power_of_two = 1; + +exit: + return rc; +} + static int netfront_configure(struct uk_netdev *n, const struct uk_netdev_conf *conf) { @@ -286,7 +477,9 @@ static unsigned int netfront_promisc_get(struct uk_netdev *n) static const struct uk_netdev_ops netfront_ops = { .configure = netfront_configure, .txq_configure = netfront_txq_setup, + .rxq_configure = netfront_rxq_setup, .txq_info_get = netfront_txq_info_get, + .rxq_info_get = netfront_rxq_info_get, .info_get = netfront_info_get, .einfo_get = netfront_einfo_get, .hwaddr_get = netfront_mac_get, diff --git a/plat/xen/drivers/net/netfront.h b/plat/xen/drivers/net/netfront.h index 05514cfc..74f19108 100644 --- a/plat/xen/drivers/net/netfront.h +++ b/plat/xen/drivers/net/netfront.h @@ -44,6 +44,7 @@ #define NET_TX_RING_SIZE __CONST_RING_SIZE(netif_tx, PAGE_SIZE) +#define NET_RX_RING_SIZE __CONST_RING_SIZE(netif_rx, PAGE_SIZE) /** * internal structure to represent the transmit queue. @@ -77,6 +78,33 @@ struct uk_netdev_tx_queue { * internal structure to represent the receive queue. */ struct uk_netdev_rx_queue { + /* The netfront device */ + struct netfront_dev *netfront_dev; + /* The libuknet queue identifier */ + uint16_t lqueue_id; + /* True if initialized */ + bool initialized; + + /* Shared ring size */ + uint16_t ring_size; + /* Shared ring */ + netif_rx_front_ring_t ring; + /* Shared ring grant ref */ + grant_ref_t ring_ref; + /* Queue event channel */ + evtchn_port_t evtchn; + + /* The flag to interrupt on the transmit queue */ + uint8_t intr_enabled; + + /* User-provided receive buffer allocation function */ + uk_netdev_alloc_rxpkts alloc_rxpkts; + void *alloc_rxpkts_argp; + + /* Receive buffers for incoming packets */ + struct uk_netbuf *netbuf[NET_RX_RING_SIZE]; + /* Grants for receive buffers */ + grant_ref_t gref[NET_RX_RING_SIZE]; }; struct xs_econf { @@ -93,6 +121,7 @@ struct netfront_dev { /* List of the Rx/Tx queues */ uint16_t txqs_num; + uint16_t rxqs_num; struct uk_netdev_tx_queue *txqs; struct uk_netdev_rx_queue *rxqs; /* Maximum number of queue pairs */ -- 2.11.0 _______________________________________________ Minios-devel mailing list Minios-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/minios-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |