merge: branch 'ih/eswitch_mode'

sriov: add support to configure the NIC's eswitch settings via Devlink

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1867
This commit is contained in:
Íñigo Huguet 2024-02-20 15:04:31 +00:00
commit 09f2e1c107
16 changed files with 1078 additions and 123 deletions

View file

@ -672,6 +672,8 @@ src_libnm_platform_libnm_platform_la_SOURCES = \
src/libnm-platform/nmp-object.h \
src/libnm-platform/nmp-plobj.c \
src/libnm-platform/nmp-plobj.h \
src/libnm-platform/devlink/nm-devlink.c \
src/libnm-platform/devlink/nm-devlink.h \
src/libnm-platform/wifi/nm-wifi-utils-nl80211.c \
src/libnm-platform/wifi/nm-wifi-utils-nl80211.h \
src/libnm-platform/wifi/nm-wifi-utils-private.h \

View file

@ -137,8 +137,7 @@ typedef struct {
GCancellable *cancellable;
NMPlatformAsyncCallback callback;
gpointer callback_data;
guint num_vfs;
NMOptionBool autoprobe;
NMPlatformSriovParams sriov_params;
} SriovOp;
typedef enum {
@ -7706,8 +7705,7 @@ sriov_op_start(NMDevice *self, SriovOp *op)
nm_platform_link_set_sriov_params_async(nm_device_get_platform(self),
priv->ifindex,
op->num_vfs,
op->autoprobe,
op->sriov_params,
sriov_op_cb,
op,
op->cancellable);
@ -7768,11 +7766,14 @@ sriov_op_queue_op(NMDevice *self, SriovOp *op)
}
static void
sriov_op_queue(NMDevice *self,
guint num_vfs,
NMOptionBool autoprobe,
NMPlatformAsyncCallback callback,
gpointer callback_data)
sriov_op_queue(NMDevice *self,
guint num_vfs,
NMOptionBool autoprobe,
NMSriovEswitchMode eswitch_mode,
NMSriovEswitchInlineMode eswitch_inline_mode,
NMSriovEswitchEncapMode eswitch_encap_mode,
NMPlatformAsyncCallback callback,
gpointer callback_data)
{
SriovOp *op;
@ -7797,8 +7798,14 @@ sriov_op_queue(NMDevice *self,
op = g_slice_new(SriovOp);
*op = (SriovOp){
.num_vfs = num_vfs,
.autoprobe = autoprobe,
.sriov_params =
(NMPlatformSriovParams){
.num_vfs = num_vfs,
.autoprobe = autoprobe,
.eswitch_mode = (_NMSriovEswitchMode) eswitch_mode,
.eswitch_inline_mode = (_NMSriovEswitchInlineMode) eswitch_inline_mode,
.eswitch_encap_mode = (_NMSriovEswitchEncapMode) eswitch_encap_mode,
},
.callback = callback,
.callback_data = callback_data,
};
@ -7823,7 +7830,14 @@ device_init_static_sriov_num_vfs(NMDevice *self)
-1,
-1);
if (num_vfs >= 0)
sriov_op_queue(self, num_vfs, NM_OPTION_BOOL_DEFAULT, NULL, NULL);
sriov_op_queue(self,
num_vfs,
NM_OPTION_BOOL_DEFAULT,
NM_SRIOV_ESWITCH_MODE_PRESERVE,
NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE,
NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE,
NULL,
NULL);
}
}
@ -10004,6 +10018,9 @@ activate_stage1_device_prepare(NMDevice *self)
sriov_op_queue(self,
nm_setting_sriov_get_total_vfs(s_sriov),
NM_TERNARY_TO_OPTION_BOOL(autoprobe),
nm_setting_sriov_get_eswitch_mode(s_sriov),
nm_setting_sriov_get_eswitch_inline_mode(s_sriov),
nm_setting_sriov_get_eswitch_encap_mode(s_sriov),
sriov_params_cb,
nm_utils_user_data_pack(self, g_steal_pointer(&plat_vfs)));
priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_PENDING;
@ -16720,6 +16737,9 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
sriov_op_queue(self,
0,
NM_OPTION_BOOL_TRUE,
NM_SRIOV_ESWITCH_MODE_PRESERVE,
NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE,
NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE,
sriov_reset_on_deactivate_cb,
nm_utils_user_data_pack(self, GINT_TO_POINTER(reason)));
}
@ -16769,7 +16789,14 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
if (priv->ifindex > 0
&& (s_sriov = nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV))) {
priv->sriov_reset_pending++;
sriov_op_queue(self, 0, NM_OPTION_BOOL_TRUE, sriov_reset_on_failure_cb, self);
sriov_op_queue(self,
0,
NM_OPTION_BOOL_TRUE,
NM_SRIOV_ESWITCH_MODE_PRESERVE,
NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE,
NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE,
sriov_reset_on_failure_cb,
self);
break;
}
/* Schedule the transition to DISCONNECTED. The device can't transition

View file

@ -277,6 +277,35 @@ typedef enum {
| _NM_VLAN_FLAG_LOOSE_BINDING | _NM_VLAN_FLAG_MVRP,
} _NMVlanFlags;
typedef enum {
/* Mirrors libnm's NMSriovEswitchMode.
* Values >= 0 mirror kernel's enum devlink_eswitch_mode. */
_NM_SRIOV_ESWITCH_MODE_PRESERVE = -1,
_NM_SRIOV_ESWITCH_MODE_UNKNOWN = -1, /*< skip >*/
_NM_SRIOV_ESWITCH_MODE_LEGACY = 0,
_NM_SRIOV_ESWITCH_MODE_SWITCHDEV = 1,
} _NMSriovEswitchMode;
typedef enum {
/* Mirrors libnm's NMSriovEswitchInlineMode.
* Values >= 0 mirror kernel's enum devlink_eswitch_inline_mode. */
_NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE = -1,
_NM_SRIOV_ESWITCH_INLINE_MODE_UNKNOWN = -1, /*< skip >*/
_NM_SRIOV_ESWITCH_INLINE_MODE_NONE = 0,
_NM_SRIOV_ESWITCH_INLINE_MODE_LINK = 1,
_NM_SRIOV_ESWITCH_INLINE_MODE_NETWORK = 2,
_NM_SRIOV_ESWITCH_INLINE_MODE_TRANSPORT = 3,
} _NMSriovEswitchInlineMode;
typedef enum {
/* Mirrors libnm's NMSriovEswitchEncapMode.
* Values >= 0 mirror kernel's enum devlink_eswitch_encap_mode. */
_NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE = -1,
_NM_SRIOV_ESWITCH_ENCAP_MODE_UNKNOWN = -1, /*< skip >*/
_NM_SRIOV_ESWITCH_ENCAP_MODE_NONE = 0,
_NM_SRIOV_ESWITCH_ENCAP_MODE_BASIC = 1,
} _NMSriovEswitchEncapMode;
/*****************************************************************************/
typedef enum {

View file

@ -1967,4 +1967,10 @@ global:
nm_setting_hsr_new;
nm_setting_ip_config_get_dhcp_dscp;
nm_setting_get_enum_property_type;
nm_setting_sriov_get_eswitch_mode;
nm_sriov_eswitch_mode_get_type;
nm_setting_sriov_get_eswitch_inline_mode;
nm_sriov_eswitch_inline_mode_get_type;
nm_setting_sriov_get_eswitch_encap_mode;
nm_sriov_eswitch_encap_mode_get_type;
} libnm_1_44_0;

