Got to the adaption of the existing representor plugin code to the
framework as the the last thing prior to submitting to reach the
soft-freeze, it could use this:
--- a/lib/plug_providers/representor/plug-representor.c
+++ b/lib/plug_providers/representor/plug-representor.c
@@ -228,11 +228,16 @@ plug_representor_port_prepare(const struct
plug_port_ctx_in *ctx_in,
char *rep_port;
rep_port = shash_find_data(&devlink_ports, keybuf);
- VLOG_INFO("plug_representor %s (%s) -> %s",
- ctx_in->lport_name, rep_port, ctx_in->br_int->name);
+ if (!rep_port) {
+ VLOG_INFO("No representor port found for "
+ "lport: %s pf-mac: '%s' vf-num: '%s'",
+ ctx_in->lport_name, pf_mac, vf_num);
+ return false;
+ }
ctx_out->name = rep_port;
ctx_out->type = NULL;
ctx_out->iface_options = NULL;
+
return true;
}
Otherwise I'm working on the runtime update part of this module and
will submit once I've received any other feedback on this series.
On Fri, Sep 3, 2021 at 9:28 PM Frode Nordahl
<[email protected]> wrote:
>
> Add the first in-tree plug provider plugin and its dependencies.
> The representor plugin can be used with multiple NIC vendors
> supporting Open vSwitch hardware offload and the devlink-port
> infrastructure[0].
>
> It is particularly useful for use with NICs connected to multiple
> distinct CPUs where the instance runs on one host and Open
> vSwitch and OVN runs on a different host, the smartnic CPU.
>
> Extend the build system with macros from the OVS build system to
> allow checking for dependencies of the plugin as well as providing
> kernel header files that may not be available at build time.
>
> The plugin will only be built when enabled and when building on
> a Linux system.
>
> 0: https://www.kernel.org/doc/html/latest/networking/devlink/devlink-port.html
> Signed-off-by: Frode Nordahl <[email protected]>
> ---
> Documentation/automake.mk | 1 +
> Documentation/topics/plug_providers/index.rst | 1 +
> .../topics/plug_providers/plug-providers.rst | 5 +
> .../plug_providers/plug-representor.rst | 45 ++
> build-aux/initial-tab-whitelist | 1 +
> configure.ac | 2 +
> include/automake.mk | 4 +
> include/linux/automake.mk | 2 +
> include/linux/devlink.h | 625 ++++++++++++++++++
> lib/automake.mk | 11 +
> lib/plug-provider.h | 6 +-
> lib/plug.c | 1 +
> .../representor/netlink-devlink.c | 499 ++++++++++++++
> .../representor/netlink-devlink.h | 115 ++++
> .../representor/plug-representor.c | 307 +++++++++
> m4/ovn.m4 | 26 +
> 16 files changed, 1650 insertions(+), 1 deletion(-)
> create mode 100644 Documentation/topics/plug_providers/plug-representor.rst
> create mode 100644 include/linux/automake.mk
> create mode 100644 include/linux/devlink.h
> create mode 100644 lib/plug_providers/representor/netlink-devlink.c
> create mode 100644 lib/plug_providers/representor/netlink-devlink.h
> create mode 100644 lib/plug_providers/representor/plug-representor.c
>
> diff --git a/Documentation/automake.mk b/Documentation/automake.mk
> index 92a843d76..2a3483bc0 100644
> --- a/Documentation/automake.mk
> +++ b/Documentation/automake.mk
> @@ -29,6 +29,7 @@ DOC_SOURCE = \
> Documentation/topics/role-based-access-control.rst \
> Documentation/topics/debugging-ddlog.rst \
> Documentation/topics/plug_providers/plug-providers.rst \
> + Documentation/topics/plug_providers/plug-representor.rst \
> Documentation/howto/index.rst \
> Documentation/howto/docker.rst \
> Documentation/howto/firewalld.rst \
> diff --git a/Documentation/topics/plug_providers/index.rst
> b/Documentation/topics/plug_providers/index.rst
> index 837eeae15..3d16458a2 100644
> --- a/Documentation/topics/plug_providers/index.rst
> +++ b/Documentation/topics/plug_providers/index.rst
> @@ -30,3 +30,4 @@ Plug Providers
> :maxdepth: 2
>
> plug-providers
> + plug-representor
> diff --git a/Documentation/topics/plug_providers/plug-providers.rst
> b/Documentation/topics/plug_providers/plug-providers.rst
> index 7b891156c..5f0089ed9 100644
> --- a/Documentation/topics/plug_providers/plug-providers.rst
> +++ b/Documentation/topics/plug_providers/plug-providers.rst
> @@ -163,6 +163,11 @@ Building with in-tree plugging providers
> Plugging providers hosted in the OVN repository living under
> `lib/plug_providers`:
>
> +* :doc:`representor <plug-representor>`
> +
> + - Representor port lookup making use of the Linux kernel devlink-port
> + infrastructure.
> +
> To enable them, provide the `--enable-plug-providers` command line option to
> the configure script when building OVN.
>
> diff --git a/Documentation/topics/plug_providers/plug-representor.rst
> b/Documentation/topics/plug_providers/plug-representor.rst
> new file mode 100644
> index 000000000..c301a6cd2
> --- /dev/null
> +++ b/Documentation/topics/plug_providers/plug-representor.rst
> @@ -0,0 +1,45 @@
> +..
> + Licensed under the Apache License, Version 2.0 (the "License"); you may
> + not use this file except in compliance with the License. You may obtain
> + a copy of the License at
> +
> + http://www.apache.org/licenses/LICENSE-2.0
> +
> + Unless required by applicable law or agreed to in writing, software
> + distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
> the
> + License for the specific language governing permissions and limitations
> + under the License.
> +
> + Convention for heading levels in OVN documentation:
> +
> + ======= Heading 0 (reserved for the title in a document)
> + ------- Heading 1
> + ~~~~~~~ Heading 2
> + +++++++ Heading 3
> + ''''''' Heading 4
> +
> + Avoid deeper levels because they do not render well.
> +
> +=============================
> +The Representor Plug Provider
> +=============================
> +
> +Logical Switch Port Options
> +---------------------------
> +
> +plug:representor:pf-mac
> +~~~~~~~~~~~~~~~~~~~~~~~
> +
> +MAC address for identifying PF device. When
> +`OVN_Northbound:Logical_Switch_Port:options` key `plug:representor:vf-num` is
> +also set, this option is used to identify PF to use as base to locate the
> +correct VF representor port. When
> `OVN_Northbound:Logical_Switch_Port:options`
> +key `plug:representor:vf-num` is not set this option is used to locate a PF
> +representor port.
> +
> +plug:representor:vf-num
> +~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Logical VF number relative to PF device specified in
> +`OVN_Northbound:Logical_Switch_Port:options` key `plug-pf-mac`.
> diff --git a/build-aux/initial-tab-whitelist b/build-aux/initial-tab-whitelist
> index b2f5a0791..c70f93891 100644
> --- a/build-aux/initial-tab-whitelist
> +++ b/build-aux/initial-tab-whitelist
> @@ -3,6 +3,7 @@
> \.mk$
> \.png$
> \.sln$
> +^include/linux/
> ^ovs/
> ^third-party/
> ^xenserver/
> diff --git a/configure.ac b/configure.ac
> index 7f3274e59..5b542bfd1 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -174,6 +174,8 @@ OVS_CHECK_PRAGMA_MESSAGE
> OVN_CHECK_OVS
> OVN_CHECK_PLUG_PROVIDER
> OVN_ENABLE_PLUG
> +OVS_CHECK_NETLINK
> +OVS_CHECK_LINUX_NETLINK
> OVS_CTAGS_IDENTIFIERS
> AC_SUBST([OVS_CFLAGS])
> AC_SUBST([OVS_LDFLAGS])
> diff --git a/include/automake.mk b/include/automake.mk
> index 9e8403f8d..75638bd9a 100644
> --- a/include/automake.mk
> +++ b/include/automake.mk
> @@ -1,2 +1,6 @@
> include include/ovn/automake.mk
>
> +if LINUX
> +include include/linux/automake.mk
> +endif
> +
> diff --git a/include/linux/automake.mk b/include/linux/automake.mk
> new file mode 100644
> index 000000000..5b53597eb
> --- /dev/null
> +++ b/include/linux/automake.mk
> @@ -0,0 +1,2 @@
> +noinst_HEADERS += \
> + include/linux/devlink.h
> diff --git a/include/linux/devlink.h b/include/linux/devlink.h
> new file mode 100644
> index 000000000..28ea92b62
> --- /dev/null
> +++ b/include/linux/devlink.h
> @@ -0,0 +1,625 @@
> +/*
> + * The kernel devlink interface has gained a number of additions in recent
> + * kernel versions. To allow Open vSwitch to consume these interfaces in its
> + * runtime environment regardless of what kernel version was available at
> build
> + * time, and also avoiding an elaborate set of autoconf macros to check for
> + * presence of individual pieces, we include the entire file here.
> + *
> + * Source:
> + *
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/devlink.h
> @ a556dded9c23c51c82654f1ebe389cbc0bc22057 */
> +#if !defined(__KERNEL__)
> +#ifndef __UAPI_LINUX_DEVLINK_WRAPPER_H
> +#define __UAPI_LINUX_DEVLINK_WRAPPER_H 1
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * include/uapi/linux/devlink.h - Network physical device Netlink interface
> + * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
> + * Copyright (c) 2016 Jiri Pirko <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef _UAPI_LINUX_DEVLINK_H_
> +#define _UAPI_LINUX_DEVLINK_H_
> +
> +#include <linux/const.h>
> +
> +#define DEVLINK_GENL_NAME "devlink"
> +#define DEVLINK_GENL_VERSION 0x1
> +#define DEVLINK_GENL_MCGRP_CONFIG_NAME "config"
> +
> +enum devlink_command {
> + /* don't change the order or add anything between, this is ABI! */
> + DEVLINK_CMD_UNSPEC,
> +
> + DEVLINK_CMD_GET, /* can dump */
> + DEVLINK_CMD_SET,
> + DEVLINK_CMD_NEW,
> + DEVLINK_CMD_DEL,
> +
> + DEVLINK_CMD_PORT_GET, /* can dump */
> + DEVLINK_CMD_PORT_SET,
> + DEVLINK_CMD_PORT_NEW,
> + DEVLINK_CMD_PORT_DEL,
> +
> + DEVLINK_CMD_PORT_SPLIT,
> + DEVLINK_CMD_PORT_UNSPLIT,
> +
> + DEVLINK_CMD_SB_GET, /* can dump */
> + DEVLINK_CMD_SB_SET,
> + DEVLINK_CMD_SB_NEW,
> + DEVLINK_CMD_SB_DEL,
> +
> + DEVLINK_CMD_SB_POOL_GET, /* can dump */
> + DEVLINK_CMD_SB_POOL_SET,
> + DEVLINK_CMD_SB_POOL_NEW,
> + DEVLINK_CMD_SB_POOL_DEL,
> +
> + DEVLINK_CMD_SB_PORT_POOL_GET, /* can dump */
> + DEVLINK_CMD_SB_PORT_POOL_SET,
> + DEVLINK_CMD_SB_PORT_POOL_NEW,
> + DEVLINK_CMD_SB_PORT_POOL_DEL,
> +
> + DEVLINK_CMD_SB_TC_POOL_BIND_GET, /* can dump */
> + DEVLINK_CMD_SB_TC_POOL_BIND_SET,
> + DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
> + DEVLINK_CMD_SB_TC_POOL_BIND_DEL,
> +
> + /* Shared buffer occupancy monitoring commands */
> + DEVLINK_CMD_SB_OCC_SNAPSHOT,
> + DEVLINK_CMD_SB_OCC_MAX_CLEAR,
> +
> + DEVLINK_CMD_ESWITCH_GET,
> +#define DEVLINK_CMD_ESWITCH_MODE_GET /* obsolete, never use this! */ \
> + DEVLINK_CMD_ESWITCH_GET
> +
> + DEVLINK_CMD_ESWITCH_SET,
> +#define DEVLINK_CMD_ESWITCH_MODE_SET /* obsolete, never use this! */ \
> + DEVLINK_CMD_ESWITCH_SET
> +
> + DEVLINK_CMD_DPIPE_TABLE_GET,
> + DEVLINK_CMD_DPIPE_ENTRIES_GET,
> + DEVLINK_CMD_DPIPE_HEADERS_GET,
> + DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
> + DEVLINK_CMD_RESOURCE_SET,
> + DEVLINK_CMD_RESOURCE_DUMP,
> +
> + /* Hot driver reload, makes configuration changes take place. The
> + * devlink instance is not released during the process.
> + */
> + DEVLINK_CMD_RELOAD,
> +
> + DEVLINK_CMD_PARAM_GET, /* can dump */
> + DEVLINK_CMD_PARAM_SET,
> + DEVLINK_CMD_PARAM_NEW,
> + DEVLINK_CMD_PARAM_DEL,
> +
> + DEVLINK_CMD_REGION_GET,
> + DEVLINK_CMD_REGION_SET,
> + DEVLINK_CMD_REGION_NEW,
> + DEVLINK_CMD_REGION_DEL,
> + DEVLINK_CMD_REGION_READ,
> +
> + DEVLINK_CMD_PORT_PARAM_GET, /* can dump */
> + DEVLINK_CMD_PORT_PARAM_SET,
> + DEVLINK_CMD_PORT_PARAM_NEW,
> + DEVLINK_CMD_PORT_PARAM_DEL,
> +
> + DEVLINK_CMD_INFO_GET, /* can dump */
> +
> + DEVLINK_CMD_HEALTH_REPORTER_GET,
> + DEVLINK_CMD_HEALTH_REPORTER_SET,
> + DEVLINK_CMD_HEALTH_REPORTER_RECOVER,
> + DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
> + DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
> + DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR,
> +
> + DEVLINK_CMD_FLASH_UPDATE,
> + DEVLINK_CMD_FLASH_UPDATE_END, /* notification only */
> + DEVLINK_CMD_FLASH_UPDATE_STATUS, /* notification only */
> +
> + DEVLINK_CMD_TRAP_GET, /* can dump */
> + DEVLINK_CMD_TRAP_SET,
> + DEVLINK_CMD_TRAP_NEW,
> + DEVLINK_CMD_TRAP_DEL,
> +
> + DEVLINK_CMD_TRAP_GROUP_GET, /* can dump */
> + DEVLINK_CMD_TRAP_GROUP_SET,
> + DEVLINK_CMD_TRAP_GROUP_NEW,
> + DEVLINK_CMD_TRAP_GROUP_DEL,
> +
> + DEVLINK_CMD_TRAP_POLICER_GET, /* can dump */
> + DEVLINK_CMD_TRAP_POLICER_SET,
> + DEVLINK_CMD_TRAP_POLICER_NEW,
> + DEVLINK_CMD_TRAP_POLICER_DEL,
> +
> + DEVLINK_CMD_HEALTH_REPORTER_TEST,
> +
> + /* add new commands above here */
> + __DEVLINK_CMD_MAX,
> + DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
> +};
> +
> +enum devlink_port_type {
> + DEVLINK_PORT_TYPE_NOTSET,
> + DEVLINK_PORT_TYPE_AUTO,
> + DEVLINK_PORT_TYPE_ETH,
> + DEVLINK_PORT_TYPE_IB,
> +};
> +
> +enum devlink_sb_pool_type {
> + DEVLINK_SB_POOL_TYPE_INGRESS,
> + DEVLINK_SB_POOL_TYPE_EGRESS,
> +};
> +
> +/* static threshold - limiting the maximum number of bytes.
> + * dynamic threshold - limiting the maximum number of bytes
> + * based on the currently available free space in the shared buffer pool.
> + * In this mode, the maximum quota is calculated based
> + * on the following formula:
> + * max_quota = alpha / (1 + alpha) * Free_Buffer
> + * While Free_Buffer is the amount of none-occupied buffer associated to
> + * the relevant pool.
> + * The value range which can be passed is 0-20 and serves
> + * for computation of alpha by following formula:
> + * alpha = 2 ^ (passed_value - 10)
> + */
> +
> +enum devlink_sb_threshold_type {
> + DEVLINK_SB_THRESHOLD_TYPE_STATIC,
> + DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC,
> +};
> +
> +#define DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX 20
> +
> +enum devlink_eswitch_mode {
> + DEVLINK_ESWITCH_MODE_LEGACY,
> + DEVLINK_ESWITCH_MODE_SWITCHDEV,
> +};
> +
> +enum devlink_eswitch_inline_mode {
> + DEVLINK_ESWITCH_INLINE_MODE_NONE,
> + DEVLINK_ESWITCH_INLINE_MODE_LINK,
> + DEVLINK_ESWITCH_INLINE_MODE_NETWORK,
> + DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT,
> +};
> +
> +enum devlink_eswitch_encap_mode {
> + DEVLINK_ESWITCH_ENCAP_MODE_NONE,
> + DEVLINK_ESWITCH_ENCAP_MODE_BASIC,
> +};
> +
> +enum devlink_port_flavour {
> + DEVLINK_PORT_FLAVOUR_PHYSICAL, /* Any kind of a port physically
> + * facing the user.
> + */
> + DEVLINK_PORT_FLAVOUR_CPU, /* CPU port */
> + DEVLINK_PORT_FLAVOUR_DSA, /* Distributed switch architecture
> + * interconnect port.
> + */
> + DEVLINK_PORT_FLAVOUR_PCI_PF, /* Represents eswitch port for
> + * the PCI PF. It is an internal
> + * port that faces the PCI PF.
> + */
> + DEVLINK_PORT_FLAVOUR_PCI_VF, /* Represents eswitch port
> + * for the PCI VF. It is an internal
> + * port that faces the PCI VF.
> + */
> + DEVLINK_PORT_FLAVOUR_VIRTUAL, /* Any virtual port facing the user. */
> + DEVLINK_PORT_FLAVOUR_UNUSED, /* Port which exists in the switch, but
> + * is not used in any way.
> + */
> + DEVLINK_PORT_FLAVOUR_PCI_SF, /* Represents eswitch port
> + * for the PCI SF. It is an internal
> + * port that faces the PCI SF.
> + */
> +};
> +
> +enum devlink_param_cmode {
> + DEVLINK_PARAM_CMODE_RUNTIME,
> + DEVLINK_PARAM_CMODE_DRIVERINIT,
> + DEVLINK_PARAM_CMODE_PERMANENT,
> +
> + /* Add new configuration modes above */
> + __DEVLINK_PARAM_CMODE_MAX,
> + DEVLINK_PARAM_CMODE_MAX = __DEVLINK_PARAM_CMODE_MAX - 1
> +};
> +
> +enum devlink_param_fw_load_policy_value {
> + DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
> + DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
> + DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK,
> + DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_UNKNOWN,
> +};
> +
> +enum devlink_param_reset_dev_on_drv_probe_value {
> + DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN,
> + DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS,
> + DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER,
> + DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK,
> +};
> +
> +enum {
> + DEVLINK_ATTR_STATS_RX_PACKETS, /* u64 */
> + DEVLINK_ATTR_STATS_RX_BYTES, /* u64 */
> + DEVLINK_ATTR_STATS_RX_DROPPED, /* u64 */
> +
> + __DEVLINK_ATTR_STATS_MAX,
> + DEVLINK_ATTR_STATS_MAX = __DEVLINK_ATTR_STATS_MAX - 1
> +};
> +
> +/* Specify what sections of a flash component can be overwritten when
> + * performing an update. Overwriting of firmware binary sections is always
> + * implicitly assumed to be allowed.
> + *
> + * Each section must be documented in
> + * Documentation/networking/devlink/devlink-flash.rst
> + *
> + */
> +enum {
> + DEVLINK_FLASH_OVERWRITE_SETTINGS_BIT,
> + DEVLINK_FLASH_OVERWRITE_IDENTIFIERS_BIT,
> +
> + __DEVLINK_FLASH_OVERWRITE_MAX_BIT,
> + DEVLINK_FLASH_OVERWRITE_MAX_BIT = __DEVLINK_FLASH_OVERWRITE_MAX_BIT -
> 1
> +};
> +
> +#define DEVLINK_FLASH_OVERWRITE_SETTINGS
> _BITUL(DEVLINK_FLASH_OVERWRITE_SETTINGS_BIT)
> +#define DEVLINK_FLASH_OVERWRITE_IDENTIFIERS
> _BITUL(DEVLINK_FLASH_OVERWRITE_IDENTIFIERS_BIT)
> +
> +#define DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS \
> + (_BITUL(__DEVLINK_FLASH_OVERWRITE_MAX_BIT) - 1)
> +
> +/**
> + * enum devlink_trap_action - Packet trap action.
> + * @DEVLINK_TRAP_ACTION_DROP: Packet is dropped by the device and a copy is
> not
> + * sent to the CPU.
> + * @DEVLINK_TRAP_ACTION_TRAP: The sole copy of the packet is sent to the CPU.
> + * @DEVLINK_TRAP_ACTION_MIRROR: Packet is forwarded by the device and a copy
> is
> + * sent to the CPU.
> + */
> +enum devlink_trap_action {
> + DEVLINK_TRAP_ACTION_DROP,
> + DEVLINK_TRAP_ACTION_TRAP,
> + DEVLINK_TRAP_ACTION_MIRROR,
> +};
> +
> +/**
> + * enum devlink_trap_type - Packet trap type.
> + * @DEVLINK_TRAP_TYPE_DROP: Trap reason is a drop. Trapped packets are only
> + * processed by devlink and not injected to the
> + * kernel's Rx path.
> + * @DEVLINK_TRAP_TYPE_EXCEPTION: Trap reason is an exception. Packet was not
> + * forwarded as intended due to an exception
> + * (e.g., missing neighbour entry) and trapped
> to
> + * control plane for resolution. Trapped
> packets
> + * are processed by devlink and injected to
> + * the kernel's Rx path.
> + * @DEVLINK_TRAP_TYPE_CONTROL: Packet was trapped because it is required for
> + * the correct functioning of the control plane.
> + * For example, an ARP request packet. Trapped
> + * packets are injected to the kernel's Rx path,
> + * but not reported to drop monitor.
> + */
> +enum devlink_trap_type {
> + DEVLINK_TRAP_TYPE_DROP,
> + DEVLINK_TRAP_TYPE_EXCEPTION,
> + DEVLINK_TRAP_TYPE_CONTROL,
> +};
> +
> +enum {
> + /* Trap can report input port as metadata */
> + DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT,
> + /* Trap can report flow action cookie as metadata */
> + DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE,
> +};
> +
> +enum devlink_reload_action {
> + DEVLINK_RELOAD_ACTION_UNSPEC,
> + DEVLINK_RELOAD_ACTION_DRIVER_REINIT, /* Driver entities
> re-instantiation */
> + DEVLINK_RELOAD_ACTION_FW_ACTIVATE, /* FW activate */
> +
> + /* Add new reload actions above */
> + __DEVLINK_RELOAD_ACTION_MAX,
> + DEVLINK_RELOAD_ACTION_MAX = __DEVLINK_RELOAD_ACTION_MAX - 1
> +};
> +
> +enum devlink_reload_limit {
> + DEVLINK_RELOAD_LIMIT_UNSPEC, /* unspecified, no constraints */
> + DEVLINK_RELOAD_LIMIT_NO_RESET, /* No reset allowed, no down time
> allowed,
> + * no link flap and no configuration
> is lost.
> + */
> +
> + /* Add new reload limit above */
> + __DEVLINK_RELOAD_LIMIT_MAX,
> + DEVLINK_RELOAD_LIMIT_MAX = __DEVLINK_RELOAD_LIMIT_MAX - 1
> +};
> +
> +#define DEVLINK_RELOAD_LIMITS_VALID_MASK (_BITUL(__DEVLINK_RELOAD_LIMIT_MAX)
> - 1)
> +
> +enum devlink_attr {
> + /* don't change the order or add anything between, this is ABI! */
> + DEVLINK_ATTR_UNSPEC,
> +
> + /* bus name + dev name together are a handle for devlink entity */
> + DEVLINK_ATTR_BUS_NAME, /* string */
> + DEVLINK_ATTR_DEV_NAME, /* string */
> +
> + DEVLINK_ATTR_PORT_INDEX, /* u32 */
> + DEVLINK_ATTR_PORT_TYPE, /* u16 */
> + DEVLINK_ATTR_PORT_DESIRED_TYPE, /* u16 */
> + DEVLINK_ATTR_PORT_NETDEV_IFINDEX, /* u32 */
> + DEVLINK_ATTR_PORT_NETDEV_NAME, /* string */
> + DEVLINK_ATTR_PORT_IBDEV_NAME, /* string */
> + DEVLINK_ATTR_PORT_SPLIT_COUNT, /* u32 */
> + DEVLINK_ATTR_PORT_SPLIT_GROUP, /* u32 */
> + DEVLINK_ATTR_SB_INDEX, /* u32 */
> + DEVLINK_ATTR_SB_SIZE, /* u32 */
> + DEVLINK_ATTR_SB_INGRESS_POOL_COUNT, /* u16 */
> + DEVLINK_ATTR_SB_EGRESS_POOL_COUNT, /* u16 */
> + DEVLINK_ATTR_SB_INGRESS_TC_COUNT, /* u16 */
> + DEVLINK_ATTR_SB_EGRESS_TC_COUNT, /* u16 */
> + DEVLINK_ATTR_SB_POOL_INDEX, /* u16 */
> + DEVLINK_ATTR_SB_POOL_TYPE, /* u8 */
> + DEVLINK_ATTR_SB_POOL_SIZE, /* u32 */
> + DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE, /* u8 */
> + DEVLINK_ATTR_SB_THRESHOLD, /* u32 */
> + DEVLINK_ATTR_SB_TC_INDEX, /* u16 */
> + DEVLINK_ATTR_SB_OCC_CUR, /* u32 */
> + DEVLINK_ATTR_SB_OCC_MAX, /* u32 */
> + DEVLINK_ATTR_ESWITCH_MODE, /* u16 */
> + DEVLINK_ATTR_ESWITCH_INLINE_MODE, /* u8 */
> +
> + DEVLINK_ATTR_DPIPE_TABLES, /* nested */
> + DEVLINK_ATTR_DPIPE_TABLE, /* nested */
> + DEVLINK_ATTR_DPIPE_TABLE_NAME, /* string */
> + DEVLINK_ATTR_DPIPE_TABLE_SIZE, /* u64 */
> + DEVLINK_ATTR_DPIPE_TABLE_MATCHES, /* nested */
> + DEVLINK_ATTR_DPIPE_TABLE_ACTIONS, /* nested */
> + DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, /* u8 */
> +
> + DEVLINK_ATTR_DPIPE_ENTRIES, /* nested */
> + DEVLINK_ATTR_DPIPE_ENTRY, /* nested */
> + DEVLINK_ATTR_DPIPE_ENTRY_INDEX, /* u64 */
> + DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES, /* nested */
> + DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES, /* nested */
> + DEVLINK_ATTR_DPIPE_ENTRY_COUNTER, /* u64 */
> +
> + DEVLINK_ATTR_DPIPE_MATCH, /* nested */
> + DEVLINK_ATTR_DPIPE_MATCH_VALUE, /* nested */
> + DEVLINK_ATTR_DPIPE_MATCH_TYPE, /* u32 */
> +
> + DEVLINK_ATTR_DPIPE_ACTION, /* nested */
> + DEVLINK_ATTR_DPIPE_ACTION_VALUE, /* nested */
> + DEVLINK_ATTR_DPIPE_ACTION_TYPE, /* u32 */
> +
> + DEVLINK_ATTR_DPIPE_VALUE,
> + DEVLINK_ATTR_DPIPE_VALUE_MASK,
> + DEVLINK_ATTR_DPIPE_VALUE_MAPPING, /* u32 */
> +
> + DEVLINK_ATTR_DPIPE_HEADERS, /* nested */
> + DEVLINK_ATTR_DPIPE_HEADER, /* nested */
> + DEVLINK_ATTR_DPIPE_HEADER_NAME, /* string */
> + DEVLINK_ATTR_DPIPE_HEADER_ID, /* u32 */
> + DEVLINK_ATTR_DPIPE_HEADER_FIELDS, /* nested */
> + DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, /* u8 */
> + DEVLINK_ATTR_DPIPE_HEADER_INDEX, /* u32 */
> +
> + DEVLINK_ATTR_DPIPE_FIELD, /* nested */
> + DEVLINK_ATTR_DPIPE_FIELD_NAME, /* string */
> + DEVLINK_ATTR_DPIPE_FIELD_ID, /* u32 */
> + DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, /* u32 */
> + DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, /* u32 */
> +
> + DEVLINK_ATTR_PAD,
> +
> + DEVLINK_ATTR_ESWITCH_ENCAP_MODE, /* u8 */
> + DEVLINK_ATTR_RESOURCE_LIST, /* nested */
> + DEVLINK_ATTR_RESOURCE, /* nested */
> + DEVLINK_ATTR_RESOURCE_NAME, /* string */
> + DEVLINK_ATTR_RESOURCE_ID, /* u64 */
> + DEVLINK_ATTR_RESOURCE_SIZE, /* u64 */
> + DEVLINK_ATTR_RESOURCE_SIZE_NEW, /* u64 */
> + DEVLINK_ATTR_RESOURCE_SIZE_VALID, /* u8 */
> + DEVLINK_ATTR_RESOURCE_SIZE_MIN, /* u64 */
> + DEVLINK_ATTR_RESOURCE_SIZE_MAX, /* u64 */
> + DEVLINK_ATTR_RESOURCE_SIZE_GRAN, /* u64 */
> + DEVLINK_ATTR_RESOURCE_UNIT, /* u8 */
> + DEVLINK_ATTR_RESOURCE_OCC, /* u64 */
> + DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID, /* u64 */
> + DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS,/* u64 */
> +
> + DEVLINK_ATTR_PORT_FLAVOUR, /* u16 */
> + DEVLINK_ATTR_PORT_NUMBER, /* u32 */
> + DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER, /* u32 */
> +
> + DEVLINK_ATTR_PARAM, /* nested */
> + DEVLINK_ATTR_PARAM_NAME, /* string */
> + DEVLINK_ATTR_PARAM_GENERIC, /* flag */
> + DEVLINK_ATTR_PARAM_TYPE, /* u8 */
> + DEVLINK_ATTR_PARAM_VALUES_LIST, /* nested */
> + DEVLINK_ATTR_PARAM_VALUE, /* nested */
> + DEVLINK_ATTR_PARAM_VALUE_DATA, /* dynamic */
> + DEVLINK_ATTR_PARAM_VALUE_CMODE, /* u8 */
> +
> + DEVLINK_ATTR_REGION_NAME, /* string */
> + DEVLINK_ATTR_REGION_SIZE, /* u64 */
> + DEVLINK_ATTR_REGION_SNAPSHOTS, /* nested */
> + DEVLINK_ATTR_REGION_SNAPSHOT, /* nested */
> + DEVLINK_ATTR_REGION_SNAPSHOT_ID, /* u32 */
> +
> + DEVLINK_ATTR_REGION_CHUNKS, /* nested */
> + DEVLINK_ATTR_REGION_CHUNK, /* nested */
> + DEVLINK_ATTR_REGION_CHUNK_DATA, /* binary */
> + DEVLINK_ATTR_REGION_CHUNK_ADDR, /* u64 */
> + DEVLINK_ATTR_REGION_CHUNK_LEN, /* u64 */
> +
> + DEVLINK_ATTR_INFO_DRIVER_NAME, /* string */
> + DEVLINK_ATTR_INFO_SERIAL_NUMBER, /* string */
> + DEVLINK_ATTR_INFO_VERSION_FIXED, /* nested */
> + DEVLINK_ATTR_INFO_VERSION_RUNNING, /* nested */
> + DEVLINK_ATTR_INFO_VERSION_STORED, /* nested */
> + DEVLINK_ATTR_INFO_VERSION_NAME, /* string */
> + DEVLINK_ATTR_INFO_VERSION_VALUE, /* string */
> +
> + DEVLINK_ATTR_SB_POOL_CELL_SIZE, /* u32 */
> +
> + DEVLINK_ATTR_FMSG, /* nested */
> + DEVLINK_ATTR_FMSG_OBJ_NEST_START, /* flag */
> + DEVLINK_ATTR_FMSG_PAIR_NEST_START, /* flag */
> + DEVLINK_ATTR_FMSG_ARR_NEST_START, /* flag */
> + DEVLINK_ATTR_FMSG_NEST_END, /* flag */
> + DEVLINK_ATTR_FMSG_OBJ_NAME, /* string */
> + DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE, /* u8 */
> + DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA, /* dynamic */
> +
> + DEVLINK_ATTR_HEALTH_REPORTER, /* nested */
> + DEVLINK_ATTR_HEALTH_REPORTER_NAME, /* string */
> + DEVLINK_ATTR_HEALTH_REPORTER_STATE, /* u8 */
> + DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT, /* u64 */
> + DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT, /* u64 */
> + DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS, /* u64 */
> + DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD, /* u64 */
> + DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER, /* u8 */
> +
> + DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME, /* string */
> + DEVLINK_ATTR_FLASH_UPDATE_COMPONENT, /* string */
> + DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG, /* string */
> + DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE, /* u64 */
> + DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL, /* u64 */
> +
> + DEVLINK_ATTR_PORT_PCI_PF_NUMBER, /* u16 */
> + DEVLINK_ATTR_PORT_PCI_VF_NUMBER, /* u16 */
> +
> + DEVLINK_ATTR_STATS, /* nested */
> +
> + DEVLINK_ATTR_TRAP_NAME, /* string */
> + /* enum devlink_trap_action */
> + DEVLINK_ATTR_TRAP_ACTION, /* u8 */
> + /* enum devlink_trap_type */
> + DEVLINK_ATTR_TRAP_TYPE, /* u8 */
> + DEVLINK_ATTR_TRAP_GENERIC, /* flag */
> + DEVLINK_ATTR_TRAP_METADATA, /* nested */
> + DEVLINK_ATTR_TRAP_GROUP_NAME, /* string */
> +
> + DEVLINK_ATTR_RELOAD_FAILED, /* u8 0 or 1 */
> +
> + DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS, /* u64 */
> +
> + DEVLINK_ATTR_NETNS_FD, /* u32 */
> + DEVLINK_ATTR_NETNS_PID, /* u32 */
> + DEVLINK_ATTR_NETNS_ID, /* u32 */
> +
> + DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP, /* u8 */
> +
> + DEVLINK_ATTR_TRAP_POLICER_ID, /* u32 */
> + DEVLINK_ATTR_TRAP_POLICER_RATE, /* u64 */
> + DEVLINK_ATTR_TRAP_POLICER_BURST, /* u64 */
> +
> + DEVLINK_ATTR_PORT_FUNCTION, /* nested */
> +
> + DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER, /* string */
> +
> + DEVLINK_ATTR_PORT_LANES, /* u32 */
> + DEVLINK_ATTR_PORT_SPLITTABLE, /* u8 */
> +
> + DEVLINK_ATTR_PORT_EXTERNAL, /* u8 */
> + DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, /* u32 */
> +
> + DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT, /* u64 */
> + DEVLINK_ATTR_FLASH_UPDATE_OVERWRITE_MASK, /* bitfield32 */
> +
> + DEVLINK_ATTR_RELOAD_ACTION, /* u8 */
> + DEVLINK_ATTR_RELOAD_ACTIONS_PERFORMED, /* bitfield32 */
> + DEVLINK_ATTR_RELOAD_LIMITS, /* bitfield32 */
> +
> + DEVLINK_ATTR_DEV_STATS, /* nested */
> + DEVLINK_ATTR_RELOAD_STATS, /* nested */
> + DEVLINK_ATTR_RELOAD_STATS_ENTRY, /* nested */
> + DEVLINK_ATTR_RELOAD_STATS_LIMIT, /* u8 */
> + DEVLINK_ATTR_RELOAD_STATS_VALUE, /* u32 */
> + DEVLINK_ATTR_REMOTE_RELOAD_STATS, /* nested */
> + DEVLINK_ATTR_RELOAD_ACTION_INFO, /* nested */
> + DEVLINK_ATTR_RELOAD_ACTION_STATS, /* nested */
> +
> + DEVLINK_ATTR_PORT_PCI_SF_NUMBER, /* u32 */
> + /* add new attributes above here, update the policy in devlink.c */
> +
> + __DEVLINK_ATTR_MAX,
> + DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1
> +};
> +
> +/* Mapping between internal resource described by the field and system
> + * structure
> + */
> +enum devlink_dpipe_field_mapping_type {
> + DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE,
> + DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX,
> +};
> +
> +/* Match type - specify the type of the match */
> +enum devlink_dpipe_match_type {
> + DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT,
> +};
> +
> +/* Action type - specify the action type */
> +enum devlink_dpipe_action_type {
> + DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY,
> +};
> +
> +enum devlink_dpipe_field_ethernet_id {
> + DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC,
> +};
> +
> +enum devlink_dpipe_field_ipv4_id {
> + DEVLINK_DPIPE_FIELD_IPV4_DST_IP,
> +};
> +
> +enum devlink_dpipe_field_ipv6_id {
> + DEVLINK_DPIPE_FIELD_IPV6_DST_IP,
> +};
> +
> +enum devlink_dpipe_header_id {
> + DEVLINK_DPIPE_HEADER_ETHERNET,
> + DEVLINK_DPIPE_HEADER_IPV4,
> + DEVLINK_DPIPE_HEADER_IPV6,
> +};
> +
> +enum devlink_resource_unit {
> + DEVLINK_RESOURCE_UNIT_ENTRY,
> +};
> +
> +enum devlink_port_function_attr {
> + DEVLINK_PORT_FUNCTION_ATTR_UNSPEC,
> + DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, /* binary */
> + DEVLINK_PORT_FN_ATTR_STATE, /* u8 */
> + DEVLINK_PORT_FN_ATTR_OPSTATE, /* u8 */
> +
> + __DEVLINK_PORT_FUNCTION_ATTR_MAX,
> + DEVLINK_PORT_FUNCTION_ATTR_MAX = __DEVLINK_PORT_FUNCTION_ATTR_MAX - 1
> +};
> +
> +enum devlink_port_fn_state {
> + DEVLINK_PORT_FN_STATE_INACTIVE,
> + DEVLINK_PORT_FN_STATE_ACTIVE,
> +};
> +
> +/**
> + * enum devlink_port_fn_opstate - indicates operational state of the function
> + * @DEVLINK_PORT_FN_OPSTATE_ATTACHED: Driver is attached to the function.
> + * For graceful tear down of the function, after inactivation of the
> + * function, user should wait for operational state to turn DETACHED.
> + * @DEVLINK_PORT_FN_OPSTATE_DETACHED: Driver is detached from the function.
> + * It is safe to delete the port.
> + */
> +enum devlink_port_fn_opstate {
> + DEVLINK_PORT_FN_OPSTATE_DETACHED,
> + DEVLINK_PORT_FN_OPSTATE_ATTACHED,
> +};
> +
> +#endif /* _UAPI_LINUX_DEVLINK_H_ */
> +#endif /* __UAPI_LINUX_DEVLINK_WRAPPER_H */
> +#endif /* !__KERNEL__ */
> diff --git a/lib/automake.mk b/lib/automake.mk
> index 086fbd62d..1057504d1 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -43,6 +43,17 @@ lib_libovn_la_SOURCES = \
> lib/plug.c \
> lib/plug-dummy.h \
> lib/plug-dummy.c
> +
> +# in-tree plug providers
> +if ENABLE_PLUG
> +if LINUX
> +lib_libovn_la_SOURCES += \
> + lib/plug_providers/representor/netlink-devlink.h \
> + lib/plug_providers/representor/netlink-devlink.c \
> + lib/plug_providers/representor/plug-representor.c
> +endif
> +endif
> +
> nodist_lib_libovn_la_SOURCES = \
> lib/ovn-dirs.c \
> lib/ovn-nb-idl.c \
> diff --git a/lib/plug-provider.h b/lib/plug-provider.h
> index 487534ee5..6587be8dc 100644
> --- a/lib/plug-provider.h
> +++ b/lib/plug-provider.h
> @@ -87,9 +87,13 @@ struct plug_class {
> };
>
> extern const struct plug_class plug_dummy_class;
> +#ifdef ENABLE_PLUG
> +/* in-tree plug classes */
> +extern const struct plug_class plug_representor;
> +#endif /* ENABLE_PLUG */
> #ifdef HAVE_PLUG_PROVIDER
> extern const struct plug_class *plug_provider_classes[];
> -#endif
> +#endif /* HAVE_PLUG_PROVIDER */
>
> #ifdef __cplusplus
> }
> diff --git a/lib/plug.c b/lib/plug.c
> index c0c34214e..dab06713a 100644
> --- a/lib/plug.c
> +++ b/lib/plug.c
> @@ -32,6 +32,7 @@ VLOG_DEFINE_THIS_MODULE(plug);
>
> #ifdef ENABLE_PLUG
> static const struct plug_class *base_plug_classes[] = {
> + &plug_representor,
> };
> #endif
>
> diff --git a/lib/plug_providers/representor/netlink-devlink.c
> b/lib/plug_providers/representor/netlink-devlink.c
> new file mode 100644
> index 000000000..82e9c71f3
> --- /dev/null
> +++ b/lib/plug_providers/representor/netlink-devlink.c
> @@ -0,0 +1,499 @@
> +/*
> + * Copyright (c) 2021 Canonical
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +#include <config.h>
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <linux/devlink.h>
> +#include <linux/genetlink.h>
> +#include "netlink.h"
> +#include "netlink-socket.h"
> +#include "netlink-devlink.h"
> +#include "openvswitch/vlog.h"
> +#include "packets.h"
> +
> +VLOG_DEFINE_THIS_MODULE(netlink_devlink);
> +
> +/* Initialized by nl_devlink_init() */
> +static int ovs_devlink_family;
> +
> +struct nl_dl_dump_state {
> + struct nl_dump dump;
> + struct ofpbuf buf;
> + int error;
> +};
> +
> +static int nl_devlink_init(void);
> +
> +const char *dl_str_not_present = "";
> +
> +/* Allocates memory for and returns a pointer to devlink dump state object.
> + *
> + * One-time initialization and lookup of the devlink generic netlink family
> is
> + * also performed, and the caller should check for error condition with a
> call
> + * to nl_dl_dump_init_error before attempting to dump devlink data.
> + *
> + * The caller owns the allocated object and is responsible for freeing the
> + * allocated memory with a call to nl_dl_dump_destroy when done. */
> +struct nl_dl_dump_state *
> +nl_dl_dump_init(void)
> +{
> + struct nl_dl_dump_state *dump_state;
> +
> + dump_state = xmalloc(sizeof(*dump_state));
> + dump_state->error = nl_devlink_init();
> + return dump_state;
> +}
> +
> +/* Get error indicator from the devlink initialization process. */
> +int
> +nl_dl_dump_init_error(struct nl_dl_dump_state *dump_state)
> +{
> + return dump_state->error;
> +}
> +
> +/* Free memory previously allocated by call to nl_dl_dump_init.
> + *
> + * Note that the caller is responsible for making a call to nl_dl_dump_finish
> + * to free up resources associated with any in-flight dump process prior to
> + * destroying the dump state object. */
> +void
> +nl_dl_dump_destroy(struct nl_dl_dump_state *dump_state)
> +{
> + free(dump_state);
> +}
> +
> +void
> +nl_msg_put_dlgenmsg(struct ofpbuf *msg, size_t expected_payload,
> + int family, uint8_t cmd, uint32_t flags)
> +{
> + nl_msg_put_genlmsghdr(msg, expected_payload, family,
> + flags, cmd, DEVLINK_GENL_VERSION);
> +}
> +
> +/* Starts a Netlink-devlink "dump" operation, by sending devlink request with
> + * command 'cmd' to the kernel on a Netlink socket, and initializes 'state'
> + * with buffer and dump state. */
> +void
> +nl_dl_dump_start(uint8_t cmd, struct nl_dl_dump_state *state)
> +{
> + struct ofpbuf *request;
> +
> + request = ofpbuf_new(NLMSG_HDRLEN + GENL_HDRLEN);
> + nl_msg_put_dlgenmsg(request, 0, ovs_devlink_family, cmd,
> + NLM_F_REQUEST);
> + nl_dump_start(&state->dump, NETLINK_GENERIC, request);
> + ofpbuf_delete(request);
> +
> + ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE);
> +}
> +
> +static bool
> +nl_dl_dump_next__(struct nl_dl_dump_state *state,
> + bool (*parse_function)(struct ofpbuf *, void *),
> + void *entry)
> +{
> + struct ofpbuf msg;
> +
> + if (!nl_dump_next(&state->dump, &msg, &state->buf)) {
> + return false;
> + }
> + if (!parse_function(&msg, entry)) {
> + ovs_mutex_lock(&state->dump.mutex);
> + state->dump.status = EPROTO;
> + ovs_mutex_unlock(&state->dump.mutex);
> + return false;
> + }
> + return true;
> +}
> +
> +/* Attempts to retrieve and parse another reply in on-going dump operation.
> + *
> + * If successful, returns true and assignes values or pointers to data in
> + * 'port_entry'. The caller must not modify 'port_entry' (because it may
> + * contain pointers to data within the buffer which will be used by future
> + * calls to this function.
> + *
> + * On failure, returns false. Failure might indicate an actual error or
> merely
> + * the end of replies. An error status for the entire dump operation is
> + * provided when it is completed by calling nl_dl_dump_finish()
> + */
> +bool
> +nl_dl_port_dump_next(struct nl_dl_dump_state *state,
> + struct dl_port *port_entry)
> +{
> + return nl_dl_dump_next__(
> + state,
> + (bool ( * )(struct ofpbuf *, void *)) &nl_dl_parse_port_policy,
> + (void *) port_entry);
> +}
> +
> +bool
> +nl_dl_info_dump_next(struct nl_dl_dump_state *state,
> + struct dl_info *info_entry)
> +{
> + return nl_dl_dump_next__(
> + state,
> + (bool ( * )(struct ofpbuf *, void *)) &nl_dl_parse_info_policy,
> + (void *) info_entry);
> +}
> +
> +int
> +nl_dl_dump_finish(struct nl_dl_dump_state *state)
> +{
> + ofpbuf_uninit(&state->buf);
> + return nl_dump_done(&state->dump);
> +}
> +
> +static uint64_t
> +attr_get_up_to_u64(size_t attr_idx, struct nlattr *attrs[],
> + const struct nl_policy policy[],
> + size_t policy_len)
> +{
> + if (attr_idx < policy_len && attrs[attr_idx]) {
> + switch (policy[attr_idx].type) {
> + case NL_A_U8:
> + return nl_attr_get_u8(attrs[attr_idx]);
> + break;
> + case NL_A_U16:
> + return nl_attr_get_u16(attrs[attr_idx]);
> + break;
> + case NL_A_U32:
> + return nl_attr_get_u32(attrs[attr_idx]);
> + break;
> + case NL_A_U64:
> + return nl_attr_get_u64(attrs[attr_idx]);
> + break;
> + case NL_A_U128:
> + case NL_A_STRING:
> + case NL_A_NO_ATTR:
> + case NL_A_UNSPEC:
> + case NL_A_FLAG:
> + case NL_A_IPV6:
> + case NL_A_NESTED:
> + case NL_A_LL_ADDR:
> + case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED();
> + };
> + }
> + return -1;
> +}
> +
> +static const char *
> +attr_get_str(size_t attr_idx, struct nlattr *attrs[],
> + const struct nl_policy policy[],
> + size_t policy_len)
> +{
> + if (attr_idx < policy_len && attrs[attr_idx]) {
> + ovs_assert(policy[attr_idx].type == NL_A_STRING);
> + return nl_attr_get_string(attrs[attr_idx]);
> + }
> + return dl_str_not_present;
> +}
> +
> +bool
> +nl_dl_parse_port_function(struct nlattr *nla, struct dl_port_function
> *port_fn)
> +{
> + static const struct nl_policy policy[] = {
> + /* Appeared in Linux v5.9 */
> + [DEVLINK_PORT_FUNCTION_ATTR_UNSPEC] = { .type = NL_A_UNSPEC,
> + .optional = true, },
> + [DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NL_A_LL_ADDR,
> + .optional = true, },
> +
> + /* Appeared in Linnux v5.12 */
> + [DEVLINK_PORT_FN_ATTR_STATE] = { .type = NL_A_U8, .optional = true,
> },
> + [DEVLINK_PORT_FN_ATTR_OPSTATE] = { .type = NL_A_U8,
> + .optional = true, },
> + };
> + struct nlattr *attrs[ARRAY_SIZE(policy)];
> + bool parsed;
> +
> + parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
> +
> + if (parsed) {
> + if (attrs[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]) {
> + size_t hw_addr_size = nl_attr_get_size(
> + attrs[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]);
> + if (hw_addr_size == sizeof(struct eth_addr)) {
> + port_fn->eth_addr = nl_attr_get_eth_addr(
> + attrs[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]);
> + } else if (hw_addr_size == sizeof(struct ib_addr)) {
> + port_fn->ib_addr = nl_attr_get_ib_addr(
> + attrs[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]);
> + } else {
> + return false;
> + }
> + } else {
> + memset(&port_fn->eth_addr, 0, sizeof(port_fn->eth_addr));
> + memset(&port_fn->ib_addr, 0, sizeof(port_fn->ib_addr));
> + }
> + port_fn->state = attr_get_up_to_u64(
> + DEVLINK_PORT_FN_ATTR_STATE,
> + attrs, policy, ARRAY_SIZE(policy));
> + port_fn->opstate = attr_get_up_to_u64(
> + DEVLINK_PORT_FN_ATTR_OPSTATE,
> + attrs, policy, ARRAY_SIZE(policy));
> + }
> +
> + return parsed;
> +}
> +
> +bool
> +nl_dl_parse_port_policy(struct ofpbuf *msg, struct dl_port *port)
> +{
> + static const struct nl_policy policy[] = {
> + /* Appeared in Linux v4.6 */
> + [DEVLINK_ATTR_BUS_NAME] = { .type = NL_A_STRING, .optional = false,
> },
> + [DEVLINK_ATTR_DEV_NAME] = { .type = NL_A_STRING, .optional = false,
> },
> + [DEVLINK_ATTR_PORT_INDEX] = { .type = NL_A_U32, .optional = false, },
> +
> + [DEVLINK_ATTR_PORT_TYPE] = { .type = NL_A_U16, .optional = true, },
> + [DEVLINK_ATTR_PORT_DESIRED_TYPE] = { .type = NL_A_U16,
> + .optional = true, },
> + [DEVLINK_ATTR_PORT_NETDEV_IFINDEX] = { .type = NL_A_U32,
> + .optional = true, },
> + [DEVLINK_ATTR_PORT_NETDEV_NAME] = { .type = NL_A_STRING,
> + .optional = true, },
> + [DEVLINK_ATTR_PORT_IBDEV_NAME] = { .type = NL_A_STRING,
> + .optional = true, },
> + [DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NL_A_U32,
> + .optional = true, },
> + [DEVLINK_ATTR_PORT_SPLIT_GROUP] = { .type = NL_A_U32,
> + .optional = true, },
> +
> + /* Appeared in Linux v4.18 */
> + [DEVLINK_ATTR_PORT_FLAVOUR] = { .type = NL_A_U16, .optional = true,
> },
> + [DEVLINK_ATTR_PORT_NUMBER] = { .type = NL_A_U32, .optional = true, },
> + [DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER] = { .type = NL_A_U32,
> + .optional = true, },
> +
> + /* Appeared in Linux v5.3 */
> + [DEVLINK_ATTR_PORT_PCI_PF_NUMBER] = { .type = NL_A_U16,
> + .optional = true, },
> + [DEVLINK_ATTR_PORT_PCI_VF_NUMBER] = { .type = NL_A_U16,
> + .optional = true, },
> +
> + /* Appeared in Linux v5.9 */
> + [DEVLINK_ATTR_PORT_FUNCTION] = { .type = NL_A_NESTED,
> + .optional = true, },
> + [DEVLINK_ATTR_PORT_LANES] = { .type = NL_A_U32, .optional = true, },
> + [DEVLINK_ATTR_PORT_SPLITTABLE] = { .type = NL_A_U8,
> + .optional = true, },
> +
> + /* Appeared in Linux v5.10 */
> + [DEVLINK_ATTR_PORT_EXTERNAL] = { .type = NL_A_U8, .optional = true },
> + [DEVLINK_ATTR_PORT_CONTROLLER_NUMBER] = { .type = NL_A_U32,
> + .optional = true},
> +
> + /* Appeared in Linux v5.12 */
> + [DEVLINK_ATTR_PORT_PCI_SF_NUMBER] = { .type = NL_A_U32,
> + .optional = true },
> + };
> + struct nlattr *attrs[ARRAY_SIZE(policy)];
> +
> + if (!nl_policy_parse(msg, NLMSG_HDRLEN + GENL_HDRLEN,
> + policy, attrs,
> + ARRAY_SIZE(policy)))
> + {
> + return false;
> + }
> + port->bus_name = nl_attr_get_string(attrs[DEVLINK_ATTR_BUS_NAME]);
> + port->dev_name = nl_attr_get_string(attrs[DEVLINK_ATTR_DEV_NAME]);
> + port->index = nl_attr_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
> +
> + port->type = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_TYPE,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->desired_type = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_DESIRED_TYPE,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->netdev_ifindex = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_NETDEV_IFINDEX,
> + attrs, policy, ARRAY_SIZE(policy));
> + if (port->type == DEVLINK_PORT_TYPE_ETH &&
> + attrs[DEVLINK_ATTR_PORT_NETDEV_NAME]) {
> + port->netdev_name = nl_attr_get_string(
> + attrs[DEVLINK_ATTR_PORT_NETDEV_NAME]);
> + } else if (port->type == DEVLINK_PORT_TYPE_IB &&
> + attrs[DEVLINK_ATTR_PORT_IBDEV_NAME]) {
> + port->ibdev_name = nl_attr_get_string(
> + attrs[DEVLINK_ATTR_PORT_IBDEV_NAME]);
> + } else {
> + port->netdev_name = dl_str_not_present;
> + }
> + port->split_count = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_SPLIT_COUNT,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->split_group = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_SPLIT_GROUP,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->flavour = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_FLAVOUR,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->number = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_NUMBER,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->split_subport_number = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->pci_pf_number = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_PCI_PF_NUMBER,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->pci_vf_number = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_PCI_VF_NUMBER,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->lanes = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_LANES,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->splittable = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_SPLITTABLE,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->external = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_EXTERNAL,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->controller_number = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_CONTROLLER_NUMBER,
> + attrs, policy, ARRAY_SIZE(policy));
> + port->pci_sf_number = attr_get_up_to_u64(
> + DEVLINK_ATTR_PORT_PCI_SF_NUMBER,
> + attrs, policy, ARRAY_SIZE(policy));
> +
> + if (attrs[DEVLINK_ATTR_PORT_FUNCTION]) {
> + if (!nl_dl_parse_port_function(attrs[DEVLINK_ATTR_PORT_FUNCTION],
> + &port->function))
> + {
> + return false;
> + }
> + } else {
> + memset(&port->function, 0, sizeof(port->function));
> + port->function.state = UINT8_MAX;
> + port->function.opstate = UINT8_MAX;
> + }
> +
> + return true;
> +}
> +
> +bool
> +nl_dl_parse_info_version(struct nlattr *nla, struct dl_info_version
> *info_ver)
> +{
> + static const struct nl_policy policy[] = {
> + /* Appeared in Linux v5.1 */
> + [DEVLINK_ATTR_INFO_VERSION_NAME] = { .type = NL_A_STRING,
> + .optional = true, },
> + [DEVLINK_ATTR_INFO_VERSION_VALUE] = { .type = NL_A_STRING,
> + .optional = true, },
> + };
> + struct nlattr *attrs[ARRAY_SIZE(policy)];
> + bool parsed;
> +
> + parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
> +
> + if (parsed) {
> + info_ver->name = attr_get_str(
> + DEVLINK_ATTR_INFO_VERSION_NAME,
> + attrs, policy, ARRAY_SIZE(policy));
> + info_ver->value = attr_get_str(
> + DEVLINK_ATTR_INFO_VERSION_NAME,
> + attrs, policy, ARRAY_SIZE(policy));
> + }
> +
> + return parsed;
> +}
> +
> +static bool
> +attr_fill_version(size_t attr_idx, struct nlattr *attrs[],
> + size_t attrs_len,
> + struct dl_info_version *version)
> +{
> + if (attr_idx < attrs_len && attrs[attr_idx]) {
> + if (!nl_dl_parse_info_version(attrs[attr_idx],
> + version))
> + {
> + return false;
> + }
> + } else {
> + version->name = dl_str_not_present;
> + version->value = dl_str_not_present;
> + }
> + return true;
> +}
> +
> +bool
> +nl_dl_parse_info_policy(struct ofpbuf *msg, struct dl_info *info)
> +{
> + static const struct nl_policy policy[] = {
> + /* Appeared in Linux v5.1 */
> + [DEVLINK_ATTR_INFO_DRIVER_NAME] = { .type = NL_A_STRING,
> + .optional = false, },
> + [DEVLINK_ATTR_INFO_SERIAL_NUMBER] = { .type = NL_A_STRING,
> + .optional = true, },
> + [DEVLINK_ATTR_INFO_VERSION_FIXED] = { .type = NL_A_NESTED,
> + .optional = true, },
> + [DEVLINK_ATTR_INFO_VERSION_RUNNING] = { .type = NL_A_NESTED,
> + .optional = true, },
> + [DEVLINK_ATTR_INFO_VERSION_STORED] = { .type = NL_A_NESTED,
> + .optional = true, },
> +
> + /* Appeared in Linux v5.9 */
> + [DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER] = { .type = NL_A_STRING,
> + .optional = true, },
> + };
> + struct nlattr *attrs[ARRAY_SIZE(policy)];
> +
> + if (!nl_policy_parse(msg, NLMSG_HDRLEN + GENL_HDRLEN,
> + policy, attrs,
> + ARRAY_SIZE(policy)))
> + {
> + return false;
> + }
> + info->driver_name = attr_get_str(
> + DEVLINK_ATTR_INFO_DRIVER_NAME,
> + attrs, policy, ARRAY_SIZE(policy));
> + info->serial_number = attr_get_str(
> + DEVLINK_ATTR_INFO_SERIAL_NUMBER,
> + attrs, policy, ARRAY_SIZE(policy));
> + info->board_serial_number = attr_get_str(
> + DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER,
> + attrs, policy, ARRAY_SIZE(policy));
> + if (!attr_fill_version(DEVLINK_ATTR_INFO_VERSION_FIXED, attrs,
> + ARRAY_SIZE(policy), &info->version_fixed)
> + || !attr_fill_version(DEVLINK_ATTR_INFO_VERSION_RUNNING, attrs,
> + ARRAY_SIZE(policy), &info->version_running)
> + || !attr_fill_version(DEVLINK_ATTR_INFO_VERSION_STORED, attrs,
> + ARRAY_SIZE(policy), &info->version_stored))
> + {
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static int
> +nl_devlink_init(void)
> +{
> + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
> + static int error;
> +
> + if (ovsthread_once_start(&once)) {
> + error = nl_lookup_genl_family(DEVLINK_GENL_NAME,
> &ovs_devlink_family);
> + if (error) {
> + VLOG_INFO("Generic Netlink family '%s' does not exist. "
> + "Linux version 4.6 or newer required.",
> + DEVLINK_GENL_NAME);
> + }
> + ovsthread_once_done(&once);
> + }
> + return error;
> +}
> diff --git a/lib/plug_providers/representor/netlink-devlink.h
> b/lib/plug_providers/representor/netlink-devlink.h
> new file mode 100644
> index 000000000..a7b108435
> --- /dev/null
> +++ b/lib/plug_providers/representor/netlink-devlink.h
> @@ -0,0 +1,115 @@
> +/*
> + * Copyright (c) 2021 Canonical
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef NETLINK_DEVLINK_H
> +#define NETLINK_DEVLINK_H 1
> +
> +/* Presence of each individual value in these structs is determined at
> runtime
> + * and depends on which kernel version we are communicating with as well as
> + * which driver implementation is filling in the information for each
> + * individual device or port.
> + *
> + * To signal non-presence of values the library follows the following
> + * convention:
> + *
> + * - integer type values will be set to their maximum value
> + * (e.g. UNIT8_MAX for a unit8_t)
> + *
> + * - hardware address type values will be set to all zero
> + *
> + * - string type values will be set to a pointer to dl_str_not_present
> + * (an empty string).
> + */
> +
> +extern const char *dl_str_not_present;
> +
> +struct dl_port_function {
> + struct eth_addr eth_addr;
> + struct ib_addr ib_addr;
> + uint8_t state;
> + uint8_t opstate;
> +};
> +
> +struct dl_port {
> + const char *bus_name;
> + const char *dev_name;
> + uint32_t index;
> + uint16_t type;
> + uint16_t desired_type;
> + uint32_t netdev_ifindex;
> + union {
> + const char *netdev_name; /* type DEVLINK_PORT_TYPE_ETH */
> + const char *ibdev_name; /* type DEVLINK_PORT_TYPE_IB */
> + };
> + uint32_t split_count;
> + uint32_t split_group;
> + uint16_t flavour;
> + uint32_t number;
> + uint32_t split_subport_number;
> + uint16_t pci_pf_number;
> + uint16_t pci_vf_number;
> + struct dl_port_function function;
> + uint32_t lanes;
> + uint8_t splittable;
> + uint8_t external;
> + uint32_t controller_number;
> + uint32_t pci_sf_number;
> +};
> +
> +struct dl_info_version {
> + const char *name;
> + const char *value;
> +};
> +
> +struct dl_info {
> + const char *driver_name;
> + const char *serial_number;
> + const char *board_serial_number;
> + struct dl_info_version version_fixed;
> + struct dl_info_version version_running;
> + struct dl_info_version version_stored;
> +};
> +
> +struct eth_addr nl_attr_get_eth_addr(const struct nlattr *nla);
> +struct ib_addr nl_attr_get_ib_addr(const struct nlattr *nla);
> +
> +/* The nl_dl_dump_state record declaration refers to types declared in
> + * netlink-socket.h, which requires OVS internal autoconf macros and
> + * definitions to be present for successful compilation.
> + *
> + * To enable friction free consumtion of these interfaces from programs
> + * external to Open vSwitch, such as OVN, we keep the declaration of
> + * nl_dl_dump_state private.
> + *
> + * Use the nl_dl_dump_init function to allocate memory for and get a pointer
> to
> + * a devlink dump state object. The caller owns the allocated object and is
> + * responsible for freeing the allocated memory when done. */
> +struct nl_dl_dump_state;
> +
> +struct nl_dl_dump_state * nl_dl_dump_init(void);
> +int nl_dl_dump_init_error(struct nl_dl_dump_state *);
> +void nl_dl_dump_destroy(struct nl_dl_dump_state *);
> +void nl_msg_put_dlgenmsg(struct ofpbuf *, size_t, int, uint8_t, uint32_t);
> +void nl_dl_dump_start(uint8_t, struct nl_dl_dump_state *);
> +bool nl_dl_port_dump_next(struct nl_dl_dump_state *, struct dl_port *);
> +bool nl_dl_info_dump_next(struct nl_dl_dump_state *, struct dl_info *);
> +int nl_dl_dump_finish(struct nl_dl_dump_state *);
> +bool nl_dl_parse_port_policy(struct ofpbuf *, struct dl_port *);
> +bool nl_dl_parse_port_function(struct nlattr *, struct dl_port_function *);
> +bool nl_dl_parse_info_policy(struct ofpbuf *, struct dl_info *);
> +bool nl_dl_parse_info_version(struct nlattr *, struct dl_info_version *);
> +
> +#endif /* NETLINK_DEVLINK_H */
> diff --git a/lib/plug_providers/representor/plug-representor.c
> b/lib/plug_providers/representor/plug-representor.c
> new file mode 100644
> index 000000000..4c4187114
> --- /dev/null
> +++ b/lib/plug_providers/representor/plug-representor.c
> @@ -0,0 +1,307 @@
> +/* Copyright (c) 2021 Canonical
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <linux/devlink.h>
> +#include <net/if.h>
> +
> +#include "plug-provider.h"
> +#include "plug.h"
> +
> +#include "hash.h"
> +#include "lib/vswitch-idl.h"
> +#include "openvswitch/hmap.h"
> +#include "openvswitch/vlog.h"
> +#include "lib/ovn-sb-idl.h"
> +#include "netlink-devlink.h"
> +#include "openvswitch/shash.h"
> +#include "packets.h"
> +
> +VLOG_DEFINE_THIS_MODULE(plug_representor);
> +
> +/* Contains netdev name of ports known to devlink indexed by PF MAC
> + * address and logical function number (if applicable).
> + *
> + * Examples:
> + * SR-IOV Physical Function: key "00:53:00:00:00:42" value "pf0hpf"
> + * SR-IOV Virtual Function: key "00:53:00:00:00:42-42" value "pf0vf42"
> + */
> +static struct shash devlink_ports;
> +
> +/* Max number of physical ports connected to a single NIC SoC. */
> +#define MAX_NIC_PHY_PORTS 64
> +/* string repr of eth MAC, '-', logical function number (uint32_t) */
> +#define MAX_KEY_LEN 17+1+10+1
> +
> +static bool compat_get_host_pf_mac(const char *, struct eth_addr *);
> +
> +static bool
> +fill_devlink_ports_key_from_strs(char *buf, size_t bufsiz,
> + const char *host_pf_mac,
> + const char *function)
> +{
> + return snprintf(buf, bufsiz,
> + function != NULL ? "%s-%s": "%s",
> + host_pf_mac, function) < bufsiz;
> +}
> +
> +/* We deliberately pass the struct eth_addr by value as we would have to copy
> + * the data either way to make use of the ETH_ADDR_ARGS macro */
> +static bool
> +fill_devlink_ports_key_from_typed(char *buf, size_t bufsiz,
> + struct eth_addr host_pf_mac,
> + uint32_t function)
> +{
> + return snprintf(
> + buf, bufsiz,
> + function < UINT32_MAX ? ETH_ADDR_FMT"-%"PRIu32 : ETH_ADDR_FMT,
> + ETH_ADDR_ARGS(host_pf_mac), function) < bufsiz;
> +}
> +
> +static void
> +devlink_port_add_function(struct dl_port *port_entry,
> + struct eth_addr *host_pf_mac)
> +{
> + char keybuf[MAX_KEY_LEN];
> + uint32_t function_number;
> +
> + switch (port_entry->flavour) {
> + case DEVLINK_PORT_FLAVOUR_PCI_PF:
> + /* for Physical Function representor ports we only add the MAC
> address
> + * and no logical function number */
> + function_number = -1;
> + break;
> + case DEVLINK_PORT_FLAVOUR_PCI_VF:
> + function_number = port_entry->pci_vf_number;
> + break;
> + default:
> + VLOG_WARN("Unsupported flavour for port '%s': %s",
> + port_entry->netdev_name,
> + port_entry->flavour == DEVLINK_PORT_FLAVOUR_PHYSICAL ?
> "PHYSICAL" :
> + port_entry->flavour == DEVLINK_PORT_FLAVOUR_CPU ? "CPU" :
> + port_entry->flavour == DEVLINK_PORT_FLAVOUR_DSA ? "DSA" :
> + port_entry->flavour == DEVLINK_PORT_FLAVOUR_PCI_PF ? "PCI_PF":
> + port_entry->flavour == DEVLINK_PORT_FLAVOUR_PCI_VF ? "PCI_VF":
> + port_entry->flavour == DEVLINK_PORT_FLAVOUR_VIRTUAL ? "VIRTUAL":
> + port_entry->flavour == DEVLINK_PORT_FLAVOUR_UNUSED ? "UNUSED":
> + port_entry->flavour == DEVLINK_PORT_FLAVOUR_PCI_SF ? "PCI_SF":
> + "UNKNOWN");
> + return;
> + };
> + /* Failure to fill key from typed values means calculation of the max key
> + * length is wrong, i.e. a bug. */
> + ovs_assert(fill_devlink_ports_key_from_typed(
> + keybuf, sizeof(keybuf),
> + *host_pf_mac, function_number));
> + shash_add(&devlink_ports, keybuf, xstrdup(port_entry->netdev_name));
> +}
> +
> +
> +static int
> +plug_representor_init(void)
> +{
> + struct nl_dl_dump_state *port_dump;
> + struct dl_port port_entry;
> + int error;
> + struct eth_addr host_pf_macs[MAX_NIC_PHY_PORTS];
> +
> + shash_init(&devlink_ports);
> +
> + port_dump = nl_dl_dump_init();
> + if ((error = nl_dl_dump_init_error(port_dump))) {
> + VLOG_WARN(
> + "unable to start dump of ports from devlink-port interface");
> + return error;
> + }
> + /* The core devlink infrastructure in the kernel keeps a linked list of
> + * the devices and each of those has a linked list of ports. These are
> + * populated by each device driver as devices are enumerated, and as such
> + * we can rely on ports being dumped in a consistent order on a device
> + * by device basis with logical numbering for each port flavour starting
> + * on 0 for each new device.
> + */
> + nl_dl_dump_start(DEVLINK_CMD_PORT_GET, port_dump);
> + while (nl_dl_port_dump_next(port_dump, &port_entry)) {
> + switch (port_entry.flavour) {
> + case DEVLINK_PORT_FLAVOUR_PHYSICAL:
> + /* The PHYSICAL flavoured port represent a network facing port on
> + * the NIC.
> + *
> + * For kernel versions where the devlink-port infrastructure does
> + * not provide MAC address for PCI_PF flavoured ports, there
> exist
> + * a interface in sysfs which is relative to the name of the
> + * PHYSICAL port netdev name.
> + *
> + * Since we at this point in the dump do not know if the MAC will
> + * be provided for the PCI_PF or not, proactively store the MAC
> + * address by looking up through the sysfs interface.
> + *
> + * If MAC address is available once we get to the PCI_PF we will
> + * overwrite the stored value.
> + */
> + if (port_entry.number > MAX_NIC_PHY_PORTS) {
> + VLOG_WARN("physical port number out of range for port '%s': "
> + "%"PRIu32,
> + port_entry.netdev_name, port_entry.number);
> + continue;
> + }
> + compat_get_host_pf_mac(port_entry.netdev_name,
> + &host_pf_macs[port_entry.number]);
> + break;
> + case DEVLINK_PORT_FLAVOUR_PCI_PF: /* FALL THROUGH */
> + /* The PCI_PF flavoured port represent a host facing port.
> + *
> + * For function flavours other than PHYSICAL pci_pf_number will
> be
> + * set to the logical number of which physical port the function
> + * belongs.
> + */
> + if (!eth_addr_is_zero(port_entry.function.eth_addr)) {
> + host_pf_macs[port_entry.pci_pf_number] =
> + port_entry.function.eth_addr;
> + }
> + /* FALL THROUGH */
> + case DEVLINK_PORT_FLAVOUR_PCI_VF:
> + /* The PCI_VF flavoured port represent a host facing
> + * PCI Virtual Function.
> + *
> + * For function flavours other than PHYSICAL pci_pf_number will
> be
> + * set to the logical number of which physical port the function
> + * belongs.
> + */
> + if (port_entry.pci_pf_number > MAX_NIC_PHY_PORTS) {
> + VLOG_WARN("physical port number out of range for port '%s': "
> + "%"PRIu32,
> + port_entry.netdev_name, port_entry.pci_pf_number);
> + continue;
> + }
> + devlink_port_add_function(&port_entry,
> +
> &host_pf_macs[port_entry.pci_pf_number]);
> + break;
> + };
> + }
> + nl_dl_dump_finish(port_dump);
> + nl_dl_dump_destroy(port_dump);
> +
> + return 0;
> +}
> +
> +static int
> +plug_representor_destroy(void)
> +{
> + shash_destroy_free_data(&devlink_ports);
> +
> + return 0;
> +}
> +
> +static bool
> +plug_representor_port_prepare(const struct plug_port_ctx_in *ctx_in,
> + struct plug_port_ctx_out *ctx_out)
> +{
> + char keybuf[MAX_KEY_LEN];
> + const char *pf_mac = smap_get(ctx_in->lport_options,
> + "plug:representor:pf-mac");
> + const char *vf_num = smap_get(ctx_in->lport_options,
> + "plug:representor:vf-num");
> + if (!fill_devlink_ports_key_from_strs(keybuf, sizeof(keybuf),
> + pf_mac, vf_num))
> + {
> + /* Overflow, most likely incorrect input data from database */
> + VLOG_WARN("Southbound DB port plugging options out of range for "
> + "lport: %s pf-mac: '%s' vf-num: '%s'",
> + ctx_in->lport_name, pf_mac, vf_num);
> + return false;
> + }
> +
> + char *rep_port;
> + rep_port = shash_find_data(&devlink_ports, keybuf);
> + VLOG_INFO("plug_representor %s (%s) -> %s",
> + ctx_in->lport_name, rep_port, ctx_in->br_int->name);
> + ctx_out->name = rep_port;
> + ctx_out->type = NULL;
> + ctx_out->iface_options = NULL;
> + return true;
> +}
> +
> +static void
> +plug_representor_port_finish(const struct plug_port_ctx_in *ctx_in
> OVS_UNUSED,
> + struct plug_port_ctx_out *ctx_out OVS_UNUSED)
> +{
> + /* Nothing to be done here for now */
> +}
> +
> +static void
> +plug_representor_port_ctx_destroy(
> + const struct plug_port_ctx_in *ctx_in OVS_UNUSED,
> + struct plug_port_ctx_out *ctx_out OVS_UNUSED)
> +{
> + /* Noting to be done here for now */
> +}
> +
> +const struct plug_class plug_representor = {
> + .type = "representor",
> + .init = plug_representor_init,
> + .destroy = plug_representor_destroy,
> + .plug_get_maintained_iface_options = NULL, /* TODO */
> + .run = NULL, /* TODO */
> + .plug_port_prepare = plug_representor_port_prepare,
> + .plug_port_finish = plug_representor_port_finish,
> + .plug_port_ctx_destroy = plug_representor_port_ctx_destroy,
> +};
> +
> +/* The kernel devlink-port interface provides a vendor neutral and standard
> way
> + * of discovering host visible resources such as MAC address of interfaces
> from
> + * a program running on the NIC SoC side.
> + *
> + * However a fairly recent kernel version is required for it to work, so
> until
> + * this is widely available we provide this helper to retrieve the same
> + * information from the interim sysfs solution. */
> +static bool
> +compat_get_host_pf_mac(const char *netdev_name, struct eth_addr *ea)
> +{
> + char file_name[IFNAMSIZ + 35 + 1];
> + FILE *stream;
> + char line[128];
> + bool retval = false;
> +
> + snprintf(file_name, sizeof(file_name),
> + "/sys/class/net/%s/smart_nic/pf/config", netdev_name);
> + stream = fopen(file_name, "r");
> + if (!stream) {
> + VLOG_WARN("%s: open failed (%s)",
> + file_name, ovs_strerror(errno));
> + *ea = eth_addr_zero;
> + return false;
> + }
> + while (fgets(line, sizeof(line), stream)) {
> + char key[16];
> + char *cp;
> + if (ovs_scan(line, "%15[^:]: ", key)
> + && key[0] == 'M' && key[1] == 'A' && key[2] == 'C')
> + {
> + /* strip any newline character */
> + if ((cp = strchr(line, '\n')) != NULL) {
> + *cp = '\0';
> + }
> + /* point cp at end of key + ': ', i.e. start of MAC address */
> + cp = line + strnlen(key, sizeof(key)) + 2;
> + retval = eth_addr_from_string(cp, ea);
> + break;
> + }
> + }
> + fclose(stream);
> + return retval;
> +}
> diff --git a/m4/ovn.m4 b/m4/ovn.m4
> index 2909914fb..2f274fc65 100644
> --- a/m4/ovn.m4
> +++ b/m4/ovn.m4
> @@ -592,3 +592,29 @@ AC_DEFUN([OVS_CHECK_DDLOG_FAST_BUILD],
> if $ddlog_fast_build; then
> DDLOG_EXTRA_RUSTFLAGS="-C opt-level=z"
> fi])
> +
> +dnl Checks for Netlink support.
> +AC_DEFUN([OVS_CHECK_NETLINK],
> + [AC_CHECK_HEADER([linux/netlink.h],
> + [HAVE_NETLINK=yes],
> + [HAVE_NETLINK=no],
> + [#include <sys/socket.h>
> + ])
> + AM_CONDITIONAL([HAVE_NETLINK], [test "$HAVE_NETLINK" = yes])
> + if test "$HAVE_NETLINK" = yes; then
> + AC_DEFINE([HAVE_NETLINK], [1],
> + [Define to 1 if Netlink protocol is available.])
> + fi])
> +
> +dnl OVS_CHECK_LINUX_NETLINK
> +dnl
> +dnl Configure Linux netlink compat.
> +AC_DEFUN([OVS_CHECK_LINUX_NETLINK], [
> + AC_COMPILE_IFELSE([
> + AC_LANG_PROGRAM([#include <linux/netlink.h>], [
> + struct nla_bitfield32 x = { 0 };
> + ])],
> + [AC_DEFINE([HAVE_NLA_BITFIELD32], [1],
> + [Define to 1 if struct nla_bitfield32 is available.])])
> +])
> +
> --
> 2.32.0
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
--
Frode Nordahl
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev