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.nord...@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.nord...@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 <j...@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 > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev -- Frode Nordahl _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev