summaryrefslogtreecommitdiffstats
path: root/docs/index.rst
AgeCommit message (Collapse)AuthorFilesLines
2020-07-06docs: Reflect the fact that the DPDK is now a pluginJohn DeNisco1-9/+11
Type: Improvement Signed-off-by: John DeNisco <jdenisco@cisco.com> Change-Id: Ic1253f601ee515392902a21b92e1dfd3962cc0a4
2019-11-06docs: Rewrite the what is VPP (first) section, also fix the buildJohn DeNisco1-13/+23
Signed-off-by: John DeNisco <jdenisco@cisco.com> Change-Id: Ifb558171f8976a721703e74afea997d006273b5f Signed-off-by: Dave Barach <dave@barachs.net>
2019-10-07docs: grammar police raidDave Barach1-7/+9
Edit the front-page intro text. Fix egregious grammar errors, including an "it's" vs. "its" blunder. Type: docs Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: If9d7d07899c402cd49bbae988110a15216035b02
2019-05-17docs: Fix Versions and Titlesjdenisco1-1/+1
Change-Id: I131e33a570bff8b22c767585d03e2eb281dd7184 Signed-off-by: jdenisco <jdenisco@cisco.com>
2018-11-06docs: Add version and build datejdenisco1-0/+1
Change-Id: I6fd53d3ef12e4b6f1af1e5605c09938bce324ec9 Signed-off-by: jdenisco <jdenisco@cisco.com>
2018-09-18docs: Added Related Projects and Archivejdenisco1-0/+2
Change-Id: I9a6076aa53d810ec44761d9f9459eb91f2c05974 Signed-off-by: jdenisco <jdenisco@cisco.com>
2018-08-30docs: Add features by release sectionScitt Keeler1-0/+1
Change-Id: Ibcae8c8ecdf8a3c2d7bcf96bf7cd5b741e2fad22 Signed-off-by: Scitt Keeler <skeeler@cisco.com>
2018-08-17docs: Moved installing up a level, removed guides.John DeNisco1-1/+0
Change-Id: Ie4b2939b693698585f1f2d12391a0009e80bac0b Signed-off-by: John DeNisco <jdenisco@cisco.com>
2018-08-14DOCS: Moved multiarch and build system, Incorprated Scott's changesJohn DeNisco1-2/+0
Change-Id: I5a57846db2d4faac1aa24db4629b612657f59afb Signed-off-by: John DeNisco <jdenisco@cisco.com>
2018-08-13DOCS: Cleanup Getting StartedJohn DeNisco1-0/+1
Change-Id: I4766773779f8d5c30a24bfed48090d7305c80ec5 Signed-off-by: John DeNisco <jdenisco@cisco.com>
2018-07-26Initial commit of Sphinx docsJohn DeNisco1-0/+30
Change-Id: I9fca8fb98502dffc2555f9de7f507b6f006e0e77 Signed-off-by: John DeNisco <jdenisco@cisco.com>
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 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
/*-
 *   BSD LICENSE
 *
 *   Copyright 2017 6WIND S.A.
 *   Copyright 2017 Mellanox.
 *
 *   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 6WIND S.A. 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 <unistd.h>

#include <rte_flow.h>
#include <rte_flow_driver.h>
#include <rte_cycles.h>

#include "failsafe_private.h"

/** Print a message out of a flow error. */
static int
fs_flow_complain(struct rte_flow_error *error)
{
	static const char *const errstrlist[] = {
		[RTE_FLOW_ERROR_TYPE_NONE] = "no error",
		[RTE_FLOW_ERROR_TYPE_UNSPECIFIED] = "cause unspecified",
		[RTE_FLOW_ERROR_TYPE_HANDLE] = "flow rule (handle)",
		[RTE_FLOW_ERROR_TYPE_ATTR_GROUP] = "group field",
		[RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY] = "priority field",
		[RTE_FLOW_ERROR_TYPE_ATTR_INGRESS] = "ingress field",
		[RTE_FLOW_ERROR_TYPE_ATTR_EGRESS] = "egress field",
		[RTE_FLOW_ERROR_TYPE_ATTR] = "attributes structure",
		[RTE_FLOW_ERROR_TYPE_ITEM_NUM] = "pattern length",
		[RTE_FLOW_ERROR_TYPE_ITEM] = "specific pattern item",
		[RTE_FLOW_ERROR_TYPE_ACTION_NUM] = "number of actions",
		[RTE_FLOW_ERROR_TYPE_ACTION] = "specific action",
	};
	const char *errstr;
	char buf[32];
	int err = rte_errno;

	if ((unsigned int)error->type >= RTE_DIM(errstrlist) ||
			!errstrlist[error->type])
		errstr = "unknown type";
	else
		errstr = errstrlist[error->type];
	ERROR("Caught error type %d (%s): %s%s\n",
		error->type, errstr,
		error->cause ? (snprintf(buf, sizeof(buf), "cause: %p, ",
				error->cause), buf) : "",
		error->message ? error->message : "(no stated reason)");
	return -err;
}

static int
eth_dev_flow_isolate_set(struct rte_eth_dev *dev,
			 struct sub_device *sdev)
{
	struct rte_flow_error ferror;
	int ret;

	if (!PRIV(dev)->flow_isolated) {
		DEBUG("Flow isolation already disabled");
	} else {
		DEBUG("Enabling flow isolation");
		ret = rte_flow_isolate(PORT_ID(sdev),
				       PRIV(dev)->flow_isolated,
				       &ferror);
		if (ret) {
			fs_flow_complain(&ferror);
			return ret;
		}
	}
	return 0;
}

static int
fs_eth_dev_conf_apply(struct rte_eth_dev *dev,
		struct sub_device *sdev)
{
	struct rte_eth_dev *edev;
	struct rte_vlan_filter_conf *vfc1;
	struct rte_vlan_filter_conf *vfc2;
	struct rte_flow *flow;
	struct rte_flow_error ferror;
	uint32_t i;
	int ret;

	edev = ETH(sdev);
	/* RX queue setup */
	for (i = 0; i < dev->data->nb_rx_queues; i++) {
		struct rxq *rxq;

		rxq = dev->data->rx_queues[i];
		ret = rte_eth_rx_queue_setup(PORT_ID(sdev), i,
				rxq->info.nb_desc, rxq->socket_id,
				&rxq->info.conf, rxq->info.mp);
		if (ret) {
			ERROR("rx_queue_setup failed");
			return ret;
		}
	}
	/* TX queue setup */
	for (i = 0; i < dev->data->nb_tx_queues; i++) {
		struct txq *txq;

		txq = dev->data->tx_queues[i];
		ret = rte_eth_tx_queue_setup(PORT_ID(sdev), i,
				txq->info.nb_desc, txq->socket_id,
				&txq->info.conf);
		if (ret) {
			ERROR("tx_queue_setup failed");
			return ret;
		}
	}
	/* dev_link.link_status */
	if (dev->data->dev_link.link_status !=
	    edev->data->dev_link.link_status) {
		DEBUG("Configuring link_status");
		if (dev->data->dev_link.link_status)
			ret = rte_eth_dev_set_link_up(PORT_ID(sdev));
		else
			ret = rte_eth_dev_set_link_down(PORT_ID(sdev));
		if (ret) {
			ERROR("Failed to apply link_status");
			return ret;
		}
	} else {
		DEBUG("link_status already set");
	}
	/* promiscuous */
	if (dev->data->promiscuous != edev->data->promiscuous) {
		DEBUG("Configuring promiscuous");
		if (dev->data->promiscuous)
			rte_eth_promiscuous_enable(PORT_ID(sdev));
		else
			rte_eth_promiscuous_disable(PORT_ID(sdev));
	} else {
		DEBUG("promiscuous already set");
	}
	/* all_multicast */
	if (dev->data->all_multicast != edev->data->all_multicast) {
		DEBUG("Configuring all_multicast");
		if (dev->data->all_multicast)
			rte_eth_allmulticast_enable(PORT_ID(sdev));
		else
			rte_eth_allmulticast_disable(PORT_ID(sdev));
	} else {
		DEBUG("all_multicast already set");
	}
	/* MTU */
	if (dev->data->mtu != edev->data->mtu) {
		DEBUG("Configuring MTU");
		ret = rte_eth_dev_set_mtu(PORT_ID(sdev), dev->data->mtu);
		if (ret) {
			ERROR("Failed to apply MTU");
			return ret;
		}
	} else {
		DEBUG("MTU already set");
	}
	/* default MAC */
	DEBUG("Configuring default MAC address");
	ret = rte_eth_dev_default_mac_addr_set(PORT_ID(sdev),
			&dev->data->mac_addrs[0]);
	if (ret) {
		ERROR("Setting default MAC address failed");
		return ret;
	}
	/* additional MAC */
	if (PRIV(dev)->nb_mac_addr > 1)
		DEBUG("Configure additional MAC address%s",
			(PRIV(dev)->nb_mac_addr > 2 ? "es" : ""));
	for (i = 1; i < PRIV(dev)->nb_mac_addr; i++) {
		struct ether_addr *ea;

		ea = &dev->data->mac_addrs[i];
		ret = rte_eth_dev_mac_addr_add(PORT_ID(sdev), ea,
				PRIV(dev)->mac_addr_pool[i]);
		if (ret) {
			char ea_fmt[ETHER_ADDR_FMT_SIZE];

			ether_format_addr(ea_fmt, ETHER_ADDR_FMT_SIZE, ea);
			ERROR("Adding MAC address %s failed", ea_fmt);
			return ret;
		}
	}
	/* VLAN filter */
	vfc1 = &dev->data->vlan_filter_conf;
	vfc2 = &edev->data->vlan_filter_conf;
	if (memcmp(vfc1, vfc2, sizeof(struct rte_vlan_filter_conf))) {
		uint64_t vbit;
		uint64_t ids;
		size_t i;
		uint16_t vlan_id;

		DEBUG("Configuring VLAN filter");
		for (i = 0; i < RTE_DIM(vfc1->ids); i++) {
			if (vfc1->ids[i] == 0)
				continue;
			ids = vfc1->ids[i];
			while (ids) {
				vlan_id = 64 * i;
				/* count trailing zeroes */
				vbit = ~ids & (ids - 1);
				/* clear least significant bit set */
				ids ^= (ids ^ (ids - 1)) ^ vbit;
				for (; vbit; vlan_id++)
					vbit >>= 1;
				ret = rte_eth_dev_vlan_filter(
					PORT_ID(sdev), vlan_id, 1);
				if (ret) {
					ERROR("Failed to apply VLAN filter %hu",
						vlan_id);
					return ret;
				}
			}
		}
	} else {
		DEBUG("VLAN filter already set");
	}
	/* rte_flow */
	if (TAILQ_EMPTY(&PRIV(dev)->flow_list)) {
		DEBUG("rte_flow already set");
	} else {
		DEBUG("Resetting rte_flow configuration");
		ret = rte_flow_flush(PORT_ID(sdev), &ferror);
		if (ret) {
			fs_flow_complain(&ferror);
			return ret;
		}
		i = 0;
		rte_errno = 0;
		DEBUG("Configuring rte_flow");
		TAILQ_FOREACH(flow, &PRIV(dev)->flow_list, next) {
			DEBUG("Creating flow #%" PRIu32, i++);
			flow->flows[SUB_ID(sdev)] =
				rte_flow_create(PORT_ID(sdev),
						&flow->fd->attr,
						flow->fd->items,
						flow->fd->actions,
						&ferror);
			ret = rte_errno;
			if (ret)
				break;
		}
		if (ret) {
			fs_flow_complain(&ferror);
			return ret;
		}
	}
	return 0;
}

