diff mbox series

[ovs-dev,v4,9/9] plug_providers: Introduce representor plugin.

Message ID 20210903192748.1408062-10-frode.nordahl@canonical.com
State Changes Requested
Headers show
Series Introduce infrastructure for plugging providers | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success

Commit Message

Frode Nordahl Sept. 3, 2021, 7:27 p.m. UTC
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 <frode.nordahl@canonical.com>
---
 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

Comments

Frode Nordahl Sept. 8, 2021, 3:03 p.m. UTC | #1
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
<frode.nordahl@canonical.com> 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 <frode.nordahl@canonical.com>
> ---
>  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 <jiri@mellanox.com>
> + *
> + * 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
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Han Zhou Sept. 21, 2021, 7:41 a.m. UTC | #2
On Fri, Sep 3, 2021 at 12:27 PM Frode Nordahl <frode.nordahl@canonical.com>
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 <frode.nordahl@canonical.com>
> ---
>  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
>

Hi Frode,

Thanks for adding this to the series. This does provide a better
understanding of how the plug_provider interfaces are going to be used for
representor ports. However, I had no idea how complex this provider would
be when I proposed adding it to the repo. Now that I am seeing it, I am not
sure if it is a good idea. It is probably better to maintain this provider
under a separate project, primarily because of totally different focus and
dependencies. For an in-tree provider, I'd consider something that plugs
regular VIFs.

I'd also like to hear what other maintainers think. I am sorry for not
realizing this earlier, and if we finally decide to exclude this single
patch from the series, I hope this doesn't waste too much of your effort,
assuming the majority of the code would be the same when it is hosted under
a separate repo.

Thanks,
Han
Numan Siddique Sept. 22, 2021, 10:53 p.m. UTC | #3
On Tue, Sep 21, 2021 at 3:41 AM Han Zhou <hzhou@ovn.org> wrote:
>
> On Fri, Sep 3, 2021 at 12:27 PM Frode Nordahl <frode.nordahl@canonical.com>
> 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 <frode.nordahl@canonical.com>
> > ---
> >  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
> >
>
> Hi Frode,
>
> Thanks for adding this to the series. This does provide a better
> understanding of how the plug_provider interfaces are going to be used for
> representor ports. However, I had no idea how complex this provider would
> be when I proposed adding it to the repo. Now that I am seeing it, I am not
> sure if it is a good idea. It is probably better to maintain this provider
> under a separate project, primarily because of totally different focus and
> dependencies. For an in-tree provider, I'd consider something that plugs
> regular VIFs.
>
> I'd also like to hear what other maintainers think. I am sorry for not
> realizing this earlier, and if we finally decide to exclude this single
> patch from the series, I hope this doesn't waste too much of your effort,
> assuming the majority of the code would be the same when it is hosted under
> a separate repo.
>

I'd agree with Han.  It is better if this implementation is out of the
tree.  +1 from me
if you think this can be an ovn-org github project.

Thanks
Numan

> Thanks,
> Han
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Frode Nordahl Sept. 23, 2021, 4:01 p.m. UTC | #4
On Thu, Sep 23, 2021 at 12:53 AM Numan Siddique <numans@ovn.org> wrote:
>
> On Tue, Sep 21, 2021 at 3:41 AM Han Zhou <hzhou@ovn.org> wrote:
> >
> > On Fri, Sep 3, 2021 at 12:27 PM Frode Nordahl <frode.nordahl@canonical.com>
> > 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 <frode.nordahl@canonical.com>
> > > ---
> > >  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
> > >
> >
> > Hi Frode,
> >
> > Thanks for adding this to the series. This does provide a better
> > understanding of how the plug_provider interfaces are going to be used for
> > representor ports. However, I had no idea how complex this provider would
> > be when I proposed adding it to the repo. Now that I am seeing it, I am not
> > sure if it is a good idea. It is probably better to maintain this provider
> > under a separate project, primarily because of totally different focus and
> > dependencies. For an in-tree provider, I'd consider something that plugs
> > regular VIFs.
> >
> > I'd also like to hear what other maintainers think. I am sorry for not
> > realizing this earlier, and if we finally decide to exclude this single
> > patch from the series, I hope this doesn't waste too much of your effort,
> > assuming the majority of the code would be the same when it is hosted under
> > a separate repo.
> >
>
> I'd agree with Han.  It is better if this implementation is out of the
> tree.  +1 from me
> if you think this can be an ovn-org github project.

That's fair and when I think about it, in line with what we have
discussed in previous RFC reviews so I'll leave this patch out of the
set going into core OVN.

I have already done the build system work for building with an
external plug provider as well as started staging a repository that
should be suitable for upstreaming once it is complete. Having it
hosted on github.com/ovn-org would be sufficient for our needs of
being able to refer to API documentation in an official place for use
in ongoing work in other projects.

Would it work for you to discuss how to go about it in next Thursday's
meeting for example? (I have to skip this evenings meeting
unfortunately).

Cheers!
Han Zhou Sept. 23, 2021, 4:26 p.m. UTC | #5
On Thu, Sep 23, 2021 at 9:02 AM Frode Nordahl <frode.nordahl@canonical.com>
wrote:
>
> On Thu, Sep 23, 2021 at 12:53 AM Numan Siddique <numans@ovn.org> wrote:
> >
> > On Tue, Sep 21, 2021 at 3:41 AM Han Zhou <hzhou@ovn.org> wrote:
> > >
> > > On Fri, Sep 3, 2021 at 12:27 PM Frode Nordahl <
frode.nordahl@canonical.com>
> > > 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 <frode.nordahl@canonical.com>
> > > > ---
> > > >  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
> > > >
> > >
> > > Hi Frode,
> > >
> > > Thanks for adding this to the series. This does provide a better
> > > understanding of how the plug_provider interfaces are going to be
used for
> > > representor ports. However, I had no idea how complex this provider
would
> > > be when I proposed adding it to the repo. Now that I am seeing it, I
am not
> > > sure if it is a good idea. It is probably better to maintain this
provider
> > > under a separate project, primarily because of totally different
focus and
> > > dependencies. For an in-tree provider, I'd consider something that
plugs
> > > regular VIFs.
> > >
> > > I'd also like to hear what other maintainers think. I am sorry for not
> > > realizing this earlier, and if we finally decide to exclude this
single
> > > patch from the series, I hope this doesn't waste too much of your
effort,
> > > assuming the majority of the code would be the same when it is hosted
under
> > > a separate repo.
> > >
> >
> > I'd agree with Han.  It is better if this implementation is out of the
> > tree.  +1 from me
> > if you think this can be an ovn-org github project.
>
> That's fair and when I think about it, in line with what we have
> discussed in previous RFC reviews so I'll leave this patch out of the
> set going into core OVN.
>
> I have already done the build system work for building with an
> external plug provider as well as started staging a repository that
> should be suitable for upstreaming once it is complete. Having it
> hosted on github.com/ovn-org would be sufficient for our needs of
> being able to refer to API documentation in an official place for use
> in ongoing work in other projects.
>
> Would it work for you to discuss how to go about it in next Thursday's
> meeting for example? (I have to skip this evenings meeting
> unfortunately).
>

Yes, hosting under ovn-org sounds good to me. We can discuss at OVN
meetings.

> Cheers!
>
> --
> Frode Nordahl
>
> > Thanks
> > Numan
> >
> > > Thanks,
> > > Han
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >
diff mbox series

Patch

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 <jiri@mellanox.com>
+ *
+ * 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.])])
+])
+