summaryrefslogtreecommitdiffstats
path: root/src/plugins/lacp/node.h
AgeCommit message (Collapse)AuthorFilesLines
2021-05-13lacp: Prohibiting bonding with remote having different key than 1st memberSteven Luong1-8/+10
After forming the bonding with 1st remote member, Subsequent member must have the same key in order to join the same bonding group. Type: fix Signed-off-by: Steven Luong <sluong@cisco.com> Change-Id: I9ff6d6c083a5b6a26beedbd7181d5a120cb5710b
2020-07-28bonding lacp: replace slave string with memberSteven Luong1-8/+8
- Replace textual string slave with member except APIs. - For APIs, mark the existing APIs as deprecated and introduce new APIs - While introducing sw_bond_interface_dump, add the optional filter by sw_if_index and enhance the testcases to make use of it. Type: improvement Signed-off-by: Steven Luong <sluong@cisco.com> Change-Id: Ib6626c514e45350308aeeda0decb70f3aba2f63e
2019-06-12lacp: cleanup unnecessary pointer castZhiyong Yang1-8/+4
Change-Id: Idc3a7588dc73e7180a15b6ace3684d3c12986b9d Signed-off-by: Zhiyong Yang <zhiyong.yang@intel.com>
2019-06-07build: add -Wall and -fno-common, fix reported issuesBenoît Ganne1-1/+1
Type: refactor Change-Id: I8489ccd54411c2aa9355439c5641dc31012c64a2 Signed-off-by: Benoît Ganne <bganne@cisco.com> Signed-off-by: Damjan Marion <damarion@cisco.com>
2019-06-07lacp: create lacp-process on demandSteven Luong1-1/+4
Create lacp-process when the very first slave interface is added to the bond. Log an event message when lacp-process starts/stops. Be mindful when lacp-process is signalled to stop. Type: refactor Change-Id: I79e10e0a2a385a21a52ae5b8735f24631fdba293 Signed-off-by: Steven Luong <sluong@cisco.com>
2018-04-23lacp: partner may time us out if fast-rate is configured [VPP-1247]Steven1-0/+1
We should be sending LACP PDU every second if the partner has LACP_TIMEOUT flag set which means it will time us out in 3 seconds. Add interface name for lacp trace Change-Id: If7d816c062d03e80cc0dd7d10dba0b76ace0664a Signed-off-by: Steven <sluong@cisco.com>
2018-03-21bond: Add bonding driver and LACP protocolSteven1-0/+276
Add bonding driver to support creation of bond interface which composes of multiple slave interfaces. The slave interfaces could be physical interfaces, or just any virtual interfaces. For example, memif interfaces. The syntax to create a bond interface is create bond mode <lacp | xor | acitve-backup | broadcast | round-robin> To enslave an interface to the bond interface, enslave interface TenGigabitEthernet6/0/0 to BondEthernet0 Please see src/plugins/lacp/lacp_doc.md for more examples and additional options. LACP is a control plane protocol which manages and monitors the status of the slave interfaces. The protocol is part of 802.3ad standard. This patch implements LACPv1. LACPv2 is not supported. To enable LACP on the bond interface, specify "mode lacp" when the bond interface is created. The syntax to enslave a slave interface is the same as other bonding modes. Change-Id: I06581d3b87635972f9f0e1ec50b67560fc13e26c Signed-off-by: Steven <sluong@cisco.com>
id='n292' href='#n292'>292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
/*-
 *   BSD LICENSE
 *
 *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/eventfd.h>

#include "vhost.h"
#include "virtio_user_dev.h"
#include "../virtio_ethdev.h"

static int
virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
{
	/* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come
	 * firstly because vhost depends on this msg to allocate virtqueue
	 * pair.
	 */
	int callfd;
	struct vhost_vring_file file;

	/* May use invalid flag, but some backend leverages kickfd and callfd as
	 * criteria to judge if dev is alive. so finally we use real event_fd.
	 */
	callfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
	if (callfd < 0) {
		PMD_DRV_LOG(ERR, "callfd error, %s\n", strerror(errno));
		return -1;
	}
	file.index = queue_sel;
	file.fd = callfd;
	vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_CALL, &file);
	dev->callfds[queue_sel] = callfd;

	return 0;
}

