int
rte_eth_bond_mac_address_reset(uint8_t bonded_port_id)
{
	struct rte_eth_dev *bonded_eth_dev;
	struct bond_dev_private *internals;

	if (valid_bonded_port_id(bonded_port_id) != 0)
		return -1;

	bonded_eth_dev = &rte_eth_devices[bonded_port_id];
	internals = bonded_eth_dev->data->dev_private;

	internals->user_defined_mac = 0;

	if (internals->slave_count > 0) {
		/* Set MAC Address of Bonded Device */
		if (mac_address_set(bonded_eth_dev,
				&internals->slaves[internals->primary_port].persisted_mac_addr)
				!= 0) {
			RTE_BOND_LOG(ERR, "Failed to set MAC address on bonded device");
			return -1;
		}
		/* Update all slave devices MAC addresses */
		return mac_address_slaves_update(bonded_eth_dev);
	}
	/* No need to update anything as no slaves present */
	return 0;
}
int
valid_port_id(uint8_t port_id)
{
	/* Verify that port id is valid */
	int ethdev_count = rte_eth_dev_count();
	if (port_id >= ethdev_count) {
		RTE_BOND_LOG(ERR, "Port Id %d is greater than rte_eth_dev_count %d",
				port_id, ethdev_count);
		return -1;
	}

	return 0;
}
int
valid_bonded_port_id(uint8_t port_id)
{
	/* Verify that port id's are valid */
	if (valid_port_id(port_id))
		return -1;

	/* Verify that bonded_port_id refers to a bonded port */
	if (valid_bonded_ethdev(&rte_eth_devices[port_id])) {
		RTE_BOND_LOG(ERR, "Specified port Id %d is not a bonded eth_dev device",
				port_id);
		return -1;
	}

	return 0;
}
/**
 * Parses a port identifier string to a port id by pci address, then by name,
 * and finally port id.
 */