View file

@ -2198,6 +2198,18 @@
dbus-type="i"
gprop-type="NMTernary"
/>
<property name="eswitch-encap-mode"
dbus-type="i"
gprop-type="gint"
/>
<property name="eswitch-inline-mode"
dbus-type="i"
gprop-type="gint"
/>
<property name="eswitch-mode"
dbus-type="i"
gprop-type="gint"
/>
<property name="total-vfs"
dbus-type="u"
gprop-type="guint"

View file

@ -9,6 +9,7 @@
#include "nm-setting-private.h"
#include "nm-utils-private.h"
#include "nm-core-enum-types.h"
/**
* SECTION:nm-setting-sriov
@ -18,7 +19,13 @@
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE(NMSettingSriov, PROP_TOTAL_VFS, PROP_VFS, PROP_AUTOPROBE_DRIVERS, );
NM_GOBJECT_PROPERTIES_DEFINE(NMSettingSriov,
PROP_TOTAL_VFS,
PROP_VFS,
PROP_AUTOPROBE_DRIVERS,
PROP_ESWITCH_MODE,
PROP_ESWITCH_INLINE_MODE,
PROP_ESWITCH_ENCAP_MODE, );
/**
* NMSettingSriov:
@ -32,6 +39,9 @@ struct _NMSettingSriov {
GPtrArray *vfs;
int autoprobe_drivers;
guint32 total_vfs;
int eswitch_mode;
int eswitch_inline_mode;
int eswitch_encap_mode;
};
struct _NMSettingSriovClass {
@ -835,6 +845,54 @@ nm_setting_sriov_get_autoprobe_drivers(NMSettingSriov *setting)
return setting->autoprobe_drivers;
}
/**
* nm_setting_sriov_get_eswitch_mode:
* @setting: the #NMSettingSriov
*
* Returns: the value contained in the #NMSettingSriov:eswitch-mode property.
*
* Since: 1.46
*/
NMSriovEswitchMode
nm_setting_sriov_get_eswitch_mode(NMSettingSriov *setting)
{
g_return_val_if_fail(NM_IS_SETTING_SRIOV(setting), NM_SRIOV_ESWITCH_MODE_PRESERVE);
return setting->eswitch_mode;
}
/**
* nm_setting_sriov_get_eswitch_inline_mode:
* @setting: the #NMSettingSriov
*
* Returns: the value contained in the #NMSettingSriov:eswitch-inline-mode property.
*
* Since: 1.46
*/
NMSriovEswitchInlineMode
nm_setting_sriov_get_eswitch_inline_mode(NMSettingSriov *setting)
{
g_return_val_if_fail(NM_IS_SETTING_SRIOV(setting), NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE);
return setting->eswitch_inline_mode;
}
/**
* nm_setting_sriov_get_eswitch_encap_mode:
* @setting: the #NMSettingSriov
*
* Returns: the value contained in the #NMSettingSriov:eswitch-encap-mode property.
*
* Since: 1.46
*/
NMSriovEswitchEncapMode
nm_setting_sriov_get_eswitch_encap_mode(NMSettingSriov *setting)
{
g_return_val_if_fail(NM_IS_SETTING_SRIOV(setting), NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE);
return setting->eswitch_encap_mode;
}
static int
vf_index_compare(gconstpointer a, gconstpointer b)
{
@ -1331,6 +1389,79 @@ nm_setting_sriov_class_init(NMSettingSriovClass *klass)
NMSettingSriov,
autoprobe_drivers);
/**
* NMSettingSriov:eswitch-mode
*
* Select the eswitch mode of the device. Currently it's only supported for
* PCI PF devices, and only if the eswitch device is managed from the same
* PCI address than the PF.
*
* If set to %NM_SRIOV_ESWITCH_MODE_PRESERVE (default) the eswitch mode won't be
* modified by NetworkManager.
*
* Since: 1.46
*/
_nm_setting_property_define_direct_enum(properties_override,
obj_properties,
NM_SETTING_SRIOV_ESWITCH_MODE,
PROP_ESWITCH_MODE,
NM_TYPE_SRIOV_ESWITCH_MODE,
NM_SRIOV_ESWITCH_MODE_PRESERVE,
NM_SETTING_PARAM_FUZZY_IGNORE,
NULL,
NMSettingSriov,
eswitch_mode);
/**
* NMSettingSriov:eswitch-inline-mode
*
* Select the eswitch inline-mode of the device. Some HWs need the VF driver to put
* part of the packet headers on the TX descriptor so the e-switch can do proper
* matching and steering.
*
* Currently it's only supported for PCI PF devices, and only if the eswitch device
* is managed from the same PCI address than the PF.
*
* If set to %NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE (default) the eswitch inline-mode
* won't be modified by NetworkManager.
*
* Since: 1.46
*/
_nm_setting_property_define_direct_enum(properties_override,
obj_properties,
NM_SETTING_SRIOV_ESWITCH_INLINE_MODE,
PROP_ESWITCH_INLINE_MODE,
NM_TYPE_SRIOV_ESWITCH_INLINE_MODE,
NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE,
NM_SETTING_PARAM_FUZZY_IGNORE,
NULL,
NMSettingSriov,
eswitch_inline_mode);
/**
* NMSettingSriov:eswitch-encap-mode
*
* Select the eswitch encapsulation support.
*
* Currently it's only supported for PCI PF devices, and only if the eswitch device
* is managed from the same PCI address than the PF.
*
* If set to %NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE (default) the eswitch encap-mode
* won't be modified by NetworkManager.
*
* Since: 1.46
*/
_nm_setting_property_define_direct_enum(properties_override,
obj_properties,
NM_SETTING_SRIOV_ESWITCH_ENCAP_MODE,
PROP_ESWITCH_ENCAP_MODE,
NM_TYPE_SRIOV_ESWITCH_ENCAP_MODE,
NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE,
NM_SETTING_PARAM_FUZZY_IGNORE,
NULL,
NMSettingSriov,
eswitch_encap_mode);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
_nm_setting_class_commit(setting_class,

View file

@ -26,9 +26,12 @@ G_BEGIN_DECLS
#define NM_SETTING_SRIOV_SETTING_NAME "sriov"
#define NM_SETTING_SRIOV_TOTAL_VFS "total-vfs"
#define NM_SETTING_SRIOV_VFS "vfs"
#define NM_SETTING_SRIOV_AUTOPROBE_DRIVERS "autoprobe-drivers"
#define NM_SETTING_SRIOV_TOTAL_VFS "total-vfs"
#define NM_SETTING_SRIOV_VFS "vfs"
#define NM_SETTING_SRIOV_AUTOPROBE_DRIVERS "autoprobe-drivers"
#define NM_SETTING_SRIOV_ESWITCH_MODE "eswitch-mode"
#define NM_SETTING_SRIOV_ESWITCH_INLINE_MODE "eswitch-inline-mode"
#define NM_SETTING_SRIOV_ESWITCH_ENCAP_MODE "eswitch-encap-mode"
#define NM_SRIOV_VF_ATTRIBUTE_MAC "mac"
#define NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK "spoof-check"
@ -53,6 +56,55 @@ typedef enum {
NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD = 1,
} NMSriovVFVlanProtocol;
/**
* NMSriovEswitchMode:
* @NM_SRIOV_ESWITCH_MODE_PRESERVE: don't modify current eswitch mode
* @NM_SRIOV_ESWITCH_MODE_LEGACY: use legacy SRIOV
* @NM_SRIOV_ESWITCH_MODE_SWITCHDEV: use switchdev mode
*
* Since: 1.46
*/
typedef enum {
NM_SRIOV_ESWITCH_MODE_PRESERVE = -1,
NM_SRIOV_ESWITCH_MODE_UNKNOWN = -1, /*< skip >*/
NM_SRIOV_ESWITCH_MODE_LEGACY = 0,
NM_SRIOV_ESWITCH_MODE_SWITCHDEV = 1,
} NMSriovEswitchMode;
/**
* NMSriovEswitchInlineMode:
* @NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE: don't modify current inline-mode
* @NM_SRIOV_ESWITCH_INLINE_MODE_NONE: don't use inline mode
* @NM_SRIOV_ESWITCH_INLINE_MODE_LINK: L2 mode
* @NM_SRIOV_ESWITCH_INLINE_MODE_NETWORK: L3 mode
* @NM_SRIOV_ESWITCH_INLINE_MODE_TRANSPORT: L4 mode
*
* Since: 1.46
*/
typedef enum {
NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE = -1,
NM_SRIOV_ESWITCH_INLINE_MODE_UNKNOWN = -1, /*< skip >*/
NM_SRIOV_ESWITCH_INLINE_MODE_NONE = 0,
NM_SRIOV_ESWITCH_INLINE_MODE_LINK = 1,
NM_SRIOV_ESWITCH_INLINE_MODE_NETWORK = 2,
NM_SRIOV_ESWITCH_INLINE_MODE_TRANSPORT = 3,
} NMSriovEswitchInlineMode;
/**
* NMSriovEswitchEncapMode:
* @NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE: don't modify current encap-mode
* @NM_SRIOV_ESWITCH_ENCAP_MODE_NONE: disable encapsulation mode
* @NM_SRIOV_ESWITCH_ENCAP_MODE_BASIC: enable encapsulation mode
*
* Since: 1.46
*/
typedef enum {
NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE = -1,
NM_SRIOV_ESWITCH_ENCAP_MODE_UNKNOWN = -1, /*< skip >*/
NM_SRIOV_ESWITCH_ENCAP_MODE_NONE = 0,
NM_SRIOV_ESWITCH_ENCAP_MODE_BASIC = 1,
} NMSriovEswitchEncapMode;
NM_AVAILABLE_IN_1_14
GType nm_setting_sriov_get_type(void);
NM_AVAILABLE_IN_1_14
@ -73,6 +125,12 @@ NM_AVAILABLE_IN_1_14
void nm_setting_sriov_clear_vfs(NMSettingSriov *setting);
NM_AVAILABLE_IN_1_14
NMTernary nm_setting_sriov_get_autoprobe_drivers(NMSettingSriov *setting);
NM_AVAILABLE_IN_1_46
NMSriovEswitchMode nm_setting_sriov_get_eswitch_mode(NMSettingSriov *setting);
NM_AVAILABLE_IN_1_46
NMSriovEswitchInlineMode nm_setting_sriov_get_eswitch_inline_mode(NMSettingSriov *setting);
NM_AVAILABLE_IN_1_46
NMSriovEswitchEncapMode nm_setting_sriov_get_eswitch_encap_mode(NMSettingSriov *setting);
NM_AVAILABLE_IN_1_14
gboolean nm_sriov_vf_add_vlan(NMSriovVF *vf, guint vlan_id);

View file

@ -0,0 +1,365 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2024 Red Hat, Inc.
*/
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
#include "nm-devlink.h"
#include <linux/if.h>
#include <linux/devlink.h>
#include "libnm-log-core/nm-logging.h"
#include "libnm-platform/nm-netlink.h"
#include "libnm-platform/nm-platform.h"
#include "libnm-platform/nm-platform-utils.h"
#define _NMLOG_PREFIX_NAME "devlink"
#define _NMLOG_DOMAIN LOGD_PLATFORM | LOGD_DEVICE
#define _NMLOG(level, ...) \
G_STMT_START \
{ \
char _ifname_buf[IFNAMSIZ]; \
const char *_ifname = self ? nmp_utils_if_indextoname(self->ifindex, _ifname_buf) : NULL; \
\
nm_log((level), \
_NMLOG_DOMAIN, \
_ifname ?: NULL, \
NULL, \
"%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
_NMLOG_PREFIX_NAME, \
NM_PRINT_FMT_QUOTED(_ifname, " (", _ifname, ")", "") \
_NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
G_STMT_END
#define CB_RESULT_PENDING 0
#define CB_RESULT_OK 1
struct _NMDevlink {
NMPlatform *plat;
struct nl_sock *genl_sock_sync;
guint16 genl_family_id;
int ifindex;
};
/**
* nm_devlink_new:
* @platform: the #NMPlatform that will use this #NMDevlink instance
* @genl_sock_sync: the netlink socket (will be used synchronously)
* @ifindex: the kernel's netdev ifindex corresponding to the devlink device
*
* Create a new #NMDevlink instance to make devlink queries regarding a specific
* device.
*
* Returns: (transfer full): the allocated new #NMDevlink
*/
NMDevlink *
nm_devlink_new(NMPlatform *platform, struct nl_sock *genl_sock_sync, int ifindex)
{
NMDevlink *self = g_new(NMDevlink, 1);
self->plat = platform;
self->genl_sock_sync = genl_sock_sync;
self->genl_family_id = nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_DEVLINK);
self->ifindex = ifindex;
return self;
}
/**
* nm_devlink_get_dev_identifier:
* @self: the #NMDevlink
* @out_bus: (out): the "bus_name" part of the devlink device identifier
* @out_addr: (out): the "bus_addr" part of the devlink device identifier
* @error: (optional): the error location
*
* Get the devlink device identifier of the device for which the #NMDevlink was
* created (with the @ifindex argument of nm_devlink_get_new()). A devlink device
* is identified as "bus_name/bus_addr" (i.e. "pci/0000:65:00.0"). This function
* provides both parts separately.
*
* Note that here we only get the potential devlink device identifier. The real devlink
* device might not even exist if the hw doesn't implement devlink or the netdev
* doesn't have a 1-1 corresponding devlink device (i.e. because it's a VF or
* because the hw uses a "one eswitch for many ports" model).
*
* Also note that currently only PCI devices are supported, an error will be
* returned for other kind of devices.
*
* Returns: FALSE in case of error, TRUE otherwise
*/
gboolean
nm_devlink_get_dev_identifier(NMDevlink *self, char **out_bus, char **out_addr, GError **error)
{
const char *bus;
char sbuf[IFNAMSIZ];
NMPUtilsEthtoolDriverInfo ethtool_driver_info;
nm_assert(out_bus != NULL && out_addr != NULL);
nm_assert(!error || !*error);
if (!nm_platform_link_get_udev_property(self->plat, self->ifindex, "ID_BUS", &bus)) {
g_set_error(error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"Can't get udev info for device '%s'",
nmp_utils_if_indextoname(self->ifindex, sbuf));
return FALSE;
}
if (!nm_streq0(bus, "pci")) {
g_set_error(error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"Devlink is only supported for PCI but device '%s' has bus name '%s'",
nmp_utils_if_indextoname(self->ifindex, sbuf),
bus);
return FALSE;
}
if (!nmp_utils_ethtool_get_driver_info(self->ifindex, &ethtool_driver_info)) {
g_set_error(error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"Can't get ethtool driver info for device '%s'",
nmp_utils_if_indextoname(self->ifindex, sbuf));
return FALSE;
}
*out_bus = g_strdup("pci");
*out_addr = g_strdup(ethtool_driver_info._private_bus_info);
return TRUE;
}
static struct nl_msg *
devlink_alloc_msg(NMDevlink *self, uint8_t cmd, uint16_t flags)
{
nm_auto_nlmsg struct nl_msg *msg = nlmsg_alloc(0);
if (!msg)
return NULL;
genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, self->genl_family_id, 0, flags, cmd, 0);
return g_steal_pointer(&msg);
}
static int
ack_cb_handler(const struct nl_msg *msg, void *data)
{
int *result = data;
*result = CB_RESULT_OK;
return NL_STOP;
}
static int
finish_cb_handler(const struct nl_msg *msg, void *data)
{
int *result = data;
*result = CB_RESULT_OK;
return NL_SKIP;
}
static int
err_cb_handler(const struct sockaddr_nl *nla, const struct nlmsgerr *err, void *data)
{
void **args = data;
NMDevlink *self = args[0];
int *result = args[1];
char **err_msg = args[2];
const char *extack_msg = NULL;
*result = err->error;
nlmsg_parse_error(nlmsg_undata(err), &extack_msg);
_LOGT("error response (%d - %s)", err->error, extack_msg ?: nm_strerror(err->error));
if (err_msg)
*err_msg = g_strdup(extack_msg ?: nm_strerror(err->error));
return NL_SKIP;
}
static int
devlink_send_and_recv(NMDevlink *self,
struct nl_msg *msg,
int (*valid_handler)(const struct nl_msg *, void *),
void *valid_data,
char **err_msg)
{
int nle;
int cb_result = CB_RESULT_PENDING;
void *err_arg[] = {self, &cb_result, err_msg};
const struct nl_cb cb = {
.err_cb = err_cb_handler,
.err_arg = err_arg,
.finish_cb = finish_cb_handler,
.finish_arg = &cb_result,
.ack_cb = ack_cb_handler,
.ack_arg = &cb_result,
.valid_cb = valid_handler,
.valid_arg = valid_data,
};
g_return_val_if_fail(msg != NULL, -ENOMEM);
if (err_msg)
*err_msg = NULL;
nle = nl_send_auto(self->genl_sock_sync, msg);
if (nle < 0)
goto out;
while (cb_result == CB_RESULT_PENDING) {
nle = nl_recvmsgs(self->genl_sock_sync, &cb);
if (nle < 0 && nle != -EAGAIN) {
_LOGW("nl_recvmsgs() error (%d - %s)", nle, nm_strerror(nle));
break;
}
}
out:
if (nle < 0 && err_msg && *err_msg == NULL)
*err_msg = strdup(nm_strerror(nle));
if (nle >= 0 && cb_result < 0)
nle = cb_result;
return nle;
}
static int
devlink_parse_eswitch_mode(const struct nl_msg *msg, void *data)
{
static const struct nla_policy eswitch_policy[] = {
[DEVLINK_ATTR_ESWITCH_MODE] = {.type = NLA_U16},
[DEVLINK_ATTR_ESWITCH_INLINE_MODE] = {.type = NLA_U8},
[DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = {.type = NLA_U8},
};
NMDevlinkEswitchParams *params = data;
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb[G_N_ELEMENTS(eswitch_policy)];
struct nlattr *nla;
if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), eswitch_policy) < 0)
return NL_SKIP;
nla = tb[DEVLINK_ATTR_ESWITCH_MODE];
params->mode = nla ? (_NMSriovEswitchMode) nla_get_u16(nla) : _NM_SRIOV_ESWITCH_MODE_UNKNOWN;
nla = tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE];
params->inline_mode =
nla ? (_NMSriovEswitchInlineMode) nla_get_u8(nla) : _NM_SRIOV_ESWITCH_INLINE_MODE_UNKNOWN;
nla = tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE];
params->encap_mode =
nla ? (_NMSriovEswitchEncapMode) nla_get_u8(nla) : _NM_SRIOV_ESWITCH_ENCAP_MODE_UNKNOWN;
return NL_OK;
}
/*
* nm_devlink_get_eswitch_params:
* @self: the #NMDevlink
* @out_params: the eswitch parameters read via Devlink
* @error: the error location
*
* Get the eswitch configuration of the device related to the #NMDevlink instance. Note
* that this might be unsupported by the device (see nm_devlink_get_dev()).
*
* Returns: FALSE in case of error, TRUE otherwise
*/
gboolean
nm_devlink_get_eswitch_params(NMDevlink *self, NMDevlinkEswitchParams *out_params, GError **error)
{
nm_auto_nlmsg struct nl_msg *msg = NULL;
gs_free char *bus = NULL;
gs_free char *addr = NULL;
gs_free char *err_msg = NULL;
int rc;
nm_assert(out_params);
if (!nm_devlink_get_dev_identifier(self, &bus, &addr, error))
return FALSE;
msg = devlink_alloc_msg(self, DEVLINK_CMD_ESWITCH_GET, 0);
NLA_PUT_STRING(msg, DEVLINK_ATTR_BUS_NAME, bus);
NLA_PUT_STRING(msg, DEVLINK_ATTR_DEV_NAME, addr);
rc = devlink_send_and_recv(self, msg, devlink_parse_eswitch_mode, out_params, &err_msg);
if (rc < 0) {
g_set_error(error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"devlink: eswitch get failed (%d - %s)",
rc,
err_msg);
return FALSE;
}
_LOGD("eswitch get success");
return TRUE;
nla_put_failure:
g_return_val_if_reached(FALSE);
}
/*
* nm_devlink_set_eswitch_params:
* @self: the #NMDevlink
* @params: the eswitch parameters to set
* @error: the error location
*
* Set the eswitch configuration of the device related to the #NMDevlink instance. Note
* that this might be unsupported by the device (see nm_devlink_get_dev()).
*
* If any of the eswitch parameters is set to "preserve" it won't be modified.
*
* Returns: FALSE in case of error, TRUE otherwise
*/
gboolean
nm_devlink_set_eswitch_params(NMDevlink *self, NMDevlinkEswitchParams params, GError **error)
{
nm_auto_nlmsg struct nl_msg *msg = NULL;
gs_free char *bus = NULL;
gs_free char *addr = NULL;
gs_free char *err_msg = NULL;
int rc;
if (params.mode == _NM_SRIOV_ESWITCH_MODE_PRESERVE
&& params.inline_mode == _NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE
&& params.encap_mode == _NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE)
return TRUE;
if (!nm_devlink_get_dev_identifier(self, &bus, &addr, error))
return FALSE;
msg = devlink_alloc_msg(self, DEVLINK_CMD_ESWITCH_SET, 0);
NLA_PUT_STRING(msg, DEVLINK_ATTR_BUS_NAME, bus);
NLA_PUT_STRING(msg, DEVLINK_ATTR_DEV_NAME, addr);
if (params.mode != _NM_SRIOV_ESWITCH_MODE_PRESERVE)
NLA_PUT_U16(msg, DEVLINK_ATTR_ESWITCH_MODE, params.mode);
if (params.inline_mode != _NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE)
NLA_PUT_U8(msg, DEVLINK_ATTR_ESWITCH_INLINE_MODE, params.inline_mode);
if (params.encap_mode != _NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE)
NLA_PUT_U8(msg, DEVLINK_ATTR_ESWITCH_ENCAP_MODE, params.encap_mode);
rc = devlink_send_and_recv(self, msg, NULL, NULL, &err_msg);
if (rc < 0) {
g_set_error(error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"devlink: eswitch set failed (%d - %s)",
rc,
err_msg);
return FALSE;
}
_LOGD("eswitch set success");
return TRUE;
nla_put_failure:
g_return_val_if_reached(FALSE);
}