static int
virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
{
	int kickfd;
	struct vhost_vring_file file;
	struct vhost_vring_state state;
	struct vring *vring = &dev->vrings[queue_sel];
	struct vhost_vring_addr addr = {
		.index = queue_sel,
		.desc_user_addr = (uint64_t)(uintptr_t)vring->desc,
		.avail_user_addr = (uint64_t)(uintptr_t)vring->avail,
		.used_user_addr = (uint64_t)(uintptr_t)vring->used,
		.log_guest_addr = 0,
		.flags = 0, /* disable log */
	};

	state.index = queue_sel;
	state.num = vring->num;
	vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_NUM, &state);

	state.num = 0; /* no reservation */
	vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_BASE, &state);

	vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_ADDR, &addr);

	/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
	 * lastly because vhost depends on this msg to judge if
	 * virtio is ready.
	 */
	kickfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
	if (kickfd < 0) {
		PMD_DRV_LOG(ERR, "kickfd error, %s\n", strerror(errno));
		return -1;
	}
	file.index = queue_sel;
	file.fd = kickfd;
	vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_KICK, &file);
	dev->kickfds[queue_sel] = kickfd;

	return 0;
}

static int
virtio_user_queue_setup(struct virtio_user_dev *dev,
			int (*fn)(struct virtio_user_dev *, uint32_t))
{
	uint32_t i, queue_sel;

	for (i = 0; i < dev->max_queue_pairs; ++i) {
		queue_sel = 2 * i + VTNET_SQ_RQ_QUEUE_IDX;
		if (fn(dev, queue_sel) < 0) {
			PMD_DRV_LOG(INFO, "setup rx vq fails: %u", i);
			return -1;
		}
	}
	for (i = 0; i < dev->max_queue_pairs; ++i) {
		queue_sel = 2 * i + VTNET_SQ_TQ_QUEUE_IDX;
		if (fn(dev, queue_sel) < 0) {
			PMD_DRV_LOG(INFO, "setup tx vq fails: %u", i);
			return -1;
		}
	}

	return 0;
}

int
virtio_user_start_device(struct virtio_user_dev *dev)
{
	uint64_t features;
	int ret;

	/* Do not check return as already done in init, or reset in stop */
	vhost_user_sock(dev->vhostfd, VHOST_USER_SET_OWNER, NULL);

	/* Step 0: tell vhost to create queues */
	if (virtio_user_queue_setup(dev, virtio_user_create_queue) < 0)
		goto error;

	/* Step 1: set features
	 * Make sure VHOST_USER_F_PROTOCOL_FEATURES is added if mq is enabled,
	 * VIRTIO_NET_F_MAC and VIRTIO_NET_F_CTRL_VQ is stripped.
	 */
	features = dev->features;
	if (dev->max_queue_pairs > 1)
		features |= VHOST_USER_MQ;
	features &= ~(1ull << VIRTIO_NET_F_MAC);
	features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
	ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
	if (ret < 0)
		goto error;
	PMD_DRV_LOG(INFO, "set features: %" PRIx64, features);

	/* Step 2: share memory regions */
	ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_MEM_TABLE, NULL);
	if (ret < 0)
		goto error;

	/* Step 3: kick queues */
	if (virtio_user_queue_setup(dev, virtio_user_kick_queue) < 0)
		goto error;

	/* Step 4: enable queues
	 * we enable the 1st queue pair by default.
	 */
	vhost_user_enable_queue_pair(dev->vhostfd, 0, 1);

	return 0;
error:
	/* TODO: free resource here or caller to check */
	return -1;
}

int virtio_user_stop_device(struct virtio_user_dev *dev)
{
	uint32_t i;

	for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
		close(dev->callfds[i]);
		close(dev->kickfds[i]);
	}

	for (i = 0; i < dev->max_queue_pairs; ++i)
		vhost_user_enable_queue_pair(dev->vhostfd, i, 0);

	return 0;
}

static inline void
parse_mac(struct virtio_user_dev *dev, const char *mac)
{
	int i, r;
	uint32_t tmp[ETHER_ADDR_LEN];

	if (!mac)
		return;

	r = sscanf(mac, "%x:%x:%x:%x:%x:%x", &tmp[0],
			&tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
	if (r == ETHER_ADDR_LEN) {
		for (i = 0; i < ETHER_ADDR_LEN; ++i)
			dev->mac_addr[i] = (uint8_t)tmp[i];
		dev->mac_specified = 1;
	} else {
		/* ignore the wrong mac, use random mac */
		PMD_DRV_LOG(ERR, "wrong format of mac: %s", mac);
	}
}

int
virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
		     int cq, int queue_size, const char *mac)
{
	uint32_t i;

	snprintf(dev->path, PATH_MAX, "%s", path);
	dev->max_queue_pairs = queues;
	dev->queue_pairs = 1; /* mq disabled by default */
	dev->queue_size = queue_size;
	dev->mac_specified = 0;
	parse_mac(dev, mac);
	dev->vhostfd = -1;

	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; ++i) {
		dev->kickfds[i] = -1;
		dev->callfds[i] = -1;
	}

	dev->vhostfd = vhost_user_setup(dev->path);
	if (dev->vhostfd < 0) {
		PMD_INIT_LOG(ERR, "backend set up fails");
		return -1;
	}

	if (vhost_user_sock(dev->vhostfd, VHOST_USER_SET_OWNER, NULL) < 0) {
		PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));
		return -1;
	}

	if (vhost_user_sock(dev->vhostfd, VHOST_USER_GET_FEATURES,
			    &dev->device_features) < 0) {
		PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
		return -1;
	}
	if (dev->mac_specified)
		dev->device_features |= (1ull << VIRTIO_NET_F_MAC);

	if (cq) {
		/* device does not really need to know anything about CQ,
		 * so if necessary, we just claim to support CQ
		 */
		dev->device_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
	} else {
		dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
		/* Also disable features depends on VIRTIO_NET_F_CTRL_VQ */
		dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_RX);
		dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_VLAN);
		dev->device_features &= ~(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
		dev->device_features &= ~(1ull << VIRTIO_NET_F_MQ);
		dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
	}

	if (dev->max_queue_pairs > 1) {
		if (!(dev->features & VHOST_USER_MQ)) {
			PMD_INIT_LOG(ERR, "MQ not supported by the backend");
			return -1;
		}
	}

	return 0;
}

void
virtio_user_dev_uninit(struct virtio_user_dev *dev)
{
	close(dev->vhostfd);
}

static uint8_t
virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs)
{
	uint16_t i;
	uint8_t ret = 0;

	if (q_pairs > dev->max_queue_pairs) {
		PMD_INIT_LOG(ERR, "multi-q config %u, but only %u supported",
			     q_pairs, dev->max_queue_pairs);
		return -1;
	}

	for (i = 0; i < q_pairs; ++i)
		ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 1);
	for (i = q_pairs; i < dev->max_queue_pairs; ++i)
		ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 0);

	dev->queue_pairs = q_pairs;

	return ret;
}

static uint32_t
virtio_user_handle_ctrl_msg(struct virtio_user_dev *dev, struct vring *vring,
			    uint16_t idx_hdr)
{
	struct virtio_net_ctrl_hdr *hdr;
	virtio_net_ctrl_ack status = ~0;
	uint16_t i, idx_data, idx_status;
	uint32_t n_descs = 0;

	/* locate desc for header, data, and status */
	idx_data = vring->desc[idx_hdr].next;
	n_descs++;

	i = idx_data;
	while (vring->desc[i].flags == VRING_DESC_F_NEXT) {
		i = vring->desc[i].next;
		n_descs++;
	}

	/* locate desc for status */
	idx_status = i;
	n_descs++;

	hdr = (void *)(uintptr_t)vring->desc[idx_hdr].addr;
	if (hdr->class == VIRTIO_NET_CTRL_MQ &&
	    hdr->cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
		uint16_t queues;

		queues = *(uint16_t *)(uintptr_t)vring->desc[idx_data].addr;
		status = virtio_user_handle_mq(dev, queues);
	}

	/* Update status */
	*(virtio_net_ctrl_ack *)(uintptr_t)vring->desc[idx_status].addr = status;

	return n_descs;
}

void
virtio_user_handle_cq(struct virtio_user_dev *dev, uint16_t queue_idx)
{
	uint16_t avail_idx, desc_idx;
	struct vring_used_elem *uep;
	uint32_t n_descs;
	struct vring *vring = &dev->vrings[queue_idx];

	/* Consume avail ring, using used ring idx as first one */
	while (vring->used->idx != vring->avail->idx) {
		avail_idx = (vring->used->idx) & (vring->num - 1);
		desc_idx = vring->avail->ring[avail_idx];

		n_descs = virtio_user_handle_ctrl_msg(dev, vring, desc_idx);

		/* Update used ring */
		uep = &vring->used->ring[avail_idx];
		uep->id = avail_idx;
		uep->len = n_descs;

		vring->used->idx++;
	}
}