static inline int
parse_port_id(const char *port_str)
{
	struct rte_pci_addr dev_addr;
	struct rte_bus *pci_bus;
	struct rte_device *dev;
	int port_id;

	pci_bus = rte_bus_find_by_name("pci");
	if (pci_bus == NULL) {
		RTE_LOG(ERR, PMD, "unable to find PCI bus\n");
		return -1;
	}

	/* try parsing as pci address, physical devices */
	if (pci_bus->parse(port_str, &dev_addr) == 0) {
		dev = pci_bus->find_device(NULL, bond_pci_addr_cmp, &dev_addr);
		if (dev == NULL) {
			RTE_LOG(ERR, PMD, "unable to find PCI device\n");
			return -1;
		}
		port_id = find_port_id_by_pci_addr(&dev_addr);
		if (port_id < 0)
			return -1;
	} else {
		/* try parsing as device name, virtual devices */
		port_id = find_port_id_by_dev_name(port_str);
		if (port_id < 0) {
			char *end;
			errno = 0;

			/* try parsing as port id */
			port_id = strtol(port_str, &end, 10);
			if (*end != 0 || errno != 0)
				return -1;
		}
	}

	if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) {
		RTE_BOND_LOG(ERR, "Slave port specified (%s) outside expected range",
				port_str);
		return -1;
	}
	return port_id;
}
int
bond_ethdev_parse_slave_port_kvarg(const char *key,
		const char *value, void *extra_args)
{
	struct bond_ethdev_slave_ports *slave_ports;

	if (value == NULL || extra_args == NULL)
		return -1;

	slave_ports = extra_args;

	if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) {
		int port_id = parse_port_id(value);
		if (port_id < 0) {
			RTE_BOND_LOG(ERR, "Invalid slave port value (%s) specified", value);
			return -1;
		} else
			slave_ports->slaves[slave_ports->slave_count++] =
					port_id;
	}
	return 0;
}
static int
__eth_bond_slave_remove_lock_free(uint8_t bonded_port_id, uint8_t slave_port_id)
{
	struct rte_eth_dev *bonded_eth_dev;
	struct bond_dev_private *internals;

	int i, slave_idx;

	if (valid_slave_port_id(slave_port_id) != 0)
		return -1;

	bonded_eth_dev = &rte_eth_devices[bonded_port_id];
	internals = bonded_eth_dev->data->dev_private;

	/* first remove from active slave list */
	slave_idx = find_slave_by_id(internals->active_slaves,
		internals->active_slave_count, slave_port_id);

	if (slave_idx < internals->active_slave_count)
		deactivate_slave(bonded_eth_dev, slave_port_id);

	slave_idx = -1;
	/* now find in slave list */
	for (i = 0; i < internals->slave_count; i++)
		if (internals->slaves[i].port_id == slave_port_id) {
			slave_idx = i;
			break;
		}

	if (slave_idx < 0) {
		RTE_BOND_LOG(ERR, "Couldn't find slave in port list, slave count %d",
				internals->slave_count);
		return -1;
	}

	/* Un-register link status change callback with bonded device pointer as
	 * argument*/
	rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC,
			bond_ethdev_lsc_event_callback,
			&rte_eth_devices[bonded_port_id].data->port_id);

	/* Restore original MAC address of slave device */
	mac_address_set(&rte_eth_devices[slave_port_id],
			&(internals->slaves[slave_idx].persisted_mac_addr));

	slave_remove(internals, &rte_eth_devices[slave_port_id]);

	/*  first slave in the active list will be the primary by default,
	 *  otherwise use first device in list */
	if (internals->current_primary_port == slave_port_id) {
		if (internals->active_slave_count > 0)
			internals->current_primary_port = internals->active_slaves[0];
		else if (internals->slave_count > 0)
			internals->current_primary_port = internals->slaves[0].port_id;
		else
			internals->primary_port = 0;
	}

	if (internals->active_slave_count < 1) {
		/* reset device link properties as no slaves are active */
		link_properties_reset(&rte_eth_devices[bonded_port_id]);

		/* if no slaves are any longer attached to bonded device and MAC is not
		 * user defined then clear MAC of bonded device as it will be reset
		 * when a new slave is added */
		if (internals->slave_count < 1 && !internals->user_defined_mac)
			memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0,
					sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs)));
	}
	if (internals->slave_count == 0) {
		internals->rx_offload_capa = 0;
		internals->tx_offload_capa = 0;
	}
	return 0;
}
static int
__eth_bond_slave_add_lock_free(uint8_t bonded_port_id, uint8_t slave_port_id)
{
	struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev;
	struct bond_dev_private *internals;
	struct bond_dev_private *temp_internals;
	struct rte_eth_link link_props;
	struct rte_eth_dev_info dev_info;

	int i, j;

	if (valid_slave_port_id(slave_port_id) != 0)
		return -1;

	bonded_eth_dev = &rte_eth_devices[bonded_port_id];
	internals = bonded_eth_dev->data->dev_private;

	/* Verify that new slave device is not already a slave of another
	 * bonded device */
	for (i = rte_eth_dev_count()-1; i >= 0; i--) {
		if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) {
			temp_internals = rte_eth_devices[i].data->dev_private;

			for (j = 0; j < temp_internals->slave_count; j++) {
				/* Device already a slave of a bonded device */
				if (temp_internals->slaves[j].port_id == slave_port_id) {
					RTE_BOND_LOG(ERR, "Slave port %d is already a slave",
							slave_port_id);
					return -1;
				}
			}
		}
	}

	slave_eth_dev = &rte_eth_devices[slave_port_id];

	/* Add slave details to bonded device */
	slave_add(internals, slave_eth_dev);

	memset(&dev_info, 0, sizeof(dev_info));
	rte_eth_dev_info_get(slave_port_id, &dev_info);

	if (internals->slave_count < 1) {
		/* if MAC is not user defined then use MAC of first slave add to
		 * bonded device */
		if (!internals->user_defined_mac)
			mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs);

		/* Inherit eth dev link properties from first slave */
		link_properties_set(bonded_eth_dev,
				&(slave_eth_dev->data->dev_link));

		/* Make primary slave */
		internals->primary_port = slave_port_id;

		/* Take the first dev's offload capabilities */
		internals->rx_offload_capa = dev_info.rx_offload_capa;
		internals->tx_offload_capa = dev_info.tx_offload_capa;

	} else {
		/* Check slave link properties are supported if props are set,
		 * all slaves must be the same */
		if (internals->link_props_set) {
			if (link_properties_valid(&(bonded_eth_dev->data->dev_link),
									  &(slave_eth_dev->data->dev_link))) {
				RTE_BOND_LOG(ERR,
						"Slave port %d link speed/duplex not supported",
						slave_port_id);
				return -1;
			}
		} else {
			link_properties_set(bonded_eth_dev,
					&(slave_eth_dev->data->dev_link));
		}
		internals->rx_offload_capa &= dev_info.rx_offload_capa;
		internals->tx_offload_capa &= dev_info.tx_offload_capa;
	}

	internals->slave_count++;

	/* Update all slave devices MACs*/
	mac_address_slaves_update(bonded_eth_dev);

	if (bonded_eth_dev->data->dev_started) {
		if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) {
			RTE_BOND_LOG(ERR, "rte_bond_slaves_configure: port=%d",
					slave_port_id);
			return -1;
		}
	}

	/* Register link status change callback with bonded device pointer as
	 * argument*/
	rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC,
			bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id);

	/* If bonded device is started then we can add the slave to our active
	 * slave array */
	if (bonded_eth_dev->data->dev_started) {
		rte_eth_link_get_nowait(slave_port_id, &link_props);

		 if (link_props.link_status == 1)
			activate_slave(bonded_eth_dev, slave_port_id);
	}
	return 0;

}
int
rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id)
{
	struct rte_pci_device *pci_dev = NULL;
	struct bond_dev_private *internals = NULL;
	struct rte_eth_dev *eth_dev = NULL;
	struct eth_driver *eth_drv = NULL;
	struct rte_pci_driver *pci_drv = NULL;
	struct rte_pci_id *pci_id_table = NULL;
	/* now do all data allocation - for eth_dev structure, dummy pci driver
	 * and internal (private) data
	 */

	if (name == NULL) {
		RTE_BOND_LOG(ERR, "Invalid name specified");
		goto err;
	}

	if (socket_id >= number_of_sockets()) {
		RTE_BOND_LOG(ERR,
				"Invalid socket id specified to create bonded device on.");
		goto err;
	}

	pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id);
	if (pci_dev == NULL) {
		RTE_BOND_LOG(ERR, "Unable to malloc pci dev on socket");
		goto err;
	}

	eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id);
	if (eth_drv == NULL) {
		RTE_BOND_LOG(ERR, "Unable to malloc eth_drv on socket");
		goto err;
	}

	pci_drv = &eth_drv->pci_drv;

	pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id);
	if (pci_id_table == NULL) {
		RTE_BOND_LOG(ERR, "Unable to malloc pci_id_table on socket");
		goto err;
	}

	pci_drv->id_table = pci_id_table;

	pci_drv->id_table->device_id = PCI_ANY_ID;
	pci_drv->id_table->subsystem_device_id = PCI_ANY_ID;
	pci_drv->id_table->vendor_id = PCI_ANY_ID;
	pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID;

	pci_drv->drv_flags = RTE_PCI_DRV_INTR_LSC;

	internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id);
	if (internals == NULL) {
		RTE_BOND_LOG(ERR, "Unable to malloc internals on socket");
		goto err;
	}

	/* reserve an ethdev entry */
	eth_dev = rte_eth_dev_allocate(name, RTE_ETH_DEV_VIRTUAL);
	if (eth_dev == NULL) {
		RTE_BOND_LOG(ERR, "Unable to allocate rte_eth_dev");
		goto err;
	}

	pci_dev->numa_node = socket_id;
	pci_drv->name = driver_name;

	eth_dev->driver = eth_drv;
	eth_dev->data->dev_private = internals;
	eth_dev->data->nb_rx_queues = (uint16_t)1;
	eth_dev->data->nb_tx_queues = (uint16_t)1;

	TAILQ_INIT(&(eth_dev->link_intr_cbs));

	eth_dev->data->dev_link.link_status = 0;

	eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0,
			socket_id);

	eth_dev->data->dev_started = 0;
	eth_dev->data->promiscuous = 0;
	eth_dev->data->scattered_rx = 0;
	eth_dev->data->all_multicast = 0;

	eth_dev->dev_ops = &default_dev_ops;
	eth_dev->pci_dev = pci_dev;

	rte_spinlock_init(&internals->lock);

	internals->port_id = eth_dev->data->port_id;
	internals->mode = BONDING_MODE_INVALID;
	internals->current_primary_port = 0;
	internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2;
	internals->xmit_hash = xmit_l2_hash;
	internals->user_defined_mac = 0;
	internals->link_props_set = 0;

	internals->link_status_polling_enabled = 0;

	internals->link_status_polling_interval_ms = DEFAULT_POLLING_INTERVAL_10_MS;
	internals->link_down_delay_ms = 0;
	internals->link_up_delay_ms = 0;

	internals->slave_count = 0;
	internals->active_slave_count = 0;
	internals->rx_offload_capa = 0;
	internals->tx_offload_capa = 0;

	memset(internals->active_slaves, 0, sizeof(internals->active_slaves));
	memset(internals->slaves, 0, sizeof(internals->slaves));

	/* Set mode 4 default configuration */
	bond_mode_8023ad_setup(eth_dev, NULL);
	if (bond_ethdev_mode_set(eth_dev, mode)) {
		RTE_BOND_LOG(ERR, "Failed to set bonded device %d mode too %d",
				 eth_dev->data->port_id, mode);
		goto err;
	}

	return eth_dev->data->port_id;