static void
fs_dev_remove(struct sub_device *sdev)
{
	int ret;

	if (sdev == NULL)
		return;
	switch (sdev->state) {
	case DEV_STARTED:
		rte_eth_dev_stop(PORT_ID(sdev));
		sdev->state = DEV_ACTIVE;
		/* fallthrough */
	case DEV_ACTIVE:
		failsafe_eth_dev_unregister_callbacks(sdev);
		rte_eth_dev_close(PORT_ID(sdev));
		sdev->state = DEV_PROBED;
		/* fallthrough */
	case DEV_PROBED:
		ret = rte_eal_hotplug_remove(sdev->bus->name,
					     sdev->dev->name);
		if (ret) {
			ERROR("Bus detach failed for sub_device %u",
			      SUB_ID(sdev));
		} else {
			ETH(sdev)->state = RTE_ETH_DEV_UNUSED;
		}
		sdev->state = DEV_PARSED;
		/* fallthrough */
	case DEV_PARSED:
	case DEV_UNDEFINED:
		sdev->state = DEV_UNDEFINED;
		/* the end */
		break;
	}
	failsafe_hotplug_alarm_install(sdev->fs_dev);
}

static void
fs_dev_stats_save(struct sub_device *sdev)
{
	struct rte_eth_stats stats;
	int err;

	/* Attempt to read current stats. */
	err = rte_eth_stats_get(PORT_ID(sdev), &stats);
	if (err) {
		uint64_t timestamp = sdev->stats_snapshot.timestamp;

		WARN("Could not access latest statistics from sub-device %d.\n",
			 SUB_ID(sdev));
		if (timestamp != 0)
			WARN("Using latest snapshot taken before %"PRIu64" seconds.\n",
				 (rte_rdtsc() - timestamp) / rte_get_tsc_hz());
	}
	failsafe_stats_increment(&PRIV(sdev->fs_dev)->stats_accumulator,
			err ? &sdev->stats_snapshot.stats : &stats);
	memset(&sdev->stats_snapshot, 0, sizeof(sdev->stats_snapshot));
}

static inline int
fs_rxtx_clean(struct sub_device *sdev)
{
	uint16_t i;

	for (i = 0; i < ETH(sdev)->data->nb_rx_queues; i++)
		if (FS_ATOMIC_RX(sdev, i))
			return 0;
	for (i = 0; i < ETH(sdev)->data->nb_tx_queues; i++)
		if (FS_ATOMIC_TX(sdev, i))
			return 0;
	return 1;
}

void
failsafe_eth_dev_unregister_callbacks(struct sub_device *sdev)
{
	int ret;

	if (sdev == NULL)
		return;
	if (sdev->rmv_callback) {
		ret = rte_eth_dev_callback_unregister(PORT_ID(sdev),
						RTE_ETH_EVENT_INTR_RMV,
						failsafe_eth_rmv_event_callback,
						sdev);
		if (ret)
			WARN("Failed to unregister RMV callback for sub_device"
			     " %d", SUB_ID(sdev));
		sdev->rmv_callback = 0;
	}
	if (sdev->lsc_callback) {
		ret = rte_eth_dev_callback_unregister(PORT_ID(sdev),
						RTE_ETH_EVENT_INTR_LSC,
						failsafe_eth_lsc_event_callback,
						sdev);
		if (ret)
			WARN("Failed to unregister LSC callback for sub_device"
			     " %d", SUB_ID(sdev));
		sdev->lsc_callback = 0;
	}
}

void
failsafe_dev_remove(struct rte_eth_dev *dev)
{
	struct sub_device *sdev;
	uint8_t i;

	FOREACH_SUBDEV_STATE(sdev, i, dev, DEV_ACTIVE)
		if (sdev->remove && fs_rxtx_clean(sdev)) {
			fs_dev_stats_save(sdev);
			fs_dev_remove(sdev);
		}
}

