diff options
Diffstat (limited to 'platform/linux-generic/pktio/loop.c')
-rw-r--r-- | platform/linux-generic/pktio/loop.c | 790 |
1 files changed, 666 insertions, 124 deletions
diff --git a/platform/linux-generic/pktio/loop.c b/platform/linux-generic/pktio/loop.c index c825393ac..05a1a3dce 100644 --- a/platform/linux-generic/pktio/loop.c +++ b/platform/linux-generic/pktio/loop.c @@ -1,257 +1,793 @@ -/* Copyright (c) 2013, Linaro Limited - * Copyright (c) 2013, Nokia Solutions and Networks - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2013-2018 Linaro Limited + * Copyright (c) 2013-2023 Nokia Solutions and Networks */ -#include <odp_api.h> -#include <odp_packet_internal.h> -#include <odp_packet_io_internal.h> +#include <odp/api/debug.h> +#include <odp/api/event.h> +#include <odp/api/hash.h> +#include <odp/api/hints.h> +#include <odp/api/packet.h> +#include <odp/api/packet_io.h> +#include <odp/api/queue.h> +#include <odp/api/time.h> + +#include <odp/api/plat/byteorder_inlines.h> +#include <odp/api/plat/packet_flag_inlines.h> +#include <odp/api/plat/queue_inlines.h> + +#include <odp_parse_internal.h> #include <odp_classification_internal.h> #include <odp_debug_internal.h> -#include <odp/api/hints.h> +#include <odp_event_internal.h> +#include <odp_global_data.h> +#include <odp_ipsec_internal.h> +#include <odp_packet_internal.h> +#include <odp_packet_io_internal.h> +#include <odp_macros_internal.h> #include <odp_queue_if.h> #include <protocols/eth.h> #include <protocols/ip.h> -#include <errno.h> #include <inttypes.h> #include <limits.h> +#include <stdint.h> +#include <stdlib.h> + +#define MAX_QUEUES (ODP_PKTIN_MAX_QUEUES > ODP_PKTOUT_MAX_QUEUES ? \ + ODP_PKTIN_MAX_QUEUES : ODP_PKTOUT_MAX_QUEUES) + +#define MAX_LOOP 16 + +#define LOOP_MTU_MIN 68 +#define LOOP_MTU_MAX UINT16_MAX + +#define LOOP_MAX_QUEUE_SIZE 1024 + +typedef struct { + odp_atomic_u64_t in_octets; + odp_atomic_u64_t in_packets; + odp_atomic_u64_t in_discards; + odp_atomic_u64_t in_errors; + odp_atomic_u64_t out_octets; + odp_atomic_u64_t out_packets; +} stats_t; + +typedef struct ODP_ALIGNED_CACHE { + /* queue handle as the "wire" */ + odp_queue_t queue; + /* queue specific statistics */ + stats_t stats; + /* config input queue size */ + uint32_t in_size; + /* config output queue size */ + uint32_t out_size; +} loop_queue_t; + +typedef struct { + /* loopback entries for "loop" device */ + loop_queue_t loopqs[MAX_QUEUES]; + /* hash config */ + odp_pktin_hash_proto_t hash; + /* config queue count */ + uint32_t num_conf_qs; + /* actual number queues */ + uint32_t num_qs; + /* link MTU */ + uint16_t mtu; + /* index of "loop" device */ + uint8_t idx; + /* create or re-create queue during start */ + uint8_t queue_create; +} pkt_loop_t; + +ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(pkt_loop_t), + "PKTIO_PRIVATE_SIZE too small"); + +static inline pkt_loop_t *pkt_priv(pktio_entry_t *pktio_entry) +{ + return (pkt_loop_t *)(uintptr_t)(pktio_entry->pkt_priv); +} /* MAC address for the "loop" interface */ -static const char pktio_loop_mac[] = {0x02, 0xe9, 0x34, 0x80, 0x73, 0x01}; +static const uint8_t pktio_loop_mac[] = {0x02, 0xe9, 0x34, 0x80, 0x73, 0x01}; -static int loopback_stats_reset(pktio_entry_t *pktio_entry); +static int loopback_init_capability(pktio_entry_t *pktio_entry); -static int loopback_open(odp_pktio_t id, pktio_entry_t *pktio_entry, +static int loopback_open(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry, const char *devname, odp_pool_t pool ODP_UNUSED) { - if (strcmp(devname, "loop")) + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + long idx; + + if (!strcmp(devname, "loop")) { + idx = 0; + } else if (!strncmp(devname, "loop", 4)) { + char *end; + + idx = strtol(devname + 4, &end, 10); + if (idx <= 0 || idx >= MAX_LOOP || *end) + return -1; + } else { return -1; + } + + memset(pkt_loop, 0, sizeof(pkt_loop_t)); + pkt_loop->mtu = LOOP_MTU_MAX; + pkt_loop->idx = idx; + pkt_loop->queue_create = 1; + loopback_init_capability(pktio_entry); + + for (uint32_t i = 0; i < MAX_QUEUES; i++) { + odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.in_octets, 0); + odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.in_packets, 0); + odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.in_discards, 0); + odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.in_errors, 0); + odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.out_octets, 0); + odp_atomic_init_u64(&pkt_loop->loopqs[i].stats.out_packets, 0); + } + + return 0; +} + +static int loopback_queue_destroy(odp_queue_t queue) +{ + odp_event_t event; - char loopq_name[ODP_QUEUE_NAME_LEN]; + do { + event = odp_queue_deq(queue); + if (event != ODP_EVENT_INVALID) + odp_event_free(event); - snprintf(loopq_name, sizeof(loopq_name), "%" PRIu64 "-pktio_loopq", - odp_pktio_to_u64(id)); - pktio_entry->s.pkt_loop.loopq = - odp_queue_create(loopq_name, NULL); + } while (event != ODP_EVENT_INVALID); - if (pktio_entry->s.pkt_loop.loopq == ODP_QUEUE_INVALID) + if (odp_queue_destroy(queue)) { + _ODP_ERR("Destroying loopback pktio queue failed\n"); return -1; + } + return 0; +} + +static int loopback_queues_destroy(loop_queue_t *queues, uint32_t num_queues) +{ + int ret = 0; + + for (uint32_t i = 0; i < num_queues; i++) { + if (loopback_queue_destroy(queues[i].queue)) + ret = -1; + } + + return ret; +} + +static int loopback_start(pktio_entry_t *pktio_entry) +{ + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + odp_queue_param_t queue_param; + char queue_name[ODP_QUEUE_NAME_LEN]; + + /* Re-create queue only when necessary */ + if (!pkt_loop->queue_create) + return 0; + + /* Destroy old queues */ + if (loopback_queues_destroy(pkt_loop->loopqs, pkt_loop->num_qs)) + return -1; + + pkt_loop->num_qs = 0; + + for (uint32_t i = 0; i < pkt_loop->num_conf_qs; i++) { + odp_queue_param_init(&queue_param); + queue_param.size = _ODP_MAX(pkt_loop->loopqs[i].in_size, + pkt_loop->loopqs[i].out_size); + snprintf(queue_name, sizeof(queue_name), "_odp_pktio_loopq-%" PRIu64 "-%u", + odp_pktio_to_u64(pktio_entry->handle), i); + pkt_loop->loopqs[i].queue = odp_queue_create(queue_name, &queue_param); + + if (pkt_loop->loopqs[i].queue == ODP_QUEUE_INVALID) { + _ODP_ERR("Creating loopback pktio queue %s failed\n", queue_name); + (void)loopback_queues_destroy(pkt_loop->loopqs, i); + return -1; + } + } - loopback_stats_reset(pktio_entry); + pkt_loop->num_qs = pkt_loop->num_conf_qs; + + return 0; +} + +static int loopback_pktin_queue_config(pktio_entry_t *pktio_entry, + const odp_pktin_queue_param_t *param) +{ + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + + pkt_loop->num_conf_qs = param->num_queues; + pkt_loop->queue_create = 1; + pkt_loop->hash.all_bits = param->hash_enable ? param->hash_proto.all_bits : 0; + + if (pktio_entry->param.in_mode == ODP_PKTIN_MODE_DIRECT) { + for (uint32_t i = 0; i < MAX_QUEUES; i++) { + if (i < pkt_loop->num_conf_qs) + pkt_loop->loopqs[i].in_size = param->queue_size[i]; + else + pkt_loop->loopqs[i].in_size = 0; + } + } + + return 0; +} + +static int loopback_pktout_queue_config(pktio_entry_t *pktio_entry, + const odp_pktout_queue_param_t *param) +{ + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + + pkt_loop->queue_create = 1; + + for (uint32_t i = 0; i < MAX_QUEUES; i++) { + if (i < param->num_queues) + pkt_loop->loopqs[i].out_size = param->queue_size[i]; + else + pkt_loop->loopqs[i].out_size = 0; + } return 0; } static int loopback_close(pktio_entry_t *pktio_entry) { - return odp_queue_destroy(pktio_entry->s.pkt_loop.loopq); + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + + return loopback_queues_destroy(pkt_loop->loopqs, pkt_loop->num_qs); } -static int loopback_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED, - odp_packet_t pkts[], int len) +static int loopback_recv(pktio_entry_t *pktio_entry, int index, odp_packet_t pkts[], int num) { int nbr, i; - odp_buffer_hdr_t *hdr_tbl[QUEUE_MULTI_MAX]; - queue_t queue; + loop_queue_t *entry = &pkt_priv(pktio_entry)->loopqs[index]; + odp_queue_t queue = entry->queue; + stats_t *stats = &entry->stats; + _odp_event_hdr_t *hdr_tbl[QUEUE_MULTI_MAX]; + odp_packet_t cls_tbl[QUEUE_MULTI_MAX]; odp_packet_hdr_t *pkt_hdr; - odp_packet_hdr_t parsed_hdr; odp_packet_t pkt; odp_time_t ts_val; odp_time_t *ts = NULL; int num_rx = 0; - int failed = 0; - - if (odp_unlikely(len > QUEUE_MULTI_MAX)) - len = QUEUE_MULTI_MAX; + int packets = 0; + int num_cls = 0; + const int cls_enabled = pktio_cls_enabled(pktio_entry); + uint32_t octets = 0; + const odp_proto_layer_t layer = pktio_entry->parse_layer; + const odp_pktin_config_opt_t opt = pktio_entry->config.pktin; - odp_ticketlock_lock(&pktio_entry->s.rxl); + if (odp_unlikely(num > QUEUE_MULTI_MAX)) + num = QUEUE_MULTI_MAX; - queue = queue_fn->from_ext(pktio_entry->s.pkt_loop.loopq); - nbr = queue_fn->deq_multi(queue, hdr_tbl, len); + nbr = odp_queue_deq_multi(queue, (odp_event_t *)hdr_tbl, num); - if (pktio_entry->s.config.pktin.bit.ts_all || - pktio_entry->s.config.pktin.bit.ts_ptp) { + if (opt.bit.ts_all || opt.bit.ts_ptp) { ts_val = odp_time_global(); ts = &ts_val; } for (i = 0; i < nbr; i++) { uint32_t pkt_len; + int do_ipsec_enq = 0; - pkt = packet_from_buf_hdr(hdr_tbl[i]); + pkt = packet_from_event_hdr(hdr_tbl[i]); pkt_len = odp_packet_len(pkt); + pkt_hdr = packet_hdr(pkt); - - if (pktio_cls_enabled(pktio_entry)) { - odp_packet_t new_pkt; - odp_pool_t new_pool; + if (layer) { uint8_t *pkt_addr; - uint8_t buf[PACKET_PARSE_SEG_LEN]; + uint8_t buf[PARSE_BYTES]; int ret; uint32_t seg_len = odp_packet_seg_len(pkt); /* Make sure there is enough data for the packet * parser in the case of a segmented packet. */ - if (odp_unlikely(seg_len < PACKET_PARSE_SEG_LEN && - pkt_len > PACKET_PARSE_SEG_LEN)) { - odp_packet_copy_to_mem(pkt, 0, - PACKET_PARSE_SEG_LEN, - buf); - seg_len = PACKET_PARSE_SEG_LEN; + if (odp_unlikely(seg_len < PARSE_BYTES && + pkt_len > seg_len)) { + seg_len = _ODP_MIN(pkt_len, PARSE_BYTES); + odp_packet_copy_to_mem(pkt, 0, seg_len, buf); pkt_addr = buf; } else { pkt_addr = odp_packet_data(pkt); } - ret = cls_classify_packet(pktio_entry, pkt_addr, - pkt_len, seg_len, - &new_pool, &parsed_hdr); - if (ret) { - failed++; + + packet_parse_reset(pkt_hdr, 1); + ret = _odp_packet_parse_common(pkt_hdr, pkt_addr, pkt_len, + seg_len, layer, opt); + if (ret) + odp_atomic_inc_u64(&stats->in_errors); + + if (ret < 0) { odp_packet_free(pkt); continue; } - if (new_pool != odp_packet_pool(pkt)) { - new_pkt = odp_packet_copy(pkt, new_pool); - odp_packet_free(pkt); + if (cls_enabled) { + odp_pool_t new_pool; + + ret = _odp_cls_classify_packet(pktio_entry, pkt_addr, + &new_pool, pkt_hdr); + if (ret < 0) + odp_atomic_inc_u64(&stats->in_discards); + + if (ret) { + odp_packet_free(pkt); + continue; + } - if (new_pkt == ODP_PACKET_INVALID) { - failed++; + if (odp_unlikely(_odp_pktio_packet_to_pool( + &pkt, &pkt_hdr, new_pool))) { + odp_packet_free(pkt); + odp_atomic_inc_u64(&stats->in_discards); continue; } - pkt = new_pkt; } } - pkt_hdr = odp_packet_hdr(pkt); - pkt_hdr->input = pktio_entry->s.handle; + packet_set_ts(pkt_hdr, ts); + pkt_hdr->input = pktio_entry->handle; + + /* Try IPsec inline processing */ + if (pktio_entry->config.inbound_ipsec && + !pkt_hdr->p.flags.ip_err && + odp_packet_has_ipsec(pkt)) { + do_ipsec_enq = !_odp_ipsec_try_inline(&pkt); + pkt_hdr = packet_hdr(pkt); + } + + if (!pkt_hdr->p.flags.all.error) { + octets += pkt_len; + packets++; + } + + if (do_ipsec_enq) { + if (odp_unlikely(odp_queue_enq(pkt_hdr->dst_queue, + odp_packet_to_event(pkt)))) { + odp_atomic_inc_u64(&stats->in_discards); + if (!pkt_hdr->p.flags.all.error) { + octets -= pkt_len; + packets--; + } + odp_packet_free(pkt); + } + } else if (cls_enabled) { + /* Enqueue packets directly to classifier destination queue */ + cls_tbl[num_cls++] = pkt; + num_cls = _odp_cls_enq(cls_tbl, num_cls, (i + 1 == nbr)); + } else { + pkts[num_rx++] = pkt; + } + } + + /* Enqueue remaining classified packets */ + if (odp_unlikely(num_cls)) + _odp_cls_enq(cls_tbl, num_cls, true); + + odp_atomic_add_u64(&stats->in_octets, octets); + odp_atomic_add_u64(&stats->in_packets, packets); + + return num_rx; +} + +#define OL_TX_CHKSUM_PKT(_cfg, _capa, _proto, _ovr_set, _ovr) \ + (_capa && _proto && (_ovr_set ? _ovr : _cfg)) + +static inline int check_proto(void *l3_hdr, + uint32_t l3_len, + odp_bool_t *l3_proto_v4, + uint8_t *l4_proto) +{ + uint8_t l3_proto_ver = _ODP_IPV4HDR_VER(*(uint8_t *)l3_hdr); + + if (l3_proto_ver == _ODP_IPV4 && l3_len >= _ODP_IPV4HDR_LEN) { + _odp_ipv4hdr_t *ip = l3_hdr; + uint16_t frag_offset = odp_be_to_cpu_16(ip->frag_offset); - if (pktio_cls_enabled(pktio_entry)) - copy_packet_cls_metadata(&parsed_hdr, pkt_hdr); + *l3_proto_v4 = 1; + if (!_ODP_IPV4HDR_IS_FRAGMENT(frag_offset)) + *l4_proto = ip->proto; else - packet_parse_layer(pkt_hdr, - pktio_entry->s.config.parser.layer); + *l4_proto = 255; - packet_set_ts(pkt_hdr, ts); + return 0; + } else if (l3_proto_ver == _ODP_IPV6 && l3_len >= _ODP_IPV6HDR_LEN) { + _odp_ipv6hdr_t *ipv6 = l3_hdr; - pktio_entry->s.stats.in_octets += pkt_len; + *l3_proto_v4 = 0; + *l4_proto = ipv6->next_hdr; - pkts[num_rx++] = pkt; + /* FIXME: check that packet is not a fragment !!! + * Might require parsing headers spanning several segments, so + * not implemented yet. */ + return 0; } - pktio_entry->s.stats.in_errors += failed; - pktio_entry->s.stats.in_ucast_pkts += num_rx - failed; + return -1; +} - odp_ticketlock_unlock(&pktio_entry->s.rxl); +static inline void loopback_fix_checksums(odp_packet_t pkt, + odp_pktout_config_opt_t *pktout_cfg, + odp_pktout_config_opt_t *pktout_capa) +{ + odp_bool_t l3_proto_v4 = false; + uint8_t l4_proto; + void *l3_hdr; + uint32_t l3_len; + odp_bool_t ipv4_chksum_pkt, udp_chksum_pkt, tcp_chksum_pkt, + sctp_chksum_pkt; + odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt); + + l3_hdr = odp_packet_l3_ptr(pkt, &l3_len); + + if (l3_hdr == NULL || + check_proto(l3_hdr, l3_len, &l3_proto_v4, &l4_proto)) + return; + + ipv4_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.ipv4_chksum, + pktout_capa->bit.ipv4_chksum, + l3_proto_v4, + pkt_hdr->p.flags.l3_chksum_set, + pkt_hdr->p.flags.l3_chksum); + udp_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.udp_chksum, + pktout_capa->bit.udp_chksum, + l4_proto == _ODP_IPPROTO_UDP, + pkt_hdr->p.flags.l4_chksum_set, + pkt_hdr->p.flags.l4_chksum); + tcp_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.tcp_chksum, + pktout_capa->bit.tcp_chksum, + l4_proto == _ODP_IPPROTO_TCP, + pkt_hdr->p.flags.l4_chksum_set, + pkt_hdr->p.flags.l4_chksum); + sctp_chksum_pkt = OL_TX_CHKSUM_PKT(pktout_cfg->bit.sctp_chksum, + pktout_capa->bit.sctp_chksum, + l4_proto == _ODP_IPPROTO_SCTP, + pkt_hdr->p.flags.l4_chksum_set, + pkt_hdr->p.flags.l4_chksum); + + if (ipv4_chksum_pkt) + _odp_packet_ipv4_chksum_insert(pkt); + + if (tcp_chksum_pkt) + _odp_packet_tcp_chksum_insert(pkt); + + if (udp_chksum_pkt) + _odp_packet_udp_chksum_insert(pkt); + + if (sctp_chksum_pkt) + _odp_packet_sctp_chksum_insert(pkt); +} - return num_rx; +static inline uint8_t *add_data(uint8_t *data, void *src, uint32_t len) +{ + return (uint8_t *)memcpy(data, src, len) + len; } -static int loopback_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED, - const odp_packet_t pkt_tbl[], int len) +static inline odp_queue_t get_dest_queue(const pkt_loop_t *pkt_loop, odp_packet_t pkt, int index) { - odp_buffer_hdr_t *hdr_tbl[QUEUE_MULTI_MAX]; - queue_t queue; + const odp_pktin_hash_proto_t *hash = &pkt_loop->hash; + _odp_udphdr_t udp; + _odp_tcphdr_t tcp; + _odp_ipv4hdr_t ipv4; + _odp_ipv6hdr_t ipv6; + uint32_t off; + /* Space for UDP/TCP source and destination ports and IPv4/IPv6 source and destination + * addresses. */ + uint8_t data[2 * sizeof(uint16_t) + 2 * 4 * sizeof(uint32_t)]; + uint8_t *head = data; + + if (hash->all_bits == 0) + return pkt_loop->loopqs[index % pkt_loop->num_qs].queue; + + memset(data, 0, sizeof(data)); + off = odp_packet_l4_offset(pkt); + + if (off != ODP_PACKET_OFFSET_INVALID) { + if ((hash->proto.ipv4_udp || hash->proto.ipv6_udp) && odp_packet_has_udp(pkt)) { + if (odp_packet_copy_to_mem(pkt, off, _ODP_UDPHDR_LEN, &udp) == 0) { + head = add_data(head, &udp.src_port, sizeof(udp.src_port)); + head = add_data(head, &udp.dst_port, sizeof(udp.dst_port)); + } + } else if ((hash->proto.ipv4_tcp || hash->proto.ipv6_tcp) && + odp_packet_has_tcp(pkt)) { + if (odp_packet_copy_to_mem(pkt, off, _ODP_TCPHDR_LEN, &tcp) == 0) { + head = add_data(head, &tcp.src_port, sizeof(tcp.src_port)); + head = add_data(head, &tcp.dst_port, sizeof(tcp.dst_port)); + } + } + } + + off = odp_packet_l3_offset(pkt); + + if (off != ODP_PACKET_OFFSET_INVALID) { + if (hash->proto.ipv4 && odp_packet_has_ipv4(pkt)) { + if (odp_packet_copy_to_mem(pkt, off, _ODP_IPV4HDR_LEN, &ipv4) == 0) { + head = add_data(head, &ipv4.src_addr, sizeof(ipv4.src_addr)); + head = add_data(head, &ipv4.dst_addr, sizeof(ipv4.dst_addr)); + } + } else if (hash->proto.ipv6 && odp_packet_has_ipv6(pkt)) { + if (odp_packet_copy_to_mem(pkt, off, _ODP_IPV6HDR_LEN, &ipv6) == 0) { + head = add_data(head, &ipv6.src_addr, sizeof(ipv6.src_addr)); + head = add_data(head, &ipv6.dst_addr, sizeof(ipv6.dst_addr)); + } + } + } + + return pkt_loop->loopqs[odp_hash_crc32c(data, head - data, 0) % pkt_loop->num_qs].queue; +} + +static int loopback_send(pktio_entry_t *pktio_entry, int index, const odp_packet_t pkt_tbl[], + int num) +{ + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + odp_queue_t queue; + stats_t *stats; int i; int ret; - uint32_t bytes = 0; + int nb_tx = 0; + int tx_ts_idx = 0; + uint8_t tx_ts_enabled = _odp_pktio_tx_ts_enabled(pktio_entry); + odp_pktout_config_opt_t *pktout_cfg = &pktio_entry->config.pktout; + odp_pktout_config_opt_t *pktout_capa = &pktio_entry->capa.config.pktout; - if (odp_unlikely(len > QUEUE_MULTI_MAX)) - len = QUEUE_MULTI_MAX; + if (pkt_loop->num_qs == 0) + return 0; - for (i = 0; i < len; ++i) { - hdr_tbl[i] = packet_to_buf_hdr(pkt_tbl[i]); - bytes += odp_packet_len(pkt_tbl[i]); - } + stats = &pkt_loop->loopqs[index].stats; - odp_ticketlock_lock(&pktio_entry->s.txl); + if (odp_unlikely(num > QUEUE_MULTI_MAX)) + num = QUEUE_MULTI_MAX; - queue = queue_fn->from_ext(pktio_entry->s.pkt_loop.loopq); - ret = queue_fn->enq_multi(queue, hdr_tbl, len); + for (i = 0; i < num; ++i) { + uint32_t pkt_len = odp_packet_len(pkt_tbl[i]); - if (ret > 0) { - pktio_entry->s.stats.out_ucast_pkts += ret; - pktio_entry->s.stats.out_octets += bytes; - } else { - ODP_DBG("queue enqueue failed %i\n", ret); - ret = -1; + if (odp_unlikely(pkt_len > pkt_loop->mtu)) { + if (nb_tx == 0) + return -1; + break; + } + + if (tx_ts_enabled && tx_ts_idx == 0) { + if (odp_unlikely(packet_hdr(pkt_tbl[i])->p.flags.ts_set)) + tx_ts_idx = i + 1; + } + + packet_subtype_set(pkt_tbl[i], ODP_EVENT_PACKET_BASIC); + loopback_fix_checksums(pkt_tbl[i], pktout_cfg, pktout_capa); + queue = get_dest_queue(pkt_loop, pkt_tbl[i], index); + ret = odp_queue_enq(queue, odp_packet_to_event(pkt_tbl[i])); + + if (ret < 0) { + _ODP_DBG("queue enqueue failed %i to queue: %" PRIu64 "\n", ret, + odp_queue_to_u64(queue)); + break; + } + + nb_tx++; + odp_atomic_inc_u64(&stats->out_packets); + odp_atomic_add_u64(&stats->out_octets, pkt_len); } - odp_ticketlock_unlock(&pktio_entry->s.txl); + if (nb_tx > 0) { + if (odp_unlikely(tx_ts_idx) && nb_tx >= tx_ts_idx) + _odp_pktio_tx_ts_set(pktio_entry); + } - return ret; + return nb_tx; } -static uint32_t loopback_mtu_get(pktio_entry_t *pktio_entry ODP_UNUSED) +static uint32_t loopback_mtu_get(pktio_entry_t *pktio_entry) { - /* the loopback interface imposes no maximum transmit size limit */ - return INT_MAX; + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + + return pkt_loop->mtu; } -static int loopback_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED, - void *mac_addr) +static int loopback_mtu_set(pktio_entry_t *pktio_entry, uint32_t maxlen_input, + uint32_t maxlen_output ODP_UNUSED) +{ + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + + pkt_loop->mtu = maxlen_input; + + return 0; +} + +static int loopback_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr) { memcpy(mac_addr, pktio_loop_mac, ETH_ALEN); + ((uint8_t *)mac_addr)[ETH_ALEN - 1] += pkt_priv(pktio_entry)->idx; return ETH_ALEN; } static int loopback_link_status(pktio_entry_t *pktio_entry ODP_UNUSED) { /* loopback interfaces are always up */ - return 1; + return ODP_PKTIO_LINK_STATUS_UP; +} + +static int loopback_link_info(pktio_entry_t *pktio_entry ODP_UNUSED, odp_pktio_link_info_t *info) +{ + memset(info, 0, sizeof(odp_pktio_link_info_t)); + + info->autoneg = ODP_PKTIO_LINK_AUTONEG_OFF; + info->duplex = ODP_PKTIO_LINK_DUPLEX_FULL; + info->media = "virtual"; + info->pause_rx = ODP_PKTIO_LINK_PAUSE_OFF; + info->pause_tx = ODP_PKTIO_LINK_PAUSE_OFF; + info->speed = ODP_PKTIO_LINK_SPEED_UNKNOWN; + info->status = ODP_PKTIO_LINK_STATUS_UP; + + return 0; } -static int loopback_capability(pktio_entry_t *pktio_entry ODP_UNUSED, - odp_pktio_capability_t *capa) +static int loopback_init_capability(pktio_entry_t *pktio_entry) { + odp_pktio_capability_t *capa = &pktio_entry->capa; + odp_queue_capability_t queue_capa; + + if (odp_queue_capability(&queue_capa)) { + _ODP_ERR("Queue capability failed\n"); + return -1; + } + memset(capa, 0, sizeof(odp_pktio_capability_t)); - capa->max_input_queues = 1; - capa->max_output_queues = 1; - capa->set_op.op.promisc_mode = 1; + capa->max_input_queues = ODP_PKTIN_MAX_QUEUES; + capa->max_output_queues = ODP_PKTOUT_MAX_QUEUES; + capa->set_op.op.promisc_mode = 0; + capa->set_op.op.maxlen = 1; + + capa->maxlen.equal = true; + capa->maxlen.min_input = LOOP_MTU_MIN; + capa->maxlen.max_input = LOOP_MTU_MAX; + capa->maxlen.min_output = LOOP_MTU_MIN; + capa->maxlen.max_output = LOOP_MTU_MAX; + + capa->min_input_queue_size = 1; + capa->max_input_queue_size = queue_capa.plain.max_size; + if (capa->max_input_queue_size == 0) + capa->max_input_queue_size = LOOP_MAX_QUEUE_SIZE; + + capa->min_output_queue_size = 1; + capa->max_output_queue_size = queue_capa.plain.max_size; + if (capa->max_output_queue_size == 0) + capa->max_output_queue_size = LOOP_MAX_QUEUE_SIZE; odp_pktio_config_init(&capa->config); + capa->config.enable_loop = 1; capa->config.pktin.bit.ts_all = 1; capa->config.pktin.bit.ts_ptp = 1; + capa->config.pktin.bit.ipv4_chksum = 1; + capa->config.pktin.bit.tcp_chksum = 1; + capa->config.pktin.bit.udp_chksum = 1; + capa->config.pktin.bit.sctp_chksum = 1; + capa->config.pktout.bit.ipv4_chksum = 1; + capa->config.pktout.bit.tcp_chksum = 1; + capa->config.pktout.bit.udp_chksum = 1; + capa->config.pktout.bit.sctp_chksum = 1; + capa->config.pktout.bit.ts_ena = 1; + capa->config.pktout.bit.tx_compl_ena = 1; + capa->tx_compl.mode_all = 1; + capa->tx_compl.mode_event = 1; + capa->tx_compl.mode_poll = 1; + + if (odp_global_ro.disable.ipsec == 0) { + capa->config.inbound_ipsec = 1; + capa->config.outbound_ipsec = 1; + } + + capa->config.pktout.bit.ipv4_chksum_ena = + capa->config.pktout.bit.ipv4_chksum; + capa->config.pktout.bit.udp_chksum_ena = + capa->config.pktout.bit.udp_chksum; + capa->config.pktout.bit.tcp_chksum_ena = + capa->config.pktout.bit.tcp_chksum; + capa->config.pktout.bit.sctp_chksum_ena = + capa->config.pktout.bit.sctp_chksum; + + capa->stats.pktio.counter.in_octets = 1; + capa->stats.pktio.counter.in_packets = 1; + capa->stats.pktio.counter.in_errors = 1; + capa->stats.pktio.counter.in_discards = 1; + capa->stats.pktio.counter.out_octets = 1; + capa->stats.pktio.counter.out_packets = 1; + capa->stats.pktin_queue.counter.octets = 1; + capa->stats.pktin_queue.counter.packets = 1; + capa->stats.pktin_queue.counter.errors = 1; + capa->stats.pktin_queue.counter.discards = 1; + capa->stats.pktout_queue.counter.octets = 1; + capa->stats.pktout_queue.counter.packets = 1; + return 0; +} + +static int loopback_capability(pktio_entry_t *pktio_entry, odp_pktio_capability_t *capa) +{ + *capa = pktio_entry->capa; return 0; } -static int loopback_promisc_mode_set(pktio_entry_t *pktio_entry, - odp_bool_t enable) +static int loopback_promisc_mode_get(pktio_entry_t *pktio_entry ODP_UNUSED) +{ + return 1; +} + +static int loopback_stats(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats) { - pktio_entry->s.pkt_loop.promisc = enable; + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + + memset(stats, 0, sizeof(odp_pktio_stats_t)); + + for (uint32_t i = 0; i < MAX_QUEUES; i++) { + stats_t *qs = &pkt_loop->loopqs[i].stats; + + stats->in_octets += odp_atomic_load_u64(&qs->in_octets); + stats->in_packets += odp_atomic_load_u64(&qs->in_packets); + stats->in_discards += odp_atomic_load_u64(&qs->in_discards); + stats->in_errors += odp_atomic_load_u64(&qs->in_errors); + stats->out_octets += odp_atomic_load_u64(&qs->out_octets); + stats->out_packets += odp_atomic_load_u64(&qs->out_packets); + } + return 0; } -static int loopback_promisc_mode_get(pktio_entry_t *pktio_entry) +static int loopback_stats_reset(pktio_entry_t *pktio_entry) { - return pktio_entry->s.pkt_loop.promisc ? 1 : 0; + pkt_loop_t *pkt_loop = pkt_priv(pktio_entry); + + for (uint32_t i = 0; i < MAX_QUEUES; i++) { + stats_t *qs = &pkt_loop->loopqs[i].stats; + + odp_atomic_store_u64(&qs->in_octets, 0); + odp_atomic_store_u64(&qs->in_packets, 0); + odp_atomic_store_u64(&qs->in_discards, 0); + odp_atomic_store_u64(&qs->in_errors, 0); + odp_atomic_store_u64(&qs->out_octets, 0); + odp_atomic_store_u64(&qs->out_packets, 0); + } + + return 0; } -static int loopback_stats(pktio_entry_t *pktio_entry, - odp_pktio_stats_t *stats) +static int loopback_pktin_stats(pktio_entry_t *pktio_entry, uint32_t index, + odp_pktin_queue_stats_t *pktin_stats) { - memcpy(stats, &pktio_entry->s.stats, sizeof(odp_pktio_stats_t)); + stats_t *qs = &pkt_priv(pktio_entry)->loopqs[index].stats; + + memset(pktin_stats, 0, sizeof(odp_pktin_queue_stats_t)); + pktin_stats->octets = odp_atomic_load_u64(&qs->in_octets); + pktin_stats->packets = odp_atomic_load_u64(&qs->in_packets); + pktin_stats->discards = odp_atomic_load_u64(&qs->in_discards); + pktin_stats->errors = odp_atomic_load_u64(&qs->in_errors); + return 0; } -static int loopback_stats_reset(pktio_entry_t *pktio_entry ODP_UNUSED) +static int loopback_pktout_stats(pktio_entry_t *pktio_entry, uint32_t index, + odp_pktout_queue_stats_t *pktout_stats) { - memset(&pktio_entry->s.stats, 0, sizeof(odp_pktio_stats_t)); + stats_t *qs = &pkt_priv(pktio_entry)->loopqs[index].stats; + + memset(pktout_stats, 0, sizeof(odp_pktout_queue_stats_t)); + pktout_stats->octets = odp_atomic_load_u64(&qs->out_octets); + pktout_stats->packets = odp_atomic_load_u64(&qs->out_packets); + return 0; } static int loop_init_global(void) { - ODP_PRINT("PKTIO: initialized loop interface.\n"); + _ODP_PRINT("PKTIO: initialized loop interface.\n"); return 0; } -const pktio_if_ops_t loopback_pktio_ops = { +const pktio_if_ops_t _odp_loopback_pktio_ops = { .name = "loop", .print = NULL, .init_global = loop_init_global, @@ -259,21 +795,27 @@ const pktio_if_ops_t loopback_pktio_ops = { .term = NULL, .open = loopback_open, .close = loopback_close, - .start = NULL, + .start = loopback_start, .stop = NULL, .stats = loopback_stats, .stats_reset = loopback_stats_reset, + .pktin_queue_stats = loopback_pktin_stats, + .pktout_queue_stats = loopback_pktout_stats, .recv = loopback_recv, .send = loopback_send, - .mtu_get = loopback_mtu_get, - .promisc_mode_set = loopback_promisc_mode_set, + .maxlen_get = loopback_mtu_get, + .maxlen_set = loopback_mtu_set, + .promisc_mode_set = NULL, .promisc_mode_get = loopback_promisc_mode_get, .mac_get = loopback_mac_addr_get, + .mac_set = NULL, .link_status = loopback_link_status, + .link_info = loopback_link_info, .capability = loopback_capability, - .pktin_ts_res = NULL, - .pktin_ts_from_ns = NULL, + .pktio_ts_res = NULL, + .pktio_ts_from_ns = NULL, + .pktio_time = NULL, .config = NULL, - .input_queues_config = NULL, - .output_queues_config = NULL, + .input_queues_config = loopback_pktin_queue_config, + .output_queues_config = loopback_pktout_queue_config, }; |