5.2. spp_vf

This section describes implementation of key features of spp_vf.

spp_vf consists of master thread and several worker threads, forwarder, classifier or merger, as slaves. For classifying packets, spp_vf has a worker thread named classifier and a table for registering MAC address entries.

5.2.1. Initialization

In master thread, data of classifier and VLAN features are initialized after rte_eal_init() is called. Port capability is a set of data for describing VLAN features. Then, each of worker threads are launched with rte_eal_remote_launch() on assigned lcores..

 /* spp_vf.c */

 ret = rte_eal_init(argc, argv);

 /* skipping lines ... */

 /* Start worker threads of classifier and forwarder */
unsigned int lcore_id = 0;
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
        rte_eal_remote_launch(slave_main, NULL, lcore_id);
}

5.2.2. Slave Main

Main function of worker thread is defined as slave_main() which is called from rte_eal_remote_launch(). Behavior of worker thread is decided in while loop in this function. If lcore status is not SPPWK_LCORE_RUNNING, worker thread does nothing. On the other hand, it does packet forwarding with or without classifying. It classifies incoming packets if component type is SPPWK_TYPE_CLS, or simply forwards packets.

/* spp_vf.c */

while ((status = spp_get_core_status(lcore_id)) !=
    SPPWK_LCORE_REQ_STOP) {
    if (status != SPPWK_LCORE_RUNNING)
        continue;

    /* skipping lines ... */

    /* It is for processing multiple components. */
    for (cnt = 0; cnt < core->num; cnt++) {
    /* Component classification to call a function. */
    if (spp_get_component_type(core->id[cnt]) ==
                SPPWK_TYPE_CLS) {
        /* Component type for classifier. */
        ret = classify_packets(core->id[cnt]);
        if (unlikely(ret != 0))
                break;
    } else {
        /* Component type for forward or merge. */
            ret = forward_packets(core->id[cnt]);
            if (unlikely(ret != 0))
                break;
        }
    }

5.2.3. Data structure of classifier

Classifier has a set of attributes for classification as struct mac_classifier, which consists of a table of MAC addresses, number of classifying ports, indices of ports and default index of port. Clasifier table is implemented as hash of struct rte_hash.

/* shared/secondary/spp_worker_th/vf_deps.h */

/* Classifier for MAC addresses. */
struct mac_classifier {
    struct rte_hash *cls_tbl;  /* Hash table for MAC classification. */
    int nof_cls_ports;  /* Num of ports classified validly. */
    int cls_ports[RTE_MAX_ETHPORTS];  /* Ports for classification. */
    int default_cls_idx;  /* Default index for classification. */
};

Classifier itself is defined as a struct cls_comp_info. There are several attributes in this struct including mac_classifier or cls_port_info or so. cls_port_info is for defining a set of attributes of ports, such as interface type, device ID or packet data.

/* shared/secondary/spp_worker_th/vf_deps.h */

/* classifier component information */
struct cls_comp_info {
    char name[STR_LEN_NAME];  /* component name */
    int mac_addr_entry;  /* mac address entry flag */
    struct mac_classifier *mac_clfs[NOF_VLAN];  /* classifiers per VLAN. */
    int nof_tx_ports;  /* Number of TX ports info entries. */
    /* Classifier has one RX port and several TX ports. */
    struct cls_port_info rx_port_i;  /* RX port info classified. */
    struct cls_port_info tx_ports_i[RTE_MAX_ETHPORTS];  /* TX info. */
};

/* Attirbutes of port for classification. */
struct cls_port_info {
    enum port_type iface_type;
    int iface_no;   /* Index of ports handled by classifier. */
    int iface_no_global;  /* ID for interface generated by spp_vf */
    uint16_t ethdev_port_id;  /* Ethdev port ID. */
    uint16_t nof_pkts;  /* Number of packets in pkts[]. */
    struct rte_mbuf *pkts[MAX_PKT_BURST];  /* packets to be classified. */
};

5.2.4. Classifying the packet

If component type is SPPWK_TYPE_CLS, worker thread behaves as a classifier, so component calls classify_packets(). In this function, packets from RX port are received with sppwk_eth_vlan_rx_burst() which is derived from rte_eth_rx_burst() for adding or deleting VLAN tags. Received packets are classified with classify_packet().

/* classifier.c */

n_rx = sppwk_eth_vlan_rx_burst(clsd_data_rx->ethdev_port_id, 0,
    rx_pkts, MAX_PKT_BURST);

/* skipping lines ... */

classify_packet(rx_pkts, n_rx, cmp_info, clsd_data_tx);

5.2.5. Packet processing in forwarder and merger

Configuration data for forwarder and merger is stored as structured tables forward_rxtx, forward_path and forward_info. The forward_rxtx has two member variables for expressing the port to be sent(tx) and to be receive(rx), forward_path has member variables for expressing the data path. Like as mac_classifier, forward_info has two tables, one is for updating by commands, the other is for looking up to process packets.

/* forwarder.c */
/* A set of port info of rx and tx */
struct forward_rxtx {
        struct spp_port_info rx; /* rx port */
        struct spp_port_info tx; /* tx port */
};

/* Information on the path used for forward. */
struct forward_path {
        char name[STR_LEN_NAME];  /* Component name */
        volatile enum sppwk_worker_type wk_type;
        int nof_rx;  /* Number of RX ports */
        int nof_tx;  /* Number of TX ports */
        struct forward_rxtx ports[RTE_MAX_ETHPORTS];  /* Set of RX and TX */
};

/* Information for forward. */
struct forward_info {
        volatile int ref_index; /* index to reference area */
        volatile int upd_index; /* index to update area    */
        struct forward_path path[SPP_INFO_AREA_MAX];
                                /* Information of data path */
};

5.2.6. L2 Multicast Support

spp_vf supports multicast for resolving ARP requests. It is implemented as handle_l2multicast_packet() and called from classify_packet() for incoming multicast packets.

/* classify_packet() in classifier.c */

/* L2 multicast(include broadcast) ? */
if (unlikely(is_multicast_ether_addr(&eth->d_addr))) {
        RTE_LOG(DEBUG, SPP_CLASSIFIER_MAC,
                        "multicast mac address.\n");
        handle_l2multicast_packet(rx_pkts[i],
                        classifier_info,
                        classified_data);
        continue;
}

Packets are cloned with rte_mbuf_refcnt_update() for distributing multicast packets.

/* classifier.c */

handle_l2multicast_packet(struct rte_mbuf *pkt,
        struct cls_comp_info *cmp_info,
        struct cls_port_info *clsd_data)
{
        int i;
        struct mac_classifier *mac_cls;
        uint16_t vid = get_vid(pkt);
        int gen_def_clsd_idx = get_general_default_classified_index(cmp_info);
        int n_act_clsd;

        /* skipping lines... */

        rte_mbuf_refcnt_update(pkt, (int16_t)(n_act_clsd - 1));

5.2.7. Two phase update for forwarding

Update of netowrk configuration in spp_vf is done in a short period of time, but not so short considering the time scale of packet forwarding. It might forward packets before the updating is completed possibly. To avoid such kind of situation, spp_vf has two phase update mechanism. Status info is referred from forwarding process after the update is completed.

int
flush_cmd(void)
{
    int ret;
    int *p_change_comp;
    struct sppwk_comp_info *p_comp_info;
    struct cancel_backup_info *backup_info;

    sppwk_get_mng_data(NULL, &p_comp_info, NULL, NULL, &p_change_comp,
            &backup_info);

    ret = update_port_info();
    if (ret < SPPWK_RET_OK)
        return ret;

    update_lcore_info();

    ret = update_comp_info(p_comp_info, p_change_comp);

    backup_mng_info(backup_info);
    return ret;
}