View file

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2024 Red Hat, Inc.
*/
#ifndef __NMP_DEVLINK_H__
#define __NMP_DEVLINK_H__
#include "libnm-base/nm-base.h"
#include <linux/devlink.h>
struct nl_sock;
typedef struct _NMPlatform NMPlatform;
typedef struct _NMDevlink NMDevlink;
typedef struct {
_NMSriovEswitchMode mode;
_NMSriovEswitchInlineMode inline_mode;
_NMSriovEswitchEncapMode encap_mode;
} NMDevlinkEswitchParams;
NMDevlink *nm_devlink_new(NMPlatform *platform, struct nl_sock *genl_sock_sync, int ifindex);
gboolean
nm_devlink_get_dev_identifier(NMDevlink *self, char **out_bus, char **out_addr, GError **error);
gboolean
nm_devlink_get_eswitch_params(NMDevlink *self, NMDevlinkEswitchParams *out_params, GError **error);
gboolean
nm_devlink_set_eswitch_params(NMDevlink *self, NMDevlinkEswitchParams params, GError **error);
#endif /* __NMP_DEVLINK_H__ */

View file

@ -12,6 +12,7 @@ libnm_platform = static_library(
'nmp-netns.c',
'nmp-object.c',
'nmp-plobj.c',
'devlink/nm-devlink.c',
'wifi/nm-wifi-utils-nl80211.c',
'wifi/nm-wifi-utils.c',
'wpan/nm-wpan-utils.c',

View file

@ -41,6 +41,7 @@
#include "libnm-platform/nm-netlink.h"
#include "libnm-platform/nm-platform-utils.h"
#include "libnm-platform/nmp-netns.h"
#include "libnm-platform/devlink/nm-devlink.h"
#include "libnm-platform/wifi/nm-wifi-utils-wext.h"
#include "libnm-platform/wifi/nm-wifi-utils.h"
#include "libnm-platform/wpan/nm-wpan-utils.h"
@ -8881,141 +8882,394 @@ nla_put_failure:
g_return_val_if_reached(FALSE);
}
static void
sriov_idle_cb(gpointer user_data, GCancellable *cancellable)
static gint64
sriov_read_sysctl_uint(NMPlatform *platform,
int dirfd,
const char *ifname,
const char *dev_file,
GError **error)
{
gs_unref_object NMPlatform *platform = NULL;
gs_free_error GError *cancelled_error = NULL;
gs_free_error GError *error = NULL;
NMPlatformAsyncCallback callback;
gpointer callback_data;
const char *path;
gint64 val;
nm_assert(NM_STRLEN("device/%s") + strlen(dev_file));
path = nm_sprintf_bufa(256, "device/%s", dev_file);
val = nm_platform_sysctl_get_int_checked(platform,
NMP_SYSCTL_PATHID_NETDIR_UNSAFE_A(dirfd, ifname, path),
10,
0,
G_MAXUINT,
-1);
if (val < 0) {
g_set_error(error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"couldn't read %s: %s",
dev_file,
nm_strerror_native(errno));
return -errno;
}
return val;
}
static gboolean
sriov_set_autoprobe(NMPlatform *platform,
int dirfd,
const char *ifname,
NMOptionBool autoprobe,
GError **error)
{
int current_autoprobe =
(int) sriov_read_sysctl_uint(platform, dirfd, ifname, "sriov_drivers_autoprobe", error);
if (current_autoprobe == -ENOENT) {
/* older kernel versions don't have this sysctl. Assume the value is "1". */
current_autoprobe = 1;
g_clear_error(error);
}
if (current_autoprobe < 0)
return FALSE;
if (autoprobe != NM_OPTION_BOOL_DEFAULT && current_autoprobe != autoprobe) {
if (!nm_platform_sysctl_set(
platform,
NMP_SYSCTL_PATHID_NETDIR_A(dirfd, ifname, "device/sriov_drivers_autoprobe"),
autoprobe == 1 ? "1" : "0")) {
g_set_error(error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"couldn't set SR-IOV drivers-autoprobe to %d: %s",
(int) autoprobe,
nm_strerror_native(errno));
return FALSE;
}
}
return TRUE;
}
#define _SRIOV_ASYNC_MAX_STEPS 4
typedef struct _SriovAsyncState {
NMPlatform *platform;
int ifindex;
NMPlatformSriovParams sriov_params;
void (*steps[_SRIOV_ASYNC_MAX_STEPS])(struct _SriovAsyncState *);
int current_step;
NMPlatformAsyncCallback callback;
gpointer data;
GCancellable *cancellable;
} SriovAsyncState;
static void
sriov_async_invoke_callback(gpointer user_data, GCancellable *cancellable)
{
gs_free_error GError *cancelled_error = NULL;
gs_free_error GError *error = NULL;
NMPlatformAsyncCallback callback;
gpointer callback_data;
g_cancellable_set_error_if_cancelled(cancellable, &cancelled_error);
nm_utils_user_data_unpack(user_data, &platform, &error, &callback, &callback_data);
nm_utils_user_data_unpack(user_data, &error, &callback, &callback_data);
callback(cancelled_error ?: error, callback_data);
}
static void
sriov_async_finish_err(SriovAsyncState *async_state, GError *error)
{
NMPlatform *platform = async_state->platform;
_LOGD("finished configuring SR-IOV, error: %s", error ? error->message : "none");
if (async_state->callback) {
/* nm_platform_link_set_sriov_params() promises to always call the callback,
* and always asynchronously. We might have reached here without doing
* any asynchronous task, so invoke the user's callback in the idle task
* to make it asynchronous. Actually, let's make it simple and do it
* always in this way, even if asynchronous tasks were made.
*/
gpointer packed = nm_utils_user_data_pack(g_steal_pointer(&error),
async_state->callback,
async_state->data);
nm_utils_invoke_on_idle(async_state->cancellable, sriov_async_invoke_callback, packed);
}
g_object_unref(async_state->platform);
g_object_unref(async_state->cancellable);
g_free(async_state);
g_free(error);
}
static void
sriov_async_call_next_step(SriovAsyncState *async_state)
{
if (g_cancellable_is_cancelled(async_state->cancellable)) {
sriov_async_finish_err(async_state, NULL); /* The error will be set later */
return;
}
async_state->current_step++;
nm_assert(async_state->current_step >= 0);
nm_assert(async_state->current_step < _SRIOV_ASYNC_MAX_STEPS);
nm_assert(async_state->steps[async_state->current_step] != NULL);
async_state->steps[async_state->current_step](async_state);
}
static void
sriov_async_sysctl_done_cb(GError *error, gpointer data)
{
SriovAsyncState *async_state = data;
if (error)
sriov_async_finish_err(async_state, g_error_copy(error));
else
sriov_async_call_next_step(async_state);
}
static void
sriov_async_set_num_vfs(SriovAsyncState *async_state, const char *val)
{
NMPlatform *platform = async_state->platform;
const char *values[] = {val, NULL};
nm_auto_close int dirfd = -1;
char ifname[IFNAMSIZ];
gs_free_error GError *error = NULL;
dirfd = nm_platform_sysctl_open_netdir(platform, async_state->ifindex, ifname);
if (!dirfd) {
g_set_error(&error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"couldn't open netdir for device with ifindex %d",
async_state->ifindex);
sriov_async_finish_err(async_state, g_steal_pointer(&error));
return;
}
sysctl_set_async(platform,
NMP_SYSCTL_PATHID_NETDIR_A(dirfd, ifname, "device/sriov_numvfs"),
values,
sriov_async_sysctl_done_cb,
async_state,
async_state->cancellable);
}
static void
sriov_async_step1_destroy_vfs(SriovAsyncState *async_state)
{
NMPlatform *platform = async_state->platform;
_LOGD("destroying VFs before configuring SR-IOV");
sriov_async_set_num_vfs(async_state, "0");
}
static void
sriov_async_step2_set_eswitch_mode(SriovAsyncState *async_state)
{
NMPlatform *platform = async_state->platform;
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
gs_free NMDevlink *devlink = NULL;
gs_free_error GError *error = NULL;
NMDevlinkEswitchParams eswitch_params = {
.mode = async_state->sriov_params.eswitch_mode,
.inline_mode = async_state->sriov_params.eswitch_inline_mode,
.encap_mode = async_state->sriov_params.eswitch_encap_mode,
};
_LOGD("setting eswitch params (mode=%d, inline-mode=%d, encap-mode=%d)",
(int) eswitch_params.mode,
(int) eswitch_params.inline_mode,
(int) eswitch_params.encap_mode);
/* We set eswitch mode as a sriov_async step because it's in the middle of
* other steps that are async. However, this step itself is synchronous. */
devlink = nm_devlink_new(platform, priv->sk_genl_sync, async_state->ifindex);
if (!nm_devlink_set_eswitch_params(devlink, eswitch_params, &error)) {
sriov_async_finish_err(async_state, g_steal_pointer(&error));
return;
}
sriov_async_call_next_step(async_state);
}
static void
sriov_async_step3_create_vfs(SriovAsyncState *async_state)
{
NMPlatform *platform = async_state->platform;
const char *val = nm_sprintf_bufa(32, "%u", async_state->sriov_params.num_vfs);
_LOGD("setting sriov_numvfs to %u", async_state->sriov_params.num_vfs);
sriov_async_set_num_vfs(async_state, val);
}
static void
sriov_async_step_finish_ok(SriovAsyncState *async_state)
{
sriov_async_finish_err(async_state, NULL);
}
static int
sriov_eswitch_get_needs_change(SriovAsyncState *async_state,
gboolean *out_needs_change,
GError **error)
{
NMPlatform *platform = async_state->platform;
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
_NMSriovEswitchMode mode = async_state->sriov_params.eswitch_mode;
_NMSriovEswitchInlineMode inline_mode = async_state->sriov_params.eswitch_inline_mode;
_NMSriovEswitchEncapMode encap_mode = async_state->sriov_params.eswitch_encap_mode;
NMDevlinkEswitchParams current_params;
gs_free NMDevlink *devlink = NULL;
nm_assert(out_needs_change);
if (mode == _NM_SRIOV_ESWITCH_MODE_PRESERVE
&& inline_mode == _NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE
&& encap_mode == _NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE) {
*out_needs_change = FALSE;
return 0;
}
devlink = nm_devlink_new(platform, priv->sk_genl_sync, async_state->ifindex);
if (!nm_devlink_get_eswitch_params(devlink, &current_params, error))
return -1;
*out_needs_change = (mode != _NM_SRIOV_ESWITCH_MODE_PRESERVE && mode != current_params.mode)
|| (inline_mode != _NM_SRIOV_ESWITCH_INLINE_MODE_PRESERVE
&& inline_mode != current_params.inline_mode)
|| (encap_mode != _NM_SRIOV_ESWITCH_ENCAP_MODE_PRESERVE
&& encap_mode != current_params.encap_mode);
return 0;
}
/*
* Take special care when setting new values:
* - don't touch anything if the right values are already set
* - to change the number of VFs, eswitch mode or autoprobe we need to destroy existing VFs
* - the autoprobe setting is irrelevant when numvfs is zero
*/
static void
link_set_sriov_params_async(NMPlatform *platform,
int ifindex,
guint num_vfs,
NMOptionBool autoprobe,
NMPlatformSriovParams sriov_params,
NMPlatformAsyncCallback callback,
gpointer data,
GCancellable *cancellable)
{
SriovAsyncState *async_state;
nm_auto_pop_netns NMPNetns *netns = NULL;
gs_free_error GError *error = NULL;
nm_auto_close int dirfd = -1;
int current_autoprobe;
guint i, total;
gint64 current_num;
char ifname[IFNAMSIZ];
gpointer packed;
const char *values[3];
char buf[64];
int max_vfs;
int current_num_vfs;
gboolean need_change_eswitch_params;
gboolean need_change_vfs;
gboolean need_destroy_vfs;
gboolean need_create_vfs;
int i;
g_return_if_fail(callback || !data);
g_return_if_fail(cancellable);
async_state = g_new0(SriovAsyncState, 1);
async_state->platform = g_object_ref(platform);
async_state->ifindex = ifindex;
async_state->sriov_params = sriov_params;
async_state->current_step = -1;
async_state->callback = callback;
async_state->data = data;
async_state->cancellable = g_object_ref(cancellable);
if (!nm_platform_netns_push(platform, &netns)) {
g_set_error_literal(&error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"couldn't change namespace");
goto out_idle;
"couldn't change network namespace");
sriov_async_finish_err(async_state, g_steal_pointer(&error));
return;
}
dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname);
if (!dirfd) {
g_set_error_literal(&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "couldn't open netdir");
goto out_idle;
}
total = nm_platform_sysctl_get_int_checked(
platform,
NMP_SYSCTL_PATHID_NETDIR_A(dirfd, ifname, "device/sriov_totalvfs"),
10,
0,
G_MAXUINT,
0);
if (!errno && num_vfs > total) {
_LOGW("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs);
num_vfs = total;
}
/*
* Take special care when setting new values:
* - don't touch anything if the right values are already set
* - to change the number of VFs or autoprobe we need to destroy existing VFs
* - the autoprobe setting is irrelevant when numvfs is zero
*/
current_num = nm_platform_sysctl_get_int_checked(
platform,
NMP_SYSCTL_PATHID_NETDIR_A(dirfd, ifname, "device/sriov_numvfs"),
10,
0,
G_MAXUINT,
-1);
current_autoprobe = nm_platform_sysctl_get_int_checked(
platform,
NMP_SYSCTL_PATHID_NETDIR_A(dirfd, ifname, "device/sriov_drivers_autoprobe"),
10,
0,
1,
-1);
if (current_autoprobe == -1 && errno == ENOENT) {
/* older kernel versions don't have this sysctl. Assume the value is
* "1". */
current_autoprobe = 1;
}
if (current_num == num_vfs
&& (autoprobe == NM_OPTION_BOOL_DEFAULT || current_autoprobe == autoprobe))
goto out_idle;
if (NM_IN_SET(autoprobe, NM_OPTION_BOOL_TRUE, NM_OPTION_BOOL_FALSE)
&& current_autoprobe != autoprobe
&& !nm_platform_sysctl_set(
platform,
NMP_SYSCTL_PATHID_NETDIR_A(dirfd, ifname, "device/sriov_drivers_autoprobe"),
nm_sprintf_buf(buf, "%d", (int) autoprobe))) {
g_set_error(&error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"couldn't set SR-IOV drivers-autoprobe to %d: %s",
(int) autoprobe,
nm_strerror_native(errno));
goto out_idle;
"couldn't open netdir for device with ifindex %d",
ifindex);
sriov_async_finish_err(async_state, g_steal_pointer(&error));
return;
}
if (current_num == 0 && num_vfs == 0)
goto out_idle;
current_num_vfs = sriov_read_sysctl_uint(platform, dirfd, ifname, "sriov_numvfs", &error);
if (current_num_vfs < 0) {
sriov_async_finish_err(async_state, g_steal_pointer(&error));
return;
}
max_vfs = sriov_read_sysctl_uint(platform, dirfd, ifname, "sriov_totalvfs", &error);
if (max_vfs < 0) {
_LOGD("link: can't read max VFs (%s)", error->message);
g_clear_error(&error);
max_vfs = sriov_params.num_vfs; /* Try to create all */
}
if (sriov_params.num_vfs > max_vfs) {
_LOGW("link: device %d only supports %u VFs (requested %u)",
ifindex,
max_vfs,
sriov_params.num_vfs);
_LOGW("link: reducing num_vfs to %u for device %d", max_vfs, ifindex);
sriov_params.num_vfs = max_vfs;
async_state->sriov_params.num_vfs = max_vfs;
}
/* Setting autoprobe goes first, we can do it synchronously */
if (sriov_params.num_vfs > 0
&& !sriov_set_autoprobe(platform, dirfd, ifname, sriov_params.autoprobe, &error)) {
sriov_async_finish_err(async_state, g_steal_pointer(&error));
return;
}
/* Decide what actions we must do. Note that we might need to destroy the VFs even
* if num_vfs == current_num_vfs, for example to change the eswitch mode. Because of
* that, we might need to create VFs even if num_vfs == current_num_vfs.
* Steps in order (unnecessary steps are skipped):
* 1. Destroy VFs
* 2. Set eswitch mode
* 3. Create VFs
* 4. Invoke caller's callback
*/
if (sriov_eswitch_get_needs_change(async_state, &need_change_eswitch_params, &error) < 0) {
sriov_async_finish_err(async_state, g_steal_pointer(&error));
return;
}
need_change_vfs = sriov_params.num_vfs != current_num_vfs;
need_destroy_vfs = current_num_vfs > 0 && (need_change_eswitch_params || need_change_vfs);
need_create_vfs = (current_num_vfs == 0 || need_destroy_vfs) && sriov_params.num_vfs > 0;
i = 0;
if (current_num != 0)
values[i++] = "0";
if (num_vfs != 0)
values[i++] = nm_sprintf_bufa(32, "%u", num_vfs);
values[i++] = NULL;
if (need_destroy_vfs)
async_state->steps[i++] = sriov_async_step1_destroy_vfs;
if (need_change_eswitch_params)
async_state->steps[i++] = sriov_async_step2_set_eswitch_mode;
if (need_create_vfs)
async_state->steps[i++] = sriov_async_step3_create_vfs;
sysctl_set_async(platform,
NMP_SYSCTL_PATHID_NETDIR_A(dirfd, ifname, "device/sriov_numvfs"),
values,
callback,
data,
cancellable);
return;
nm_assert(i < _SRIOV_ASYNC_MAX_STEPS);
out_idle:
if (callback) {
packed = nm_utils_user_data_pack(g_object_ref(platform),
g_steal_pointer(&error),
callback,
data);
nm_utils_invoke_on_idle(cancellable, sriov_idle_cb, packed);
}
async_state->steps[i] = sriov_async_step_finish_ok;
sriov_async_call_next_step(async_state);
}
static gboolean

