X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fnet%2Ftap%2Frte_eth_tap.c;h=20ed9355f647695abaf55a200154bd64ff562e00;hb=c9aa56edec8e;hp=df396bfdee1e3be0a48b55aabef8153689b77437;hpb=403232b81753cb712e82b7385228020f03757aa4;p=dpdk.git diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c index df396bfdee..20ed9355f6 100644 --- a/drivers/net/tap/rte_eth_tap.c +++ b/drivers/net/tap/rte_eth_tap.c @@ -16,7 +16,10 @@ #include #include #include +#include +#include +#include #include #include #include @@ -55,6 +58,16 @@ #define ETH_TAP_CMP_MAC_FMT "0123456789ABCDEFabcdef" #define ETH_TAP_MAC_ARG_FMT ETH_TAP_MAC_FIXED "|" ETH_TAP_USR_MAC_FMT +#define TAP_GSO_MBUFS_PER_CORE 128 +#define TAP_GSO_MBUF_SEG_SIZE 128 +#define TAP_GSO_MBUF_CACHE_SIZE 4 +#define TAP_GSO_MBUFS_NUM \ + (TAP_GSO_MBUFS_PER_CORE * TAP_GSO_MBUF_CACHE_SIZE) + +/* IPC key for queue fds sync */ +#define TAP_MP_KEY "tap_mp_sync_queues" + +static int tap_devices_count; static struct rte_vdev_driver pmd_tap_drv; static struct rte_vdev_driver pmd_tun_drv; @@ -93,6 +106,17 @@ enum ioctl_mode { REMOTE_ONLY, }; +/* Message header to synchronize queues via IPC */ +struct ipc_queues { + char port_name[RTE_DEV_NAME_MAX_LEN]; + int rxq_count; + int txq_count; + /* + * The file descriptors are in the dedicated part + * of the Unix message to be translated by the kernel. + */ +}; + static int tap_intr_handle_set(struct rte_eth_dev *dev, int set); /** @@ -298,8 +322,7 @@ tap_rx_offload_get_queue_capa(void) return DEV_RX_OFFLOAD_SCATTER | DEV_RX_OFFLOAD_IPV4_CKSUM | DEV_RX_OFFLOAD_UDP_CKSUM | - DEV_RX_OFFLOAD_TCP_CKSUM | - DEV_RX_OFFLOAD_CRC_STRIP; + DEV_RX_OFFLOAD_TCP_CKSUM; } /* Callback to handle the rx burst of packets to the correct interface and @@ -309,6 +332,7 @@ static uint16_t pmd_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) { struct rx_queue *rxq = queue; + struct pmd_process_private *process_private; uint16_t num_rx; unsigned long num_rx_bytes = 0; uint32_t trigger = tap_trigger; @@ -317,6 +341,7 @@ pmd_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) return 0; if (trigger) rxq->trigger_seen = trigger; + process_private = rte_eth_devices[rxq->in_port].process_private; rte_compiler_barrier(); for (num_rx = 0; num_rx < nb_pkts; ) { struct rte_mbuf *mbuf = rxq->pool; @@ -325,9 +350,9 @@ pmd_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) uint16_t data_off = rte_pktmbuf_headroom(mbuf); int len; - len = readv(rxq->fd, *rxq->iovecs, - 1 + - (rxq->rxmode->offloads & DEV_RX_OFFLOAD_SCATTER ? + len = readv(process_private->rxq_fds[rxq->queue_id], + *rxq->iovecs, + 1 + (rxq->rxmode->offloads & DEV_RX_OFFLOAD_SCATTER ? rxq->nb_rx_desc : 1)); if (len < (int)sizeof(struct tun_pi)) break; @@ -412,12 +437,45 @@ tap_tx_offload_get_queue_capa(void) return DEV_TX_OFFLOAD_MULTI_SEGS | DEV_TX_OFFLOAD_IPV4_CKSUM | DEV_TX_OFFLOAD_UDP_CKSUM | - DEV_TX_OFFLOAD_TCP_CKSUM; + DEV_TX_OFFLOAD_TCP_CKSUM | + DEV_TX_OFFLOAD_TCP_TSO; +} + +/* Finalize l4 checksum calculation */ +static void +tap_tx_l4_cksum(uint16_t *l4_cksum, uint16_t l4_phdr_cksum, + uint32_t l4_raw_cksum) +{ + if (l4_cksum) { + uint32_t cksum; + + cksum = __rte_raw_cksum_reduce(l4_raw_cksum); + cksum += l4_phdr_cksum; + + cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff); + cksum = (~cksum) & 0xffff; + if (cksum == 0) + cksum = 0xffff; + *l4_cksum = cksum; + } +} + +/* Accumaulate L4 raw checksums */ +static void +tap_tx_l4_add_rcksum(char *l4_data, unsigned int l4_len, uint16_t *l4_cksum, + uint32_t *l4_raw_cksum) +{ + if (l4_cksum == NULL) + return; + + *l4_raw_cksum = __rte_raw_cksum(l4_data, l4_len, *l4_raw_cksum); } +/* L3 and L4 pseudo headers checksum offloads */ static void -tap_tx_offload(char *packet, uint64_t ol_flags, unsigned int l2_len, - unsigned int l3_len) +tap_tx_l3_cksum(char *packet, uint64_t ol_flags, unsigned int l2_len, + unsigned int l3_len, unsigned int l4_len, uint16_t **l4_cksum, + uint16_t *l4_phdr_cksum, uint32_t *l4_raw_cksum) { void *l3_hdr = packet + l2_len; @@ -430,69 +488,53 @@ tap_tx_offload(char *packet, uint64_t ol_flags, unsigned int l2_len, iph->hdr_checksum = (cksum == 0xffff) ? cksum : ~cksum; } if (ol_flags & PKT_TX_L4_MASK) { - uint16_t l4_len; - uint32_t cksum; - uint16_t *l4_cksum; void *l4_hdr; l4_hdr = packet + l2_len + l3_len; if ((ol_flags & PKT_TX_L4_MASK) == PKT_TX_UDP_CKSUM) - l4_cksum = &((struct udp_hdr *)l4_hdr)->dgram_cksum; + *l4_cksum = &((struct udp_hdr *)l4_hdr)->dgram_cksum; else if ((ol_flags & PKT_TX_L4_MASK) == PKT_TX_TCP_CKSUM) - l4_cksum = &((struct tcp_hdr *)l4_hdr)->cksum; + *l4_cksum = &((struct tcp_hdr *)l4_hdr)->cksum; else return; - *l4_cksum = 0; - if (ol_flags & PKT_TX_IPV4) { - struct ipv4_hdr *iph = l3_hdr; - - l4_len = rte_be_to_cpu_16(iph->total_length) - l3_len; - cksum = rte_ipv4_phdr_cksum(l3_hdr, 0); - } else { - struct ipv6_hdr *ip6h = l3_hdr; - - /* payload_len does not include ext headers */ - l4_len = rte_be_to_cpu_16(ip6h->payload_len) - - l3_len + sizeof(struct ipv6_hdr); - cksum = rte_ipv6_phdr_cksum(l3_hdr, 0); - } - cksum += rte_raw_cksum(l4_hdr, l4_len); - cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff); - cksum = (~cksum) & 0xffff; - if (cksum == 0) - cksum = 0xffff; - *l4_cksum = cksum; + **l4_cksum = 0; + if (ol_flags & PKT_TX_IPV4) + *l4_phdr_cksum = rte_ipv4_phdr_cksum(l3_hdr, 0); + else + *l4_phdr_cksum = rte_ipv6_phdr_cksum(l3_hdr, 0); + *l4_raw_cksum = __rte_raw_cksum(l4_hdr, l4_len, 0); } } -/* Callback to handle sending packets from the tap interface - */ -static uint16_t -pmd_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +static inline void +tap_write_mbufs(struct tx_queue *txq, uint16_t num_mbufs, + struct rte_mbuf **pmbufs, + uint16_t *num_packets, unsigned long *num_tx_bytes) { - struct tx_queue *txq = queue; - uint16_t num_tx = 0; - unsigned long num_tx_bytes = 0; - uint32_t max_size; int i; + uint16_t l234_hlen; + struct pmd_process_private *process_private; - if (unlikely(nb_pkts == 0)) - return 0; + process_private = rte_eth_devices[txq->out_port].process_private; - max_size = *txq->mtu + (ETHER_HDR_LEN + ETHER_CRC_LEN + 4); - for (i = 0; i < nb_pkts; i++) { - struct rte_mbuf *mbuf = bufs[num_tx]; - struct iovec iovecs[mbuf->nb_segs + 1]; + for (i = 0; i < num_mbufs; i++) { + struct rte_mbuf *mbuf = pmbufs[i]; + struct iovec iovecs[mbuf->nb_segs + 2]; struct tun_pi pi = { .flags = 0, .proto = 0x00 }; struct rte_mbuf *seg = mbuf; char m_copy[mbuf->data_len]; + int proto; int n; int j; - - /* stats.errs will be incremented */ - if (rte_pktmbuf_pkt_len(mbuf) > max_size) - break; - + int k; /* current index in iovecs for copying segments */ + uint16_t seg_len; /* length of first segment */ + uint16_t nb_segs; + uint16_t *l4_cksum; /* l4 checksum (pseudo header + payload) */ + uint32_t l4_raw_cksum = 0; /* TCP/UDP payload raw checksum */ + uint16_t l4_phdr_cksum = 0; /* TCP/UDP pseudo header checksum */ + uint16_t is_cksum = 0; /* in case cksum should be offloaded */ + + l4_cksum = NULL; if (txq->type == ETH_TUNTAP_TYPE_TUN) { /* * TUN and TAP are created with IFF_NO_PI disabled. @@ -505,48 +547,167 @@ pmd_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) * is 4 or 6, then protocol field is updated. */ char *buff_data = rte_pktmbuf_mtod(seg, void *); - j = (*buff_data & 0xf0); - pi.proto = (j == 0x40) ? rte_cpu_to_be_16(ETHER_TYPE_IPv4) : - (j == 0x60) ? rte_cpu_to_be_16(ETHER_TYPE_IPv6) : 0x00; + proto = (*buff_data & 0xf0); + pi.proto = (proto == 0x40) ? + rte_cpu_to_be_16(ETHER_TYPE_IPv4) : + ((proto == 0x60) ? + rte_cpu_to_be_16(ETHER_TYPE_IPv6) : + 0x00); } - iovecs[0].iov_base = π - iovecs[0].iov_len = sizeof(pi); - for (j = 1; j <= mbuf->nb_segs; j++) { - iovecs[j].iov_len = rte_pktmbuf_data_len(seg); - iovecs[j].iov_base = - rte_pktmbuf_mtod(seg, void *); - seg = seg->next; - } + k = 0; + iovecs[k].iov_base = π + iovecs[k].iov_len = sizeof(pi); + k++; + + nb_segs = mbuf->nb_segs; if (txq->csum && ((mbuf->ol_flags & (PKT_TX_IP_CKSUM | PKT_TX_IPV4) || (mbuf->ol_flags & PKT_TX_L4_MASK) == PKT_TX_UDP_CKSUM || (mbuf->ol_flags & PKT_TX_L4_MASK) == PKT_TX_TCP_CKSUM))) { - /* Support only packets with all data in the same seg */ - if (mbuf->nb_segs > 1) + is_cksum = 1; + + /* Support only packets with at least layer 4 + * header included in the first segment + */ + seg_len = rte_pktmbuf_data_len(mbuf); + l234_hlen = mbuf->l2_len + mbuf->l3_len + mbuf->l4_len; + if (seg_len < l234_hlen) break; - /* To change checksums, work on a copy of data. */ + + /* To change checksums, work on a * copy of l2, l3 + * headers + l4 pseudo header + */ rte_memcpy(m_copy, rte_pktmbuf_mtod(mbuf, void *), - rte_pktmbuf_data_len(mbuf)); - tap_tx_offload(m_copy, mbuf->ol_flags, - mbuf->l2_len, mbuf->l3_len); - iovecs[1].iov_base = m_copy; + l234_hlen); + tap_tx_l3_cksum(m_copy, mbuf->ol_flags, + mbuf->l2_len, mbuf->l3_len, mbuf->l4_len, + &l4_cksum, &l4_phdr_cksum, + &l4_raw_cksum); + iovecs[k].iov_base = m_copy; + iovecs[k].iov_len = l234_hlen; + k++; + + /* Update next iovecs[] beyond l2, l3, l4 headers */ + if (seg_len > l234_hlen) { + iovecs[k].iov_len = seg_len - l234_hlen; + iovecs[k].iov_base = + rte_pktmbuf_mtod(seg, char *) + + l234_hlen; + tap_tx_l4_add_rcksum(iovecs[k].iov_base, + iovecs[k].iov_len, l4_cksum, + &l4_raw_cksum); + k++; + nb_segs++; + } + seg = seg->next; } + + for (j = k; j <= nb_segs; j++) { + iovecs[j].iov_len = rte_pktmbuf_data_len(seg); + iovecs[j].iov_base = rte_pktmbuf_mtod(seg, void *); + if (is_cksum) + tap_tx_l4_add_rcksum(iovecs[j].iov_base, + iovecs[j].iov_len, l4_cksum, + &l4_raw_cksum); + seg = seg->next; + } + + if (is_cksum) + tap_tx_l4_cksum(l4_cksum, l4_phdr_cksum, l4_raw_cksum); + /* copy the tx frame data */ - n = writev(txq->fd, iovecs, mbuf->nb_segs + 1); + n = writev(process_private->txq_fds[txq->queue_id], iovecs, j); if (n <= 0) break; + (*num_packets)++; + (*num_tx_bytes) += rte_pktmbuf_pkt_len(mbuf); + } +} + +/* Callback to handle sending packets from the tap interface + */ +static uint16_t +pmd_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct tx_queue *txq = queue; + uint16_t num_tx = 0; + uint16_t num_packets = 0; + unsigned long num_tx_bytes = 0; + uint32_t max_size; + int i; + + if (unlikely(nb_pkts == 0)) + return 0; + + struct rte_mbuf *gso_mbufs[MAX_GSO_MBUFS]; + max_size = *txq->mtu + (ETHER_HDR_LEN + ETHER_CRC_LEN + 4); + for (i = 0; i < nb_pkts; i++) { + struct rte_mbuf *mbuf_in = bufs[num_tx]; + struct rte_mbuf **mbuf; + uint16_t num_mbufs = 0; + uint16_t tso_segsz = 0; + int ret; + uint16_t hdrs_len; + int j; + uint64_t tso; + + tso = mbuf_in->ol_flags & PKT_TX_TCP_SEG; + if (tso) { + struct rte_gso_ctx *gso_ctx = &txq->gso_ctx; + + assert(gso_ctx != NULL); + + /* TCP segmentation implies TCP checksum offload */ + mbuf_in->ol_flags |= PKT_TX_TCP_CKSUM; + + /* gso size is calculated without ETHER_CRC_LEN */ + hdrs_len = mbuf_in->l2_len + mbuf_in->l3_len + + mbuf_in->l4_len; + tso_segsz = mbuf_in->tso_segsz + hdrs_len; + if (unlikely(tso_segsz == hdrs_len) || + tso_segsz > *txq->mtu) { + txq->stats.errs++; + break; + } + gso_ctx->gso_size = tso_segsz; + ret = rte_gso_segment(mbuf_in, /* packet to segment */ + gso_ctx, /* gso control block */ + (struct rte_mbuf **)&gso_mbufs, /* out mbufs */ + RTE_DIM(gso_mbufs)); /* max tso mbufs */ + + /* ret contains the number of new created mbufs */ + if (ret < 0) + break; + + mbuf = gso_mbufs; + num_mbufs = ret; + } else { + /* stats.errs will be incremented */ + if (rte_pktmbuf_pkt_len(mbuf_in) > max_size) + break; + + /* ret 0 indicates no new mbufs were created */ + ret = 0; + mbuf = &mbuf_in; + num_mbufs = 1; + } + tap_write_mbufs(txq, num_mbufs, mbuf, + &num_packets, &num_tx_bytes); num_tx++; - num_tx_bytes += mbuf->pkt_len; - rte_pktmbuf_free(mbuf); + /* free original mbuf */ + rte_pktmbuf_free(mbuf_in); + /* free tso mbufs */ + for (j = 0; j < ret; j++) + rte_pktmbuf_free(mbuf[j]); } - txq->stats.opackets += num_tx; + txq->stats.opackets += num_packets; txq->stats.errs += nb_pkts - num_tx; txq->stats.obytes += num_tx_bytes; - return num_tx; + return num_packets; } static const char * @@ -641,12 +802,22 @@ tap_link_set_up(struct rte_eth_dev *dev) static int tap_dev_start(struct rte_eth_dev *dev) { - int err; + int err, i; err = tap_intr_handle_set(dev, 1); if (err) return err; - return tap_link_set_up(dev); + + err = tap_link_set_up(dev); + if (err) + return err; + + for (i = 0; i < dev->data->nb_tx_queues; i++) + dev->data->tx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED; + for (i = 0; i < dev->data->nb_rx_queues; i++) + dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED; + + return err; } /* This function gets called when the current port gets stopped. @@ -654,6 +825,13 @@ tap_dev_start(struct rte_eth_dev *dev) static void tap_dev_stop(struct rte_eth_dev *dev) { + int i; + + for (i = 0; i < dev->data->nb_tx_queues; i++) + dev->data->tx_queue_state[i] = RTE_ETH_QUEUE_STATE_STOPPED; + for (i = 0; i < dev->data->nb_rx_queues; i++) + dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STOPPED; + tap_intr_handle_set(dev, 0); tap_link_set_down(dev); } @@ -814,19 +992,20 @@ tap_dev_close(struct rte_eth_dev *dev) { int i; struct pmd_internals *internals = dev->data->dev_private; + struct pmd_process_private *process_private = dev->process_private; tap_link_set_down(dev); tap_flow_flush(dev, NULL); tap_flow_implicit_flush(internals, NULL); for (i = 0; i < RTE_PMD_TAP_MAX_QUEUES; i++) { - if (internals->rxq[i].fd != -1) { - close(internals->rxq[i].fd); - internals->rxq[i].fd = -1; + if (process_private->rxq_fds[i] != -1) { + close(process_private->rxq_fds[i]); + process_private->rxq_fds[i] = -1; } - if (internals->txq[i].fd != -1) { - close(internals->txq[i].fd); - internals->txq[i].fd = -1; + if (process_private->txq_fds[i] != -1) { + close(process_private->txq_fds[i]); + process_private->txq_fds[i] = -1; } } @@ -850,10 +1029,14 @@ static void tap_rx_queue_release(void *queue) { struct rx_queue *rxq = queue; + struct pmd_process_private *process_private; - if (rxq && (rxq->fd > 0)) { - close(rxq->fd); - rxq->fd = -1; + if (!rxq) + return; + process_private = rte_eth_devices[rxq->in_port].process_private; + if (process_private->rxq_fds[rxq->queue_id] > 0) { + close(process_private->rxq_fds[rxq->queue_id]); + process_private->rxq_fds[rxq->queue_id] = -1; rte_pktmbuf_free(rxq->pool); rte_free(rxq->iovecs); rxq->pool = NULL; @@ -865,10 +1048,15 @@ static void tap_tx_queue_release(void *queue) { struct tx_queue *txq = queue; + struct pmd_process_private *process_private; - if (txq && (txq->fd > 0)) { - close(txq->fd); - txq->fd = -1; + if (!txq) + return; + process_private = rte_eth_devices[txq->out_port].process_private; + + if (process_private->txq_fds[txq->queue_id] > 0) { + close(process_private->txq_fds[txq->queue_id]); + process_private->txq_fds[txq->queue_id] = -1; } } @@ -1003,32 +1191,77 @@ tap_mac_set(struct rte_eth_dev *dev, struct ether_addr *mac_addr) return 0; } +static int +tap_gso_ctx_setup(struct rte_gso_ctx *gso_ctx, struct rte_eth_dev *dev) +{ + uint32_t gso_types; + char pool_name[64]; + + /* + * Create private mbuf pool with TAP_GSO_MBUF_SEG_SIZE bytes + * size per mbuf use this pool for both direct and indirect mbufs + */ + + struct rte_mempool *mp; /* Mempool for GSO packets */ + + /* initialize GSO context */ + gso_types = DEV_TX_OFFLOAD_TCP_TSO; + snprintf(pool_name, sizeof(pool_name), "mp_%s", dev->device->name); + mp = rte_mempool_lookup((const char *)pool_name); + if (!mp) { + mp = rte_pktmbuf_pool_create(pool_name, TAP_GSO_MBUFS_NUM, + TAP_GSO_MBUF_CACHE_SIZE, 0, + RTE_PKTMBUF_HEADROOM + TAP_GSO_MBUF_SEG_SIZE, + SOCKET_ID_ANY); + if (!mp) { + struct pmd_internals *pmd = dev->data->dev_private; + RTE_LOG(DEBUG, PMD, "%s: failed to create mbuf pool for device %s\n", + pmd->name, dev->device->name); + return -1; + } + } + + gso_ctx->direct_pool = mp; + gso_ctx->indirect_pool = mp; + gso_ctx->gso_types = gso_types; + gso_ctx->gso_size = 0; /* gso_size is set in tx_burst() per packet */ + gso_ctx->flag = 0; + + return 0; +} + static int tap_setup_queue(struct rte_eth_dev *dev, struct pmd_internals *internals, uint16_t qid, int is_rx) { + int ret; int *fd; int *other_fd; const char *dir; struct pmd_internals *pmd = dev->data->dev_private; + struct pmd_process_private *process_private = dev->process_private; struct rx_queue *rx = &internals->rxq[qid]; struct tx_queue *tx = &internals->txq[qid]; + struct rte_gso_ctx *gso_ctx; if (is_rx) { - fd = &rx->fd; - other_fd = &tx->fd; + fd = &process_private->rxq_fds[qid]; + other_fd = &process_private->txq_fds[qid]; dir = "rx"; + gso_ctx = NULL; } else { - fd = &tx->fd; - other_fd = &rx->fd; + fd = &process_private->txq_fds[qid]; + other_fd = &process_private->rxq_fds[qid]; dir = "tx"; + gso_ctx = &tx->gso_ctx; } if (*fd != -1) { /* fd for this queue already exists */ TAP_LOG(DEBUG, "%s: fd %d for %s queue qid %d exists", pmd->name, *fd, dir, qid); + gso_ctx = NULL; } else if (*other_fd != -1) { /* Only other_fd exists. dup it */ *fd = dup(*other_fd); @@ -1053,6 +1286,11 @@ tap_setup_queue(struct rte_eth_dev *dev, tx->mtu = &dev->data->mtu; rx->rxmode = &dev->data->dev_conf.rxmode; + if (gso_ctx) { + ret = tap_gso_ctx_setup(gso_ctx, dev); + if (ret) + return -1; + } tx->type = pmd->type; @@ -1068,6 +1306,7 @@ tap_rx_queue_setup(struct rte_eth_dev *dev, struct rte_mempool *mp) { struct pmd_internals *internals = dev->data->dev_private; + struct pmd_process_private *process_private = dev->process_private; struct rx_queue *rxq = &internals->rxq[rx_queue_id]; struct rte_mbuf **tmp = &rxq->pool; long iov_max = sysconf(_SC_IOV_MAX); @@ -1088,6 +1327,7 @@ tap_rx_queue_setup(struct rte_eth_dev *dev, rxq->mp = mp; rxq->trigger_seen = 1; /* force initial burst */ rxq->in_port = dev->data->port_id; + rxq->queue_id = rx_queue_id; rxq->nb_rx_desc = nb_desc; iovecs = rte_zmalloc_socket(dev->device->name, sizeof(*iovecs), 0, socket_id); @@ -1126,7 +1366,8 @@ tap_rx_queue_setup(struct rte_eth_dev *dev, } TAP_LOG(DEBUG, " RX TUNTAP device name %s, qid %d on fd %d", - internals->name, rx_queue_id, internals->rxq[rx_queue_id].fd); + internals->name, rx_queue_id, + process_private->rxq_fds[rx_queue_id]); return 0; @@ -1146,6 +1387,7 @@ tap_tx_queue_setup(struct rte_eth_dev *dev, const struct rte_eth_txconf *tx_conf) { struct pmd_internals *internals = dev->data->dev_private; + struct pmd_process_private *process_private = dev->process_private; struct tx_queue *txq; int ret; uint64_t offloads; @@ -1154,6 +1396,8 @@ tap_tx_queue_setup(struct rte_eth_dev *dev, return -1; dev->data->tx_queues[tx_queue_id] = &internals->txq[tx_queue_id]; txq = dev->data->tx_queues[tx_queue_id]; + txq->out_port = dev->data->port_id; + txq->queue_id = tx_queue_id; offloads = tx_conf->offloads | dev->data->dev_conf.txmode.offloads; txq->csum = !!(offloads & @@ -1166,7 +1410,8 @@ tap_tx_queue_setup(struct rte_eth_dev *dev, return -1; TAP_LOG(DEBUG, " TX TUNTAP device name %s, qid %d on fd %d csum %s", - internals->name, tx_queue_id, internals->txq[tx_queue_id].fd, + internals->name, tx_queue_id, + process_private->txq_fds[tx_queue_id], txq->csum ? "on" : "off"); return 0; @@ -1342,6 +1587,37 @@ tap_rss_hash_update(struct rte_eth_dev *dev, return 0; } +static int +tap_rx_queue_start(struct rte_eth_dev *dev, uint16_t rx_queue_id) +{ + dev->data->rx_queue_state[rx_queue_id] = RTE_ETH_QUEUE_STATE_STARTED; + + return 0; +} + +static int +tap_tx_queue_start(struct rte_eth_dev *dev, uint16_t tx_queue_id) +{ + dev->data->tx_queue_state[tx_queue_id] = RTE_ETH_QUEUE_STATE_STARTED; + + return 0; +} + +static int +tap_rx_queue_stop(struct rte_eth_dev *dev, uint16_t rx_queue_id) +{ + dev->data->rx_queue_state[rx_queue_id] = RTE_ETH_QUEUE_STATE_STOPPED; + + return 0; +} + +static int +tap_tx_queue_stop(struct rte_eth_dev *dev, uint16_t tx_queue_id) +{ + dev->data->tx_queue_state[tx_queue_id] = RTE_ETH_QUEUE_STATE_STOPPED; + + return 0; +} static const struct eth_dev_ops ops = { .dev_start = tap_dev_start, .dev_stop = tap_dev_stop, @@ -1350,6 +1626,10 @@ static const struct eth_dev_ops ops = { .dev_infos_get = tap_dev_info, .rx_queue_setup = tap_rx_queue_setup, .tx_queue_setup = tap_tx_queue_setup, + .rx_queue_start = tap_rx_queue_start, + .tx_queue_start = tap_tx_queue_start, + .rx_queue_stop = tap_rx_queue_stop, + .tx_queue_stop = tap_tx_queue_stop, .rx_queue_release = tap_rx_queue_release, .tx_queue_release = tap_tx_queue_release, .flow_ctrl_get = tap_flow_ctrl_get, @@ -1379,6 +1659,7 @@ eth_dev_tap_create(struct rte_vdev_device *vdev, char *tap_name, int numa_node = rte_socket_id(); struct rte_eth_dev *dev; struct pmd_internals *pmd; + struct pmd_process_private *process_private; struct rte_eth_dev_data *data; struct ifreq ifr; int i; @@ -1393,7 +1674,16 @@ eth_dev_tap_create(struct rte_vdev_device *vdev, char *tap_name, goto error_exit_nodev; } + process_private = (struct pmd_process_private *) + rte_zmalloc_socket(tap_name, sizeof(struct pmd_process_private), + RTE_CACHE_LINE_SIZE, dev->device->numa_node); + + if (process_private == NULL) { + TAP_LOG(ERR, "Failed to alloc memory for process private"); + return -1; + } pmd = dev->data->dev_private; + dev->process_private = process_private; pmd->dev = dev; snprintf(pmd->name, sizeof(pmd->name), "%s", tap_name); pmd->type = type; @@ -1429,8 +1719,8 @@ eth_dev_tap_create(struct rte_vdev_device *vdev, char *tap_name, /* Presetup the fds to -1 as being not valid */ pmd->ka_fd = -1; for (i = 0; i < RTE_PMD_TAP_MAX_QUEUES; i++) { - pmd->rxq[i].fd = -1; - pmd->txq[i].fd = -1; + process_private->rxq_fds[i] = -1; + process_private->txq_fds[i] = -1; } if (pmd->type == ETH_TUNTAP_TYPE_TAP) { @@ -1690,6 +1980,8 @@ rte_pmd_tun_probe(struct rte_vdev_device *dev) return -1; } eth_dev->dev_ops = &ops; + eth_dev->device = &dev->device; + rte_eth_dev_probing_finish(eth_dev); return 0; } @@ -1731,6 +2023,102 @@ leave: return ret; } +/* Request queue file descriptors from secondary to primary. */ +static int +tap_mp_attach_queues(const char *port_name, struct rte_eth_dev *dev) +{ + int ret; + struct timespec timeout = {.tv_sec = 1, .tv_nsec = 0}; + struct rte_mp_msg request, *reply; + struct rte_mp_reply replies; + struct ipc_queues *request_param = (struct ipc_queues *)request.param; + struct ipc_queues *reply_param; + struct pmd_process_private *process_private = dev->process_private; + int queue, fd_iterator; + + /* Prepare the request */ + strlcpy(request.name, TAP_MP_KEY, sizeof(request.name)); + strlcpy(request_param->port_name, port_name, + sizeof(request_param->port_name)); + request.len_param = sizeof(*request_param); + /* Send request and receive reply */ + ret = rte_mp_request_sync(&request, &replies, &timeout); + if (ret < 0) { + TAP_LOG(ERR, "Failed to request queues from primary: %d", + rte_errno); + return -1; + } + reply = &replies.msgs[0]; + reply_param = (struct ipc_queues *)reply->param; + TAP_LOG(DEBUG, "Received IPC reply for %s", reply_param->port_name); + + /* Attach the queues from received file descriptors */ + dev->data->nb_rx_queues = reply_param->rxq_count; + dev->data->nb_tx_queues = reply_param->txq_count; + fd_iterator = 0; + for (queue = 0; queue < reply_param->rxq_count; queue++) + process_private->rxq_fds[queue] = reply->fds[fd_iterator++]; + for (queue = 0; queue < reply_param->txq_count; queue++) + process_private->txq_fds[queue] = reply->fds[fd_iterator++]; + + return 0; +} + +/* Send the queue file descriptors from the primary process to secondary. */ +static int +tap_mp_sync_queues(const struct rte_mp_msg *request, const void *peer) +{ + struct rte_eth_dev *dev; + struct pmd_process_private *process_private; + struct rte_mp_msg reply; + const struct ipc_queues *request_param = + (const struct ipc_queues *)request->param; + struct ipc_queues *reply_param = + (struct ipc_queues *)reply.param; + uint16_t port_id; + int queue; + int ret; + + /* Get requested port */ + TAP_LOG(DEBUG, "Received IPC request for %s", request_param->port_name); + ret = rte_eth_dev_get_port_by_name(request_param->port_name, &port_id); + if (ret) { + TAP_LOG(ERR, "Failed to get port id for %s", + request_param->port_name); + return -1; + } + dev = &rte_eth_devices[port_id]; + process_private = dev->process_private; + + /* Fill file descriptors for all queues */ + reply.num_fds = 0; + reply_param->rxq_count = 0; + for (queue = 0; queue < dev->data->nb_rx_queues; queue++) { + reply.fds[reply.num_fds++] = process_private->rxq_fds[queue]; + reply_param->rxq_count++; + } + RTE_ASSERT(reply_param->rxq_count == dev->data->nb_rx_queues); + RTE_ASSERT(reply_param->txq_count == dev->data->nb_tx_queues); + RTE_ASSERT(reply.num_fds <= RTE_MP_MAX_FD_NUM); + + reply_param->txq_count = 0; + for (queue = 0; queue < dev->data->nb_tx_queues; queue++) { + reply.fds[reply.num_fds++] = process_private->txq_fds[queue]; + reply_param->txq_count++; + } + + /* Send reply */ + strlcpy(reply.name, request->name, sizeof(reply.name)); + strlcpy(reply_param->port_name, request_param->port_name, + sizeof(reply_param->port_name)); + reply.len_param = sizeof(*reply_param); + if (rte_mp_reply(&reply, peer) < 0) { + TAP_LOG(ERR, "Failed to reply an IPC request to sync queues"); + return -1; + } + return 0; +} + /* Open a TAP interface device. */ static int @@ -1744,21 +2132,41 @@ rte_pmd_tap_probe(struct rte_vdev_device *dev) char remote_iface[RTE_ETH_NAME_MAX_LEN]; struct ether_addr user_mac = { .addr_bytes = {0} }; struct rte_eth_dev *eth_dev; + int tap_devices_count_increased = 0; strcpy(tuntap_name, "TAP"); name = rte_vdev_device_name(dev); params = rte_vdev_device_args(dev); - if (rte_eal_process_type() == RTE_PROC_SECONDARY && - strlen(params) == 0) { + if (rte_eal_process_type() == RTE_PROC_SECONDARY) { eth_dev = rte_eth_dev_attach_secondary(name); if (!eth_dev) { TAP_LOG(ERR, "Failed to probe %s", name); return -1; } - /* TODO: request info from primary to set up Rx and Tx */ eth_dev->dev_ops = &ops; + eth_dev->device = &dev->device; + eth_dev->rx_pkt_burst = pmd_rx_burst; + eth_dev->tx_pkt_burst = pmd_tx_burst; + if (!rte_eal_primary_proc_alive(NULL)) { + TAP_LOG(ERR, "Primary process is missing"); + return -1; + } + eth_dev->process_private = (struct pmd_process_private *) + rte_zmalloc_socket(name, + sizeof(struct pmd_process_private), + RTE_CACHE_LINE_SIZE, + eth_dev->device->numa_node); + if (eth_dev->process_private == NULL) { + TAP_LOG(ERR, + "Failed to alloc memory for process private"); + return -1; + } + + ret = tap_mp_attach_queues(name, eth_dev); + if (ret != 0) + return -1; rte_eth_dev_probing_finish(eth_dev); return 0; } @@ -1806,6 +2214,17 @@ rte_pmd_tap_probe(struct rte_vdev_device *dev) TAP_LOG(NOTICE, "Initializing pmd_tap for %s as %s", name, tap_name); + /* Register IPC feed callback */ + if (!tap_devices_count) { + ret = rte_mp_action_register(TAP_MP_KEY, tap_mp_sync_queues); + if (ret < 0) { + TAP_LOG(ERR, "%s: Failed to register IPC callback: %s", + tuntap_name, strerror(rte_errno)); + goto leave; + } + } + tap_devices_count++; + tap_devices_count_increased = 1; ret = eth_dev_tap_create(dev, tap_name, remote_iface, &user_mac, ETH_TUNTAP_TYPE_TAP); @@ -1813,6 +2232,11 @@ leave: if (ret == -1) { TAP_LOG(ERR, "Failed to create pmd for %s as %s", name, tap_name); + if (tap_devices_count_increased == 1) { + if (tap_devices_count == 1) + rte_mp_action_unregister(TAP_MP_KEY); + tap_devices_count--; + } tap_unit--; /* Restore the unit number */ } rte_kvargs_free(kvlist); @@ -1827,14 +2251,19 @@ rte_pmd_tap_remove(struct rte_vdev_device *dev) { struct rte_eth_dev *eth_dev = NULL; struct pmd_internals *internals; + struct pmd_process_private *process_private; int i; /* find the ethdev entry */ eth_dev = rte_eth_dev_allocated(rte_vdev_device_name(dev)); if (!eth_dev) - return 0; + return -ENODEV; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return rte_eth_dev_release_port_secondary(eth_dev); internals = eth_dev->data->dev_private; + process_private = eth_dev->process_private; TAP_LOG(DEBUG, "Closing %s Ethernet device on numa %u", (internals->type == ETH_TUNTAP_TYPE_TAP) ? "TAP" : "TUN", @@ -1846,18 +2275,22 @@ rte_pmd_tap_remove(struct rte_vdev_device *dev) tap_nl_final(internals->nlsk_fd); } for (i = 0; i < RTE_PMD_TAP_MAX_QUEUES; i++) { - if (internals->rxq[i].fd != -1) { - close(internals->rxq[i].fd); - internals->rxq[i].fd = -1; + if (process_private->rxq_fds[i] != -1) { + close(process_private->rxq_fds[i]); + process_private->rxq_fds[i] = -1; } - if (internals->txq[i].fd != -1) { - close(internals->txq[i].fd); - internals->txq[i].fd = -1; + if (process_private->txq_fds[i] != -1) { + close(process_private->txq_fds[i]); + process_private->txq_fds[i] = -1; } } close(internals->ioctl_sock); rte_free(eth_dev->data->dev_private); + rte_free(eth_dev->process_private); + if (tap_devices_count == 1) + rte_mp_action_unregister(TAP_MP_KEY); + tap_devices_count--; rte_eth_dev_release_port(eth_dev); if (internals->ka_fd != -1) { @@ -1888,9 +2321,7 @@ RTE_PMD_REGISTER_PARAM_STRING(net_tap, ETH_TAP_REMOTE_ARG "="); int tap_logtype; -RTE_INIT(tap_init_log); -static void -tap_init_log(void) +RTE_INIT(tap_init_log) { tap_logtype = rte_log_register("pmd.net.tap"); if (tap_logtype >= 0)