err:
	if (pci_dev)
		rte_free(pci_dev);
	if (pci_id_table)
		rte_free(pci_id_table);
	if (eth_drv)
		rte_free(eth_drv);
	if (internals)
		rte_free(internals);
	return -1;
}
static int
__eth_bond_slave_add_lock_free(uint8_t bonded_port_id, uint8_t slave_port_id)
{
	struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev;
	struct bond_dev_private *internals;
	struct rte_eth_link link_props;
	struct rte_eth_dev_info dev_info;

	if (valid_slave_port_id(slave_port_id) != 0)
		return -1;

	bonded_eth_dev = &rte_eth_devices[bonded_port_id];
	internals = bonded_eth_dev->data->dev_private;

	slave_eth_dev = &rte_eth_devices[slave_port_id];
	if (slave_eth_dev->data->dev_flags & RTE_ETH_DEV_BONDED_SLAVE) {
		RTE_BOND_LOG(ERR, "Slave device is already a slave of a bonded device");
		return -1;
	}

	/* Add slave details to bonded device */
	slave_eth_dev->data->dev_flags |= RTE_ETH_DEV_BONDED_SLAVE;

	rte_eth_dev_info_get(slave_port_id, &dev_info);
	if (dev_info.max_rx_pktlen < internals->max_rx_pktlen) {
		RTE_BOND_LOG(ERR, "Slave (port %u) max_rx_pktlen too small",
			     slave_port_id);
		return -1;
	}

	slave_add(internals, slave_eth_dev);

	/* We need to store slaves reta_size to be able to synchronize RETA for all
	 * slave devices even if its sizes are different.
	 */
	internals->slaves[internals->slave_count].reta_size = dev_info.reta_size;

	if (internals->slave_count < 1) {
		/* if MAC is not user defined then use MAC of first slave add to
		 * bonded device */
		if (!internals->user_defined_mac)
			mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs);

		/* Inherit eth dev link properties from first slave */
		link_properties_set(bonded_eth_dev,
				&(slave_eth_dev->data->dev_link));

		/* Make primary slave */
		internals->primary_port = slave_port_id;
		internals->current_primary_port = slave_port_id;

		/* Inherit queues settings from first slave */
		internals->nb_rx_queues = slave_eth_dev->data->nb_rx_queues;
		internals->nb_tx_queues = slave_eth_dev->data->nb_tx_queues;

		internals->reta_size = dev_info.reta_size;

		/* Take the first dev's offload capabilities */
		internals->rx_offload_capa = dev_info.rx_offload_capa;
		internals->tx_offload_capa = dev_info.tx_offload_capa;
		internals->flow_type_rss_offloads = dev_info.flow_type_rss_offloads;

		/* Inherit first slave's max rx packet size */
		internals->candidate_max_rx_pktlen = dev_info.max_rx_pktlen;

	} else {
		internals->rx_offload_capa &= dev_info.rx_offload_capa;
		internals->tx_offload_capa &= dev_info.tx_offload_capa;
		internals->flow_type_rss_offloads &= dev_info.flow_type_rss_offloads;

		/* RETA size is GCD of all slaves RETA sizes, so, if all sizes will be
		 * the power of 2, the lower one is GCD
		 */
		if (internals->reta_size > dev_info.reta_size)
			internals->reta_size = dev_info.reta_size;

		if (!internals->max_rx_pktlen &&
		    dev_info.max_rx_pktlen < internals->candidate_max_rx_pktlen)
			internals->candidate_max_rx_pktlen = dev_info.max_rx_pktlen;
	}

	bonded_eth_dev->data->dev_conf.rx_adv_conf.rss_conf.rss_hf &=
			internals->flow_type_rss_offloads;

	internals->slave_count++;

	/* Update all slave devices MACs*/
	mac_address_slaves_update(bonded_eth_dev);

	if (bonded_eth_dev->data->dev_started) {
		if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) {
			slave_eth_dev->data->dev_flags &= (~RTE_ETH_DEV_BONDED_SLAVE);
			RTE_BOND_LOG(ERR, "rte_bond_slaves_configure: port=%d",
					slave_port_id);
			return -1;
		}
	}

	/* Register link status change callback with bonded device pointer as
	 * argument*/
	rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC,
			bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id);

	/* If bonded device is started then we can add the slave to our active
	 * slave array */
	if (bonded_eth_dev->data->dev_started) {
		rte_eth_link_get_nowait(slave_port_id, &link_props);

		 if (link_props.link_status == ETH_LINK_UP) {
			if (internals->active_slave_count == 0 &&
			    !internals->user_defined_primary_port)
				bond_ethdev_primary_set(internals,
							slave_port_id);

			if (find_slave_by_id(internals->active_slaves,
					     internals->active_slave_count,
					     slave_port_id) == internals->active_slave_count)
				activate_slave(bonded_eth_dev, slave_port_id);
		}
	}

	slave_vlan_filter_set(bonded_port_id, slave_port_id);

	return 0;

}
int
rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id)
{
	struct bond_dev_private *internals = NULL;
	struct rte_eth_dev *eth_dev = NULL;
	uint32_t vlan_filter_bmp_size;

	/* now do all data allocation - for eth_dev structure, dummy pci driver
	 * and internal (private) data
	 */

	if (name == NULL) {
		RTE_BOND_LOG(ERR, "Invalid name specified");
		goto err;
	}

	if (socket_id >= number_of_sockets()) {
		RTE_BOND_LOG(ERR,
				"Invalid socket id specified to create bonded device on.");
		goto err;
	}

	internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id);
	if (internals == NULL) {
		RTE_BOND_LOG(ERR, "Unable to malloc internals on socket");
		goto err;
	}

	/* reserve an ethdev entry */
	eth_dev = rte_eth_dev_allocate(name);
	if (eth_dev == NULL) {
		RTE_BOND_LOG(ERR, "Unable to allocate rte_eth_dev");
		goto err;
	}

	eth_dev->data->dev_private = internals;
	eth_dev->data->nb_rx_queues = (uint16_t)1;
	eth_dev->data->nb_tx_queues = (uint16_t)1;

	eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0,
			socket_id);
	if (eth_dev->data->mac_addrs == NULL) {
		RTE_BOND_LOG(ERR, "Unable to malloc mac_addrs");
		goto err;
	}

	eth_dev->dev_ops = &default_dev_ops;
	eth_dev->data->dev_flags = RTE_ETH_DEV_INTR_LSC |
		RTE_ETH_DEV_DETACHABLE;
	eth_dev->driver = NULL;
	eth_dev->data->kdrv = RTE_KDRV_NONE;
	eth_dev->data->drv_name = pmd_bond_drv.driver.name;
	eth_dev->data->numa_node =  socket_id;

	rte_spinlock_init(&internals->lock);

	internals->port_id = eth_dev->data->port_id;
	internals->mode = BONDING_MODE_INVALID;
	internals->current_primary_port = RTE_MAX_ETHPORTS + 1;
	internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2;
	internals->xmit_hash = xmit_l2_hash;
	internals->user_defined_mac = 0;
	internals->link_props_set = 0;

	internals->link_status_polling_enabled = 0;

	internals->link_status_polling_interval_ms = DEFAULT_POLLING_INTERVAL_10_MS;
	internals->link_down_delay_ms = 0;
	internals->link_up_delay_ms = 0;

	internals->slave_count = 0;
	internals->active_slave_count = 0;
	internals->rx_offload_capa = 0;
	internals->tx_offload_capa = 0;
	internals->candidate_max_rx_pktlen = 0;
	internals->max_rx_pktlen = 0;

	/* Initially allow to choose any offload type */
	internals->flow_type_rss_offloads = ETH_RSS_PROTO_MASK;

	memset(internals->active_slaves, 0, sizeof(internals->active_slaves));
	memset(internals->slaves, 0, sizeof(internals->slaves));

	/* Set mode 4 default configuration */
	bond_mode_8023ad_setup(eth_dev, NULL);
	if (bond_ethdev_mode_set(eth_dev, mode)) {
		RTE_BOND_LOG(ERR, "Failed to set bonded device %d mode too %d",
				 eth_dev->data->port_id, mode);
		goto err;
	}

	vlan_filter_bmp_size =
		rte_bitmap_get_memory_footprint(ETHER_MAX_VLAN_ID + 1);
	internals->vlan_filter_bmpmem = rte_malloc(name, vlan_filter_bmp_size,
						   RTE_CACHE_LINE_SIZE);
	if (internals->vlan_filter_bmpmem == NULL) {
		RTE_BOND_LOG(ERR,
			     "Failed to allocate vlan bitmap for bonded device %u\n",
			     eth_dev->data->port_id);
		goto err;
	}

	internals->vlan_filter_bmp = rte_bitmap_init(ETHER_MAX_VLAN_ID + 1,
			internals->vlan_filter_bmpmem, vlan_filter_bmp_size);
	if (internals->vlan_filter_bmp == NULL) {
		RTE_BOND_LOG(ERR,
			     "Failed to init vlan bitmap for bonded device %u\n",
			     eth_dev->data->port_id);
		rte_free(internals->vlan_filter_bmpmem);
		goto err;
	}

	return eth_dev->data->port_id;

err:
	rte_free(internals);
	if (eth_dev != NULL) {
		rte_free(eth_dev->data->mac_addrs);
		rte_eth_dev_release_port(eth_dev);
	}
	return -1;
}