View file

@ -452,6 +452,10 @@ _nm_platform_kernel_support_init(NMPlatformKernelSupportType type, int value)
/*****************************************************************************/
const NMPGenlFamilyInfo nmp_genl_family_infos[_NMP_GENL_FAMILY_TYPE_NUM] = {
[NMP_GENL_FAMILY_TYPE_DEVLINK] =
{
.name = "devlink",
},
[NMP_GENL_FAMILY_TYPE_ETHTOOL] =
{
.name = "ethtool",
@ -2018,8 +2022,7 @@ nm_platform_link_supports_sriov(NMPlatform *self, int ifindex)
void
nm_platform_link_set_sriov_params_async(NMPlatform *self,
int ifindex,
guint num_vfs,
NMOptionBool autoprobe,
NMPlatformSriovParams sriov_params,
NMPlatformAsyncCallback callback,
gpointer callback_data,
GCancellable *cancellable)
@ -2028,11 +2031,17 @@ nm_platform_link_set_sriov_params_async(NMPlatform *self,
g_return_if_fail(ifindex > 0);
_LOG3D("link: setting %u total VFs and autoprobe %d", num_vfs, (int) autoprobe);
_LOG3D("link: setting SR-IOV params (numvfs=%u, autoprobe=%d, eswitch mode=%d inline-mode=%d "
"encap-mode=%d)",
sriov_params.num_vfs,
(int) sriov_params.autoprobe,
(int) sriov_params.eswitch_mode,
(int) sriov_params.eswitch_inline_mode,
(int) sriov_params.eswitch_encap_mode);
klass->link_set_sriov_params_async(self,
ifindex,
num_vfs,
autoprobe,
sriov_params,
callback,
callback_data,
cancellable);

View file

@ -993,6 +993,14 @@ typedef struct {
guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN];
} _nm_alignas(NMPlatformObject) NMPlatformLnkWireGuard;
typedef struct {
guint num_vfs;
NMOptionBool autoprobe;
_NMSriovEswitchMode eswitch_mode;
_NMSriovEswitchInlineMode eswitch_inline_mode;
_NMSriovEswitchEncapMode eswitch_encap_mode;
} NMPlatformSriovParams;
typedef enum {
NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE = 0,
NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS = (1LL << 0),
@ -1084,6 +1092,7 @@ nm_platform_kernel_support_get(NMPlatformKernelSupportType type)
}
typedef enum {
NMP_GENL_FAMILY_TYPE_DEVLINK,
NMP_GENL_FAMILY_TYPE_ETHTOOL,
NMP_GENL_FAMILY_TYPE_MPTCP_PM,
NMP_GENL_FAMILY_TYPE_NL80211,
@ -1171,8 +1180,7 @@ typedef struct {
gboolean (*link_set_name)(NMPlatform *self, int ifindex, const char *name);
void (*link_set_sriov_params_async)(NMPlatform *self,
int ifindex,
guint num_vfs,
NMOptionBool autoprobe,
NMPlatformSriovParams sriov_params,
NMPlatformAsyncCallback callback,
gpointer callback_data,
GCancellable *cancellable);
@ -2034,8 +2042,7 @@ gboolean nm_platform_link_set_name(NMPlatform *self, int ifindex, const char *na
void nm_platform_link_set_sriov_params_async(NMPlatform *self,
int ifindex,
guint num_vfs,
NMOptionBool autoprobe,
NMPlatformSriovParams sriov_params,
NMPlatformAsyncCallback callback,
gpointer callback_data,
GCancellable *cancellable);

View file

@ -7363,6 +7363,15 @@ static const NMMetaPropertyInfo *const property_infos_SRIOV[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS,
.property_type = &_pt_gobject_ternary,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_ESWITCH_MODE,
.property_type = &_pt_gobject_enum,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_ESWITCH_INLINE_MODE,
.property_type = &_pt_gobject_enum,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_ESWITCH_ENCAP_MODE,
.property_type = &_pt_gobject_enum,
),
NULL
};

View file

@ -310,6 +310,9 @@
#define DESCRIBE_DOC_NM_SETTING_SERIAL_SEND_DELAY N_("Time to delay between each byte sent to the modem, in microseconds.")
#define DESCRIBE_DOC_NM_SETTING_SERIAL_STOPBITS N_("Number of stop bits for communication on the serial port. Either 1 or 2. The 1 in \"8n1\" for example.")
#define DESCRIBE_DOC_NM_SETTING_SRIOV_AUTOPROBE_DRIVERS N_("Whether to autoprobe virtual functions by a compatible driver. If set to \"true\" (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF. If set to \"false\" (0), VFs will not be claimed and no network interfaces will be created for them. When set to \"default\" (-1), the global default is used; in case the global default is unspecified it is assumed to be \"true\" (1).")
#define DESCRIBE_DOC_NM_SETTING_SRIOV_ESWITCH_ENCAP_MODE N_("Select the eswitch encapsulation support. Currently it's only supported for PCI PF devices, and only if the eswitch device is managed from the same PCI address than the PF. If set to \"preserve\" (-1) (default) the eswitch encap-mode won't be modified by NetworkManager.")
#define DESCRIBE_DOC_NM_SETTING_SRIOV_ESWITCH_INLINE_MODE N_("Select the eswitch inline-mode of the device. Some HWs need the VF driver to put part of the packet headers on the TX descriptor so the e-switch can do proper matching and steering. Currently it's only supported for PCI PF devices, and only if the eswitch device is managed from the same PCI address than the PF. If set to \"preserve\" (-1) (default) the eswitch inline-mode won't be modified by NetworkManager.")
#define DESCRIBE_DOC_NM_SETTING_SRIOV_ESWITCH_MODE N_("Select the eswitch mode of the device. Currently it's only supported for PCI PF devices, and only if the eswitch device is managed from the same PCI address than the PF. If set to \"preserve\" (-1) (default) the eswitch mode won't be modified by NetworkManager.")
#define DESCRIBE_DOC_NM_SETTING_SRIOV_TOTAL_VFS N_("The total number of virtual functions to create. Note that when the sriov setting is present NetworkManager enforces the number of virtual functions on the interface (also when it is zero) during activation and resets it upon deactivation. To prevent any changes to SR-IOV parameters don't add a sriov setting to the connection.")
#define DESCRIBE_DOC_NM_SETTING_SRIOV_VFS N_("Array of virtual function descriptors. Each VF descriptor is a dictionary mapping attribute names to GVariant values. The 'index' entry is mandatory for each VF. When represented as string a VF is in the form: \"INDEX [ATTR=VALUE[ ATTR=VALUE]...]\". for example: \"2 mac=00:11:22:33:44:55 spoof-check=true\". Multiple VFs can be specified using a comma as separator. Currently, the following attributes are supported: mac, spoof-check, trust, min-tx-rate, max-tx-rate, vlans. The \"vlans\" attribute is represented as a semicolon-separated list of VLAN descriptors, where each descriptor has the form \"ID[.PRIORITY[.PROTO]]\". PROTO can be either 'q' for 802.1Q (the default) or 'ad' for 802.1ad.")
#define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS N_("Array of TC queueing disciplines. When the \"tc\" setting is present, qdiscs from this property are applied upon activation. If the property is empty, all qdiscs are removed and the device will only have the default qdisc assigned by kernel according to the \"net.core.default_qdisc\" sysctl. If the \"tc\" setting is not present, NetworkManager doesn't touch the qdiscs present on the interface.")

View file

@ -1820,6 +1820,18 @@
nmcli-description="Whether to autoprobe virtual functions by a compatible driver. If set to &quot;true&quot; (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF. If set to &quot;false&quot; (0), VFs will not be claimed and no network interfaces will be created for them. When set to &quot;default&quot; (-1), the global default is used; in case the global default is unspecified it is assumed to be &quot;true&quot; (1)."
format="ternary"
values="true/yes/on, false/no/off, default/unknown" />
<property name="eswitch-mode"
nmcli-description="Select the eswitch mode of the device. Currently it&apos;s only supported for PCI PF devices, and only if the eswitch device is managed from the same PCI address than the PF. If set to &quot;preserve&quot; (-1) (default) the eswitch mode won&apos;t be modified by NetworkManager."
format="choice (NMSriovEswitchMode)"
values="preserve (-1), legacy (0), switchdev (1)" />
<property name="eswitch-inline-mode"
nmcli-description="Select the eswitch inline-mode of the device. Some HWs need the VF driver to put part of the packet headers on the TX descriptor so the e-switch can do proper matching and steering. Currently it&apos;s only supported for PCI PF devices, and only if the eswitch device is managed from the same PCI address than the PF. If set to &quot;preserve&quot; (-1) (default) the eswitch inline-mode won&apos;t be modified by NetworkManager."
format="choice (NMSriovEswitchInlineMode)"
values="preserve (-1), none (0), link (1), network (2), transport (3)" />
<property name="eswitch-encap-mode"
nmcli-description="Select the eswitch encapsulation support. Currently it&apos;s only supported for PCI PF devices, and only if the eswitch device is managed from the same PCI address than the PF. If set to &quot;preserve&quot; (-1) (default) the eswitch encap-mode won&apos;t be modified by NetworkManager."
format="choice (NMSriovEswitchEncapMode)"
values="preserve (-1), none (0), basic (1)" />
</setting>
<setting name="tc" >
<property name="qdiscs"