int
failsafe_eth_dev_state_sync(struct rte_eth_dev *dev)
{
	struct sub_device *sdev;
	uint32_t inactive;
	int ret;
	uint8_t i;

	if (PRIV(dev)->state < DEV_PARSED)
		return 0;

	ret = failsafe_args_parse_subs(dev);
	if (ret)
		goto err_remove;

	if (PRIV(dev)->state < DEV_PROBED)
		return 0;
	ret = failsafe_eal_init(dev);
	if (ret)
		goto err_remove;
	if (PRIV(dev)->state < DEV_ACTIVE)
		return 0;
	inactive = 0;
	FOREACH_SUBDEV(sdev, i, dev) {
		if (sdev->state == DEV_PROBED) {
			inactive |= UINT32_C(1) << i;
			ret = eth_dev_flow_isolate_set(dev, sdev);
			if (ret) {
				ERROR("Could not apply configuration to sub_device %d",
				      i);
				goto err_remove;
			}
		}
	}
	ret = dev->dev_ops->dev_configure(dev);
	if (ret)
		goto err_remove;
	FOREACH_SUBDEV(sdev, i, dev) {
		if (inactive & (UINT32_C(1) << i)) {
			ret = fs_eth_dev_conf_apply(dev, sdev);
			if (ret) {
				ERROR("Could not apply configuration to sub_device %d",
				      i);
				goto err_remove;
			}
		}
	}
	/*
	 * If new devices have been configured, check if
	 * the link state has changed.
	 */
	if (inactive)
		dev->dev_ops->link_update(dev, 1);
	if (PRIV(dev)->state < DEV_STARTED)
		return 0;
	ret = dev->dev_ops->dev_start(dev);
	if (ret)
		goto err_remove;
	return 0;
err_remove:
	FOREACH_SUBDEV(sdev, i, dev)
		if (sdev->state != PRIV(dev)->state)
			sdev->remove = 1;
	return ret;
}

void
failsafe_stats_increment(struct rte_eth_stats *to, struct rte_eth_stats *from)
{
	uint32_t i;

	RTE_ASSERT(to != NULL && from != NULL);
	to->ipackets += from->ipackets;
	to->opackets += from->opackets;
	to->ibytes += from->ibytes;
	to->obytes += from->obytes;
	to->imissed += from->imissed;
	to->ierrors += from->ierrors;
	to->oerrors += from->oerrors;
	to->rx_nombuf += from->rx_nombuf;
	for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) {
		to->q_ipackets[i] += from->q_ipackets[i];
		to->q_opackets[i] += from->q_opackets[i];
		to->q_ibytes[i] += from->q_ibytes[i];
		to->q_obytes[i] += from->q_obytes[i];
		to->q_errors[i] += from->q_errors[i];
	}
}

int
failsafe_eth_rmv_event_callback(uint16_t port_id __rte_unused,
				enum rte_eth_event_type event __rte_unused,
				void *cb_arg, void *out __rte_unused)
{
	struct sub_device *sdev = cb_arg;

	/* Switch as soon as possible tx_dev. */
	fs_switch_dev(sdev->fs_dev, sdev);
	/* Use safe bursts in any case. */
	set_burst_fn(sdev->fs_dev, 1);
	/*
	 * Async removal, the sub-PMD will try to unregister
	 * the callback at the source of the current thread context.
	 */
	sdev->remove = 1;
	return 0;
}

int
failsafe_eth_lsc_event_callback(uint16_t port_id __rte_unused,
				enum rte_eth_event_type event __rte_unused,
				void *cb_arg, void *out __rte_unused)
{
	struct rte_eth_dev *dev = cb_arg;
	int ret;

	ret = dev->dev_ops->link_update(dev, 0);
	/* We must pass on the LSC event */
	if (ret)
		return _rte_eth_dev_callback_process(dev,
						     RTE_ETH_EVENT_INTR_LSC,
						     NULL, NULL);
	else
		return 0;
}