summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2018-07-11 16:29:55 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2018-07-11 16:29:55 +0200
commit7df4fc38499faf68fbb3bf55ad81c84c91ca408d (patch)
treed668a75734135d812babb447c59a10818344406f
parent2abf4652d43651e014d99e5fac8de9df10df9bbb (diff)
parentc02d1c488f69ed6183cb86c80a771c902ea5e397 (diff)
downloadNetworkManager-7df4fc38499faf68fbb3bf55ad81c84c91ca408d.tar.gz
merge: branch 'bg/sriov-vf-rh1555013'
Add support for changing SR-IOV VF settings. https://bugzilla.redhat.com/show_bug.cgi?id=1555013
-rw-r--r--Makefile.am4
-rw-r--r--clients/cli/connections.c3
-rw-r--r--clients/common/nm-client-utils.c1
-rw-r--r--clients/common/nm-meta-setting-desc.c104
-rw-r--r--clients/common/settings-docs.h.in6
-rw-r--r--docs/libnm/libnm-docs.xml1
-rw-r--r--libnm-core/meson.build2
-rw-r--r--libnm-core/nm-connection.c29
-rw-r--r--libnm-core/nm-connection.h2
-rw-r--r--libnm-core/nm-core-internal.h7
-rw-r--r--libnm-core/nm-core-types.h1
-rw-r--r--libnm-core/nm-dbus-interface.h18
-rw-r--r--libnm-core/nm-keyfile.c83
-rw-r--r--libnm-core/nm-setting-ip-config.c26
-rw-r--r--libnm-core/nm-setting-ip4-config.c2
-rw-r--r--libnm-core/nm-setting-ip6-config.c2
-rw-r--r--libnm-core/nm-setting-sriov.c1353
-rw-r--r--libnm-core/nm-setting-sriov.h123
-rw-r--r--libnm-core/nm-setting-tc-config.c24
-rw-r--r--libnm-core/nm-setting-team-port.c6
-rw-r--r--libnm-core/nm-setting-team.c6
-rw-r--r--libnm-core/nm-setting.c1
-rw-r--r--libnm-core/nm-utils-private.h7
-rw-r--r--libnm-core/nm-utils.c323
-rw-r--r--libnm-core/nm-utils.h11
-rw-r--r--libnm-core/tests/test-setting.c236
-rw-r--r--libnm/NetworkManager.h1
-rw-r--r--libnm/libnm.ver32
-rw-r--r--man/NetworkManager.conf.xml4
-rw-r--r--po/POTFILES.in1
-rw-r--r--shared/nm-meta-setting.c5
-rw-r--r--shared/nm-meta-setting.h1
-rw-r--r--shared/nm-utils/nm-hash-utils.h9
-rw-r--r--shared/nm-utils/nm-macros-internal.h17
-rw-r--r--src/devices/nm-device.c142
-rw-r--r--src/platform/nm-fake-platform.c7
-rw-r--r--src/platform/nm-linux-platform.c209
-rw-r--r--src/platform/nm-platform.c90
-rw-r--r--src/platform/nm-platform.h27
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c72
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c41
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c111
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h20
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov19
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c150
46 files changed, 3193 insertions, 164 deletions
diff --git a/Makefile.am b/Makefile.am
index 53235df7e1..45074fa1e7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -432,6 +432,7 @@ libnm_core_lib_h_pub_real = \
libnm-core/nm-setting-pppoe.h \
libnm-core/nm-setting-proxy.h \
libnm-core/nm-setting-serial.h \
+ libnm-core/nm-setting-sriov.h \
libnm-core/nm-setting-tc-config.h \
libnm-core/nm-setting-team-port.h \
libnm-core/nm-setting-team.h \
@@ -502,6 +503,7 @@ libnm_core_lib_c_settings_real = \
libnm-core/nm-setting-pppoe.c \
libnm-core/nm-setting-proxy.c \
libnm-core/nm-setting-serial.c \
+ libnm-core/nm-setting-sriov.c \
libnm-core/nm-setting-tc-config.c \
libnm-core/nm-setting-team-port.c \
libnm-core/nm-setting-team.c \
@@ -2301,6 +2303,8 @@ EXTRA_DIST += \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-permissions \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-read-proxy-basic \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sit-ignore \
+ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov \
+ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc \
diff --git a/clients/cli/connections.c b/clients/cli/connections.c
index c8af47c0f6..9e6859dad4 100644
--- a/clients/cli/connections.c
+++ b/clients/cli/connections.c
@@ -769,7 +769,8 @@ const NmcMetaGenericInfo *const metagen_con_active_vpn[_NMC_GENERIC_INFO_TYPE_CO
NM_SETTING_WPAN_SETTING_NAME","\
NM_SETTING_6LOWPAN_SETTING_NAME","\
NM_SETTING_PROXY_SETTING_NAME"," \
- NM_SETTING_TC_CONFIG_SETTING_NAME
+ NM_SETTING_TC_CONFIG_SETTING_NAME"," \
+ NM_SETTING_SRIOV_SETTING_NAME
// NM_SETTING_DUMMY_SETTING_NAME
// NM_SETTING_WIMAX_SETTING_NAME
diff --git a/clients/common/nm-client-utils.c b/clients/common/nm-client-utils.c
index 058ddae39a..2e3d45ca38 100644
--- a/clients/common/nm-client-utils.c
+++ b/clients/common/nm-client-utils.c
@@ -351,6 +351,7 @@ NM_UTILS_LOOKUP_STR_DEFINE (nmc_device_reason_to_string, NMDeviceStateReason,
NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED, N_("OpenVSwitch database connection failed")),
NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE, N_("A duplicate IP address was detected")),
NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED, N_("The selected IP method is not supported")),
+ NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED, N_("Failed to configure SR-IOV parameters")),
)
NM_UTILS_LOOKUP_STR_DEFINE (nm_active_connection_state_reason_to_string, NMActiveConnectionStateReason,
diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c
index 519f3c856c..a52fe3ee38 100644
--- a/clients/common/nm-meta-setting-desc.c
+++ b/clients/common/nm-meta-setting-desc.c
@@ -3775,6 +3775,37 @@ _validate_fcn_proxy_pac_script (const char *value, char **out_to_free, GError **
}
static gconstpointer
+_get_fcn_sriov_vfs (ARGS_GET_FCN)
+{
+ NMSettingSriov *s_sriov = NM_SETTING_SRIOV (setting);
+ GString *printable;
+ guint num_vfs, i;
+ NMSriovVF *vf;
+ char *str;
+
+ RETURN_UNSUPPORTED_GET_TYPE ();
+
+ printable = g_string_new (NULL);
+
+ num_vfs = nm_setting_sriov_get_num_vfs (s_sriov);
+ for (i = 0; i < num_vfs; i++) {
+ vf = nm_setting_sriov_get_vf (s_sriov, i);
+
+ if (printable->len > 0)
+ g_string_append (printable, ", ");
+
+ str = nm_utils_sriov_vf_to_str (vf, FALSE, NULL);
+ if (str) {
+ g_string_append (printable, str);
+ g_free (str);
+ }
+ }
+
+ NM_SET_OUT (out_is_default, num_vfs == 0);
+ RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
+}
+
+static gconstpointer
_get_fcn_tc_config_qdiscs (ARGS_GET_FCN)
{
NMSettingTCConfig *s_tc = NM_SETTING_TC_CONFIG (setting);
@@ -3806,6 +3837,28 @@ _get_fcn_tc_config_qdiscs (ARGS_GET_FCN)
}
static gboolean
+_set_fcn_sriov_vfs (ARGS_SET_FCN)
+{
+ gs_free const char **strv = NULL;
+ const char *const*iter;
+ NMSriovVF *vf;
+ GError *local = NULL;
+
+ strv = nm_utils_strsplit_set (value, ",");
+ for (iter = strv; strv && *iter; iter++) {
+ vf = nm_utils_sriov_vf_from_str (*iter, &local);
+ if (!vf) {
+ g_set_error (error, 1, 0, "%s. %s", local->message,
+ _("The valid syntax is: vf [attribute=value]... [,vf [attribute=value]...]"));
+ return FALSE;
+ }
+ nm_setting_sriov_add_vf (NM_SETTING_SRIOV (setting), vf);
+ nm_sriov_vf_unref (vf);
+ }
+ return TRUE;
+}
+
+static gboolean
_set_fcn_tc_config_qdiscs (ARGS_SET_FCN)
{
gs_free const char **strv = NULL;
@@ -3828,6 +3881,34 @@ _set_fcn_tc_config_qdiscs (ARGS_SET_FCN)
}
static gboolean
+_validate_and_remove_sriov_vf (NMSettingSriov *setting,
+ const char *value,
+ GError **error)
+{
+ NMSriovVF *vf;
+ gboolean ret;
+
+ vf = nm_utils_sriov_vf_from_str (value, error);
+ if (!vf)
+ return FALSE;
+
+ ret = nm_setting_sriov_remove_vf_by_index (setting, nm_sriov_vf_get_index (vf));
+ if (!ret) {
+ g_set_error (error, 1, 0,
+ _("the property doesn't contain vf with index %u"),
+ nm_sriov_vf_get_index (vf));
+ }
+ nm_sriov_vf_unref (vf);
+ return ret;
+}
+DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_sriov_vfs,
+ NM_SETTING_SRIOV,
+ nm_setting_sriov_get_num_vfs,
+ nm_setting_sriov_remove_vf,
+ _validate_and_remove_sriov_vf)
+
+
+static gboolean
_validate_and_remove_tc_qdisc (NMSettingTCConfig *setting,
const char *value,
GError **error)
@@ -6832,6 +6913,25 @@ static const NMMetaPropertyInfo *const property_infos_SERIAL[] = {
};
#undef _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_SRIOV
+static const NMMetaPropertyInfo *const property_infos_SRIOV[] = {
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_TOTAL_VFS,
+ .property_type = &_pt_gobject_int,
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_VFS,
+ .property_type = DEFINE_PROPERTY_TYPE (
+ .get_fcn = _get_fcn_sriov_vfs,
+ .set_fcn = _set_fcn_sriov_vfs,
+ .remove_fcn = _remove_fcn_sriov_vfs,
+ ),
+ ),
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS,
+ .property_type = &_pt_gobject_enum,
+ ),
+ NULL
+};
+
+#undef _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TUN
static const NMMetaPropertyInfo *const property_infos_TUN[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_TUN_MODE,
@@ -7649,6 +7749,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
#define SETTING_PRETTY_NAME_PPPOE N_("PPPoE")
#define SETTING_PRETTY_NAME_PROXY N_("Proxy")
#define SETTING_PRETTY_NAME_SERIAL N_("Serial settings")
+#define SETTING_PRETTY_NAME_SRIOV N_("SR-IOV settings")
#define SETTING_PRETTY_NAME_TC_CONFIG N_("Traffic controls")
#define SETTING_PRETTY_NAME_TEAM N_("Team device")
#define SETTING_PRETTY_NAME_TEAM_PORT N_("Team port")
@@ -7765,6 +7866,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
.valid_parts = NM_META_SETTING_VALID_PARTS (
NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
NM_META_SETTING_VALID_PART_ITEM (INFINIBAND, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (SRIOV, FALSE),
),
.setting_init_fcn = _setting_init_fcn_infiniband,
),
@@ -7844,6 +7946,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
.setting_init_fcn = _setting_init_fcn_proxy,
),
SETTING_INFO (SERIAL),
+ SETTING_INFO (SRIOV),
SETTING_INFO (TC_CONFIG),
SETTING_INFO (TEAM,
.valid_parts = NM_META_SETTING_VALID_PARTS (
@@ -7896,6 +7999,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
NM_META_SETTING_VALID_PART_ITEM (WIRED, TRUE),
NM_META_SETTING_VALID_PART_ITEM (802_1X, FALSE),
NM_META_SETTING_VALID_PART_ITEM (DCB, FALSE),
+ NM_META_SETTING_VALID_PART_ITEM (SRIOV, FALSE),
),
),
SETTING_INFO (WIRELESS,
diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in
index bde1d1900a..886c8f0c70 100644
--- a/clients/common/settings-docs.h.in
+++ b/clients/common/settings-docs.h.in
@@ -323,8 +323,12 @@
#define DESCRIBE_DOC_NM_SETTING_SERIAL_PARITY N_("Parity setting of the serial port.")
#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 NM_TERNARY_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 NM_TERNARY_FALSE (0), VFs will not be claimed and no network interfaces will be created for them. When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_TRUE (1).")
+#define DESCRIBE_DOC_NM_SETTING_SRIOV_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".")
+#define DESCRIBE_DOC_NM_SETTING_SRIOV_TOTAL_VFS N_("The total number of virtual functions to create.")
+#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\". The \"vlans\" attribute is represented as a semicolor-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_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".")
-#define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS N_("Array of TC queuening disciplines.")
+#define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS N_("Array of TC queueing disciplines.")
#define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_TFILTERS N_("Array of TC traffic filters.")
#define DESCRIBE_DOC_NM_SETTING_TEAM_CONFIG N_("The JSON configuration for the team network interface. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details.")
#define DESCRIBE_DOC_NM_SETTING_TEAM_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-incative', 'send-always'. See teamd.conf man for more details.")
diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml
index 14d9636e26..bdb3c2daec 100644
--- a/docs/libnm/libnm-docs.xml
+++ b/docs/libnm/libnm-docs.xml
@@ -220,6 +220,7 @@ print ("NetworkManager version " + client.get_version())]]></programlisting></in
<xi:include href="xml/nm-setting-ppp.xml"/>
<xi:include href="xml/nm-setting-proxy.xml"/>
<xi:include href="xml/nm-setting-serial.xml"/>
+ <xi:include href="xml/nm-setting-sriov.xml"/>
<xi:include href="xml/nm-setting-tc-config.xml"/>
<xi:include href="xml/nm-setting-team-port.xml"/>
<xi:include href="xml/nm-setting-team.xml"/>
diff --git a/libnm-core/meson.build b/libnm-core/meson.build
index 39ea608c44..edee801ab5 100644
--- a/libnm-core/meson.build
+++ b/libnm-core/meson.build
@@ -34,6 +34,7 @@ libnm_core_headers = files(
'nm-setting-pppoe.h',
'nm-setting-proxy.h',
'nm-setting-serial.h',
+ 'nm-setting-sriov.h',
'nm-setting-tc-config.h',
'nm-setting-team-port.h',
'nm-setting-team.h',
@@ -86,6 +87,7 @@ libnm_core_settings_sources = files(
'nm-setting-pppoe.c',
'nm-setting-proxy.c',
'nm-setting-serial.c',
+ 'nm-setting-sriov.c',
'nm-setting-tc-config.c',
'nm-setting-team-port.c',
'nm-setting-team.c',
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index 07b72c169c..2f8cb4e905 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -1178,6 +1178,18 @@ _normalize_ip_tunnel_wired_setting (NMConnection *self, GHashTable *parameters)
}
static gboolean
+_normalize_sriov_vf_order (NMConnection *self, GHashTable *parameters)
+{
+ NMSettingSriov *s_sriov;
+
+ s_sriov = nm_connection_get_setting_sriov (self);
+ if (!s_sriov)
+ return FALSE;
+
+ return _nm_setting_sriov_sort_vfs (s_sriov);
+}
+
+static gboolean
_normalize_required_settings (NMConnection *self, GHashTable *parameters)
{
NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self);
@@ -1523,6 +1535,7 @@ nm_connection_normalize (NMConnection *connection,
was_modified |= _normalize_bluetooth_type (connection, parameters);
was_modified |= _normalize_ovs_interface_type (connection, parameters);
was_modified |= _normalize_ip_tunnel_wired_setting (connection, parameters);
+ was_modified |= _normalize_sriov_vf_order (connection, parameters);
/* Verify anew. */
success = _nm_connection_verify (connection, error);
@@ -2627,6 +2640,22 @@ nm_connection_get_setting_serial (NMConnection *connection)
}
/**
+ * nm_connection_get_setting_sriov:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingSriov the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingSriov if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.14
+ **/
+NMSettingSriov *
+nm_connection_get_setting_sriov (NMConnection *connection)
+{
+ return _connection_get_setting_check (connection, NM_TYPE_SETTING_SRIOV);
+}
+
+/**
* nm_connection_get_setting_tc_config:
* @connection: the #NMConnection
*
diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h
index 8f65e9fce9..312760f157 100644
--- a/libnm-core/nm-connection.h
+++ b/libnm-core/nm-connection.h
@@ -231,6 +231,8 @@ NMSettingPppoe * nm_connection_get_setting_pppoe (NMConnec
NM_AVAILABLE_IN_1_6
NMSettingProxy * nm_connection_get_setting_proxy (NMConnection *connection);
NMSettingSerial * nm_connection_get_setting_serial (NMConnection *connection);
+NM_AVAILABLE_IN_1_14
+NMSettingSriov * nm_connection_get_setting_sriov (NMConnection *connection);
NM_AVAILABLE_IN_1_12
NMSettingTCConfig * nm_connection_get_setting_tc_config (NMConnection *connection);
NMSettingTun * nm_connection_get_setting_tun (NMConnection *connection);
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 8806e2ba53..3808a9e61b 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -65,6 +65,7 @@
#include "nm-setting-ppp.h"
#include "nm-setting-pppoe.h"
#include "nm-setting-serial.h"
+#include "nm-setting-sriov.h"
#include "nm-setting-tc-config.h"
#include "nm-setting-team-port.h"
#include "nm-setting-team.h"
@@ -226,6 +227,9 @@ gboolean _nm_ip_route_attribute_validate_all (const NMIPRoute *route);
const char **_nm_ip_route_get_attribute_names (const NMIPRoute *route, gboolean sorted, guint *out_length);
GHashTable *_nm_ip_route_get_attributes_direct (NMIPRoute *route);
+NMSriovVF *_nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError **error);
+gboolean _nm_sriov_vf_attribute_validate_all (const NMSriovVF *vf, GError **error);
+
static inline void
_nm_auto_ip_route_unref (NMIPRoute **v)
{
@@ -513,4 +517,7 @@ _nm_connection_type_is_master (const char *type)
gboolean _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin);
/*****************************************************************************/
+
+gboolean _nm_setting_sriov_sort_vfs (NMSettingSriov *setting);
+
#endif
diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h
index 73ba579489..622b104f60 100644
--- a/libnm-core/nm-core-types.h
+++ b/libnm-core/nm-core-types.h
@@ -58,6 +58,7 @@ typedef struct _NMSettingOvsPort NMSettingOvsPort;
typedef struct _NMSettingPpp NMSettingPpp;
typedef struct _NMSettingPppoe NMSettingPppoe;
typedef struct _NMSettingSerial NMSettingSerial;
+typedef struct _NMSettingSriov NMSettingSriov;
typedef struct _NMSettingTCConfig NMSettingTCConfig;
typedef struct _NMSettingTeam NMSettingTeam;
typedef struct _NMSettingTeamPort NMSettingTeamPort;
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index 94b4c28d75..388898dd96 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -559,6 +559,7 @@ typedef enum {
* @NM_DEVICE_STATE_REASON_OVSDB_FAILED: problem communicating with Open vSwitch database
* @NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE: a duplicate IP address was detected
* @NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED: The selected IP method is not supported
+ * @NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED: configuration of SR-IOV parameters failed
*
* Device state change reason codes
*/
@@ -629,6 +630,7 @@ typedef enum {
NM_DEVICE_STATE_REASON_OVSDB_FAILED = 63,
NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE = 64,
NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED = 65,
+ NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED = 66,
} NMDeviceStateReason;
/**
@@ -991,4 +993,20 @@ typedef enum { /*< flags >*/
NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT = (1LL << 5),
} NMSettingsUpdate2Flags;
+/**
+ * NMTernary:
+ * @NM_TERNARY_DEFAULT: use the globally-configured default value.
+ * @NM_TERNARY_FALSE: the option is disabled.
+ * @NM_TERNARY_TRUE: the option is enabled.
+ *
+ * An boolean value that can be overridden by a default.
+ *
+ * Since: 1.14
+ **/
+typedef enum {
+ NM_TERNARY_DEFAULT = -1,
+ NM_TERNARY_FALSE = 0,
+ NM_TERNARY_TRUE = 1,
+} NMTernary;
+
#endif /* __NM_DBUS_INTERFACE_H__ */
diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c
index 17741f5f14..dbc0b187f1 100644
--- a/libnm-core/nm-keyfile.c
+++ b/libnm-core/nm-keyfile.c
@@ -119,6 +119,49 @@ setting_alias_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *k
}
static void
+sriov_vfs_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ const char *setting_name = nm_setting_get_name (setting);
+ gs_unref_ptrarray GPtrArray *vfs = NULL;
+ gs_strfreev char **keys = NULL;
+ gsize n_keys = 0;
+ int i;
+
+ keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
+ if (!keys || n_keys == 0)
+ return;
+
+ vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+
+ for (i = 0; i < n_keys; i++) {
+ gs_free char *value = NULL;
+ NMSriovVF *vf;
+ const char *rest;
+
+ if (!g_str_has_prefix (keys[i], "vf."))
+ continue;
+
+ rest = &keys[i][3];
+
+ if (!NM_STRCHAR_ALL (rest, ch, g_ascii_isdigit (ch)))
+ continue;
+
+ value = nm_keyfile_plugin_kf_get_string (info->keyfile,
+ setting_name,
+ keys[i],
+ NULL);
+
+ vf = _nm_utils_sriov_vf_from_strparts (rest, value, NULL);
+ if (vf)
+ g_ptr_array_add (vfs, vf);
+ }
+
+ g_object_set (G_OBJECT (setting),
+ key, vfs,
+ NULL);
+}
+
+static void
read_array_of_uint (GKeyFile *file,
NMSetting *setting,
const char *key)
@@ -1510,6 +1553,37 @@ setting_alias_writer (KeyfileWriterInfo *info,
}
static void
+sriov_vfs_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ GPtrArray *vfs;
+ guint i;
+
+ vfs = g_value_get_boxed (value);
+ if (!vfs)
+ return;
+
+ for (i = 0; i < vfs->len; i++) {
+ const NMSriovVF *vf = vfs->pdata[i];
+ gs_free char *kf_value = NULL;
+ char kf_key[32];
+
+ kf_value = nm_utils_sriov_vf_to_str (vf, TRUE, NULL);
+ if (!kf_value)
+ continue;
+
+ nm_sprintf_buf (kf_key, "vf.%u", nm_sriov_vf_get_index (vf));
+
+ nm_keyfile_plugin_kf_set_string (info->keyfile,
+ nm_setting_get_name (setting),
+ kf_key,
+ kf_value);
+ }
+}
+
+static void
write_array_of_uint (GKeyFile *file,
NMSetting *setting,
const char *key,
@@ -2189,6 +2263,15 @@ static const ParseInfoSetting parse_infos[] = {
),
),
),
+ PARSE_INFO_SETTING (NM_SETTING_SRIOV_SETTING_NAME,
+ PARSE_INFO_PROPERTIES (
+ PARSE_INFO_PROPERTY (NM_SETTING_SRIOV_VFS,
+ .parser_no_check_key = TRUE,
+ .parser = sriov_vfs_parser,
+ .writer = sriov_vfs_writer,
+ ),
+ ),
+ ),
PARSE_INFO_SETTING (NM_SETTING_TC_CONFIG_SETTING_NAME,
PARSE_INFO_PROPERTIES (
PARSE_INFO_PROPERTY (NM_SETTING_TC_CONFIG_QDISCS,
diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c
index 8e2df0c321..fa5aa60f1a 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -1589,8 +1589,11 @@ nm_setting_ip_config_clear_dns (NMSettingIPConfig *setting)
g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
- g_ptr_array_set_size (priv->dns, 0);
- g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS);
+
+ if (priv->dns->len != 0) {
+ g_ptr_array_set_size (priv->dns, 0);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS);
+ }
}
/**
@@ -1727,8 +1730,11 @@ nm_setting_ip_config_clear_dns_searches (NMSettingIPConfig *setting)
g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
- g_ptr_array_set_size (priv->dns_search, 0);
- g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH);
+
+ if (priv->dns_search->len != 0) {
+ g_ptr_array_set_size (priv->dns_search, 0);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH);
+ }
}
/**
@@ -2107,8 +2113,10 @@ nm_setting_ip_config_clear_addresses (NMSettingIPConfig *setting)
g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
- g_ptr_array_set_size (priv->addresses, 0);
- g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
+ if (priv->addresses->len != 0) {
+ g_ptr_array_set_size (priv->addresses, 0);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
+ }
}
/**
@@ -2264,8 +2272,10 @@ nm_setting_ip_config_clear_routes (NMSettingIPConfig *setting)
g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
- g_ptr_array_set_size (priv->routes, 0);
- g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
+ if (priv->routes->len != 0) {
+ g_ptr_array_set_size (priv->routes, 0);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
+ }
}
/**
diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c
index c7d80a9fdc..a88a3f2b9c 100644
--- a/libnm-core/nm-setting-ip4-config.c
+++ b/libnm-core/nm-setting-ip4-config.c
@@ -746,7 +746,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
/* ---ifcfg-rh---
* property: dad-timeout
- * variable: ACD_TIMEOUT, ARPING_WAIT
+ * variable: ACD_TIMEOUT(+), ARPING_WAIT
* default: missing variable means global default (config override or zero)
* description: Timeout (in milliseconds for ACD_TIMEOUT or in seconds
* for ARPING_WAIT) for address conflict detection before configuring
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
index d483d1921a..76a68aa085 100644
--- a/libnm-core/nm-setting-ip6-config.c
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -589,7 +589,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
/* ---ifcfg-rh---
* property: dns-search
- * variable: IPV6_DOMAIN
+ * variable: IPV6_DOMAIN(+)
* format: string (space-separated domains)
* description: List of DNS search domains.
* ---end---
diff --git a/libnm-core/nm-setting-sriov.c b/libnm-core/nm-setting-sriov.c
new file mode 100644
index 0000000000..29161c8cd9
--- /dev/null
+++ b/libnm-core/nm-setting-sriov.c
@@ -0,0 +1,1353 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-sriov.h"
+#include "nm-setting-private.h"
+#include "nm-utils-private.h"
+
+/**
+ * SECTION:nm-setting-sriov
+ * @short_description: Describes SR-IOV connection properties
+ * @include: nm-setting-sriov.h
+ **/
+
+/**
+ * NMSettingSriov:
+ *
+ * SR-IOV settings.
+ *
+ * Since: 1.14
+ */
+struct _NMSettingSriov {
+ NMSetting parent;
+ GPtrArray *vfs;
+ guint total_vfs;
+ NMTernary autoprobe_drivers;
+};
+
+struct _NMSettingSriovClass {
+ NMSettingClass parent;
+};
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingSriov, nm_setting_sriov, NM_TYPE_SETTING,
+ _nm_register_setting (SRIOV, NM_SETTING_PRIORITY_HW_AUX))
+
+enum {
+ PROP_0,
+ PROP_TOTAL_VFS,
+ PROP_VFS,
+ PROP_AUTOPROBE_DRIVERS,
+
+ LAST_PROP
+};
+
+/*****************************************************************************/
+
+G_DEFINE_BOXED_TYPE (NMSriovVF, nm_sriov_vf, nm_sriov_vf_dup, nm_sriov_vf_unref)
+
+struct _NMSriovVF {
+ guint refcount;
+ guint index;
+ GHashTable *attributes;
+ GHashTable *vlans;
+ guint *vlan_ids;
+};
+
+typedef struct {
+ guint id;
+ guint qos;
+ NMSriovVFVlanProtocol protocol;
+} VFVlan;
+
+static guint
+_vf_vlan_hash (gconstpointer ptr)
+{
+ return nm_hash_val (1348254767u, *((guint *) ptr));
+}
+
+static gboolean
+_vf_vlan_equal (gconstpointer a, gconstpointer b)
+{
+ return *((guint *) a) == *((guint *) b);
+}
+
+static GHashTable *
+_vf_vlan_create_hash (void)
+{
+ G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (VFVlan, id) == 0);
+ return g_hash_table_new_full (_vf_vlan_hash,
+ _vf_vlan_equal,
+ NULL,
+ nm_g_slice_free_fcn (VFVlan));
+}
+
+/**
+ * nm_srio_vf_new:
+ * @index: the VF index
+ *
+ * Creates a new #NMSriovVF object.
+ *
+ * Returns: (transfer full): the new #NMSriovVF object.
+ *
+ * Since: 1.14
+ **/
+NMSriovVF *
+nm_sriov_vf_new (guint index)
+{
+ NMSriovVF *vf;
+
+ vf = g_slice_new0 (NMSriovVF);
+ vf->refcount = 1;
+ vf->index = index;
+ vf->attributes = g_hash_table_new_full (nm_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) g_variant_unref);
+ return vf;
+}
+
+/**
+ * nm_sriov_vf_ref:
+ * @vf: the #NMSriovVF
+ *
+ * Increases the reference count of the object.
+ *
+ * Since: 1.14
+ **/
+void
+nm_sriov_vf_ref (NMSriovVF *vf)
+{
+ g_return_if_fail (vf);
+ g_return_if_fail (vf->refcount > 0);
+
+ vf->refcount++;
+}
+
+/**
+ * nm_sriov_vf_unref:
+ * @vf: the #NMSriovVF
+ *
+ * Decreases the reference count of the object. If the reference count
+ * reaches zero, the object will be destroyed.
+ *
+ * Since: 1.14
+ **/
+void
+nm_sriov_vf_unref (NMSriovVF *vf)
+{
+ g_return_if_fail (vf);
+ g_return_if_fail (vf->refcount > 0);
+
+ vf->refcount--;
+ if (vf->refcount == 0) {
+ g_hash_table_unref (vf->attributes);
+ if (vf->vlans)
+ g_hash_table_unref (vf->vlans);
+ g_free (vf->vlan_ids);
+ g_slice_free (NMSriovVF, vf);
+ }
+}
+
+/**
+ * nm_sriov_vf_equal:
+ * @vf: the #NMSriovVF
+ * @other: the #NMSriovVF to compare @vf to.
+ *
+ * Determines if two #NMSriovVF objects have the same index,
+ * attributes and VLANs.
+ *
+ * Returns: %TRUE if the objects contain the same values, %FALSE
+ * if they do not.
+ *
+ * Since: 1.14
+ **/
+gboolean
+nm_sriov_vf_equal (const NMSriovVF *vf, const NMSriovVF *other)
+{
+ GHashTableIter iter;
+ const char *key;
+ GVariant *value, *value2;
+ VFVlan *vlan, *vlan2;
+ guint n_vlans;
+
+ g_return_val_if_fail (vf, FALSE);
+ g_return_val_if_fail (vf->refcount > 0, FALSE);
+ g_return_val_if_fail (other, FALSE);
+ g_return_val_if_fail (other->refcount > 0, FALSE);
+
+ if (vf == other)
+ return TRUE;
+
+ if (vf->index != other->index)
+ return FALSE;
+
+ if (g_hash_table_size (vf->attributes) != g_hash_table_size (other->attributes))
+ return FALSE;
+ g_hash_table_iter_init (&iter, vf->attributes);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
+ value2 = g_hash_table_lookup (other->attributes, key);
+ if (!value2)
+ return FALSE;
+ if (!g_variant_equal (value, value2))
+ return FALSE;
+ }
+
+ n_vlans = vf->vlans ? g_hash_table_size (vf->vlans) : 0u;
+ if (n_vlans != (other->vlans ? g_hash_table_size (other->vlans) : 0u))
+ return FALSE;
+ if (n_vlans > 0) {
+ g_hash_table_iter_init (&iter, vf->vlans);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &vlan, NULL)) {
+ vlan2 = g_hash_table_lookup (other->vlans, vlan);
+ if (!vlan2)
+ return FALSE;
+ if ( vlan->qos != vlan2->qos
+ || vlan->protocol != vlan2->protocol)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+vf_add_vlan (NMSriovVF *vf,
+ guint vlan_id,
+ guint qos,
+ NMSriovVFVlanProtocol protocol)
+{
+ VFVlan *vlan;
+
+ vlan = g_slice_new0 (VFVlan);
+ vlan->id = vlan_id;
+ vlan->qos = qos;
+ vlan->protocol = protocol;
+
+ if (!vf->vlans)
+ vf->vlans = _vf_vlan_create_hash ();
+
+ g_hash_table_add (vf->vlans, vlan);
+ g_clear_pointer (&vf->vlan_ids, g_free);
+}
+
+/**
+ * nm_sriov_vf_dup:
+ * @vf: the #NMSriovVF
+ *
+ * Creates a copy of @vf.
+ *
+ * Returns: (transfer full): a copy of @vf
+ *
+ * Since: 1.14
+ **/
+NMSriovVF *
+nm_sriov_vf_dup (const NMSriovVF *vf)
+{
+ NMSriovVF *copy;
+ GHashTableIter iter;
+ const char *name;
+ GVariant *variant;
+ VFVlan *vlan;
+
+ g_return_val_if_fail (vf, NULL);
+ g_return_val_if_fail (vf->refcount > 0, NULL);
+
+ copy = nm_sriov_vf_new (vf->index);
+
+ g_hash_table_iter_init (&iter, vf->attributes);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant))
+ nm_sriov_vf_set_attribute (copy, name, variant);
+
+ if (vf->vlans) {
+ g_hash_table_iter_init (&iter, vf->vlans);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &vlan, NULL))
+ vf_add_vlan (copy, vlan->id, vlan->qos, vlan->protocol);
+ }
+
+ return copy;
+}
+
+/**
+ * nm_sriov_vf_get_index:
+ * @vf: the #NMSriovVF
+ *
+ * Gets the index property of this VF object.
+ *
+ * Returns: the VF index
+ *
+ * Since: 1.14
+ **/
+guint
+nm_sriov_vf_get_index (const NMSriovVF *vf)
+{
+ g_return_val_if_fail (vf, 0);
+ g_return_val_if_fail (vf->refcount > 0, 0);
+
+ return vf->index;
+}
+
+/**
+ * nm_sriov_vf_set_attribute:
+ * @vf: the #NMSriovVF
+ * @name: the name of a route attribute
+ * @value: (transfer none) (allow-none): the value
+ *
+ * Sets the named attribute on @vf to the given value.
+ *
+ * Since: 1.14
+ **/
+void
+nm_sriov_vf_set_attribute (NMSriovVF *vf, const char *name, GVariant *value)
+{
+ g_return_if_fail (vf);
+ g_return_if_fail (vf->refcount > 0);
+ g_return_if_fail (name && *name != '\0');
+ g_return_if_fail (!nm_streq (name, "index"));
+
+ if (value) {
+ g_hash_table_insert (vf->attributes,
+ g_strdup (name),
+ g_variant_ref_sink (value));
+ } else
+ g_hash_table_remove (vf->attributes, name);
+}
+
+/**
+ * nm_sriov_vf_get_attribute_names:
+ * @vf: the #NMSriovVF
+ *
+ * Gets an array of attribute names defined on @vf.
+ *
+ * Returns: (transfer container): a %NULL-terminated array of attribute names
+ *
+ * Since: 1.14
+ **/
+const char **
+nm_sriov_vf_get_attribute_names (const NMSriovVF *vf)
+{
+ g_return_val_if_fail (vf, NULL);
+ g_return_val_if_fail (vf->refcount > 0, NULL);
+
+ return nm_utils_strdict_get_keys (vf->attributes, TRUE, NULL);
+}
+
+/**
+ * nm_sriov_vf_get_attribute:
+ * @vf: the #NMSriovVF
+ * @name: the name of a VF attribute
+ *
+ * Gets the value of the attribute with name @name on @vf
+ *
+ * Returns: (transfer none): the value of the attribute with name @name on
+ * @vf, or %NULL if @vf has no such attribute.
+ *
+ * Since: 1.14
+ **/
+GVariant *
+nm_sriov_vf_get_attribute (const NMSriovVF *vf, const char *name)
+{
+ g_return_val_if_fail (vf, NULL);
+ g_return_val_if_fail (vf->refcount > 0, NULL);
+ g_return_val_if_fail (name && *name != '\0', NULL);
+
+ return g_hash_table_lookup (vf->attributes, name);
+}
+
+#define SRIOV_ATTR_SPEC_PTR(name, type, str_type) \
+ &(NMVariantAttributeSpec) { name, type, FALSE, FALSE, FALSE, FALSE, str_type }
+
+const NMVariantAttributeSpec * const _nm_sriov_vf_attribute_spec[] = {
+ SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_MAC, G_VARIANT_TYPE_STRING, 'm'),
+ SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, G_VARIANT_TYPE_BOOLEAN, 0),
+ SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_TRUST, G_VARIANT_TYPE_BOOLEAN, 0),
+ SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, G_VARIANT_TYPE_UINT32, 0),
+ SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, G_VARIANT_TYPE_UINT32, 0),
+ /* D-Bus only, synthetic attributes */
+ SRIOV_ATTR_SPEC_PTR ("vlans", G_VARIANT_TYPE_STRING, 'd'),
+ NULL,
+};
+
+/**
+ * nm_sriov_vf_attribute_validate:
+ * @name: the attribute name
+ * @value: the attribute value
+ * @known: (out): on return, whether the attribute name is a known one
+ * @error: (allow-none): return location for a #GError, or %NULL
+ *
+ * Validates a VF attribute, i.e. checks that the attribute is a known one,
+ * the value is of the correct type and well-formed.
+ *
+ * Returns: %TRUE if the attribute is valid, %FALSE otherwise
+ *
+ * Since: 1.14
+ */
+gboolean
+nm_sriov_vf_attribute_validate (const char *name,
+ GVariant *value,
+ gboolean *known,
+ GError **error)
+{
+ const NMVariantAttributeSpec *const *iter;
+ const NMVariantAttributeSpec *spec = NULL;
+
+ g_return_val_if_fail (name, FALSE);
+ g_return_val_if_fail (value, FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+
+ for (iter = _nm_sriov_vf_attribute_spec; *iter; iter++) {
+ if (nm_streq (name, (*iter)->name)) {
+ spec = *iter;
+ break;
+ }
+ }
+
+ if (!spec || spec->str_type == 'd') {
+ NM_SET_OUT (known, FALSE);
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ _("unknown attribute"));
+ return FALSE;
+ }
+
+ NM_SET_OUT (known, TRUE);
+
+ if (!g_variant_is_of_type (value, spec->type)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ _("invalid attribute type '%s'"),
+ g_variant_get_type_string (value));
+ return FALSE;
+ }
+
+ if (spec->type == G_VARIANT_TYPE_STRING) {
+ const char *string;
+
+ switch (spec->str_type) {
+ case 'm': /* MAC address */
+ string = g_variant_get_string (value, NULL);
+ if (!nm_utils_hwaddr_valid (string, -1)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ _("'%s' is not a valid MAC address"),
+ string);
+ return FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_nm_sriov_vf_attribute_validate_all (const NMSriovVF *vf, GError **error)
+{
+ GHashTableIter iter;
+ const char *name;
+ GVariant *variant;
+ GVariant *min, *max;
+
+ g_return_val_if_fail (vf, FALSE);
+ g_return_val_if_fail (vf->refcount > 0, FALSE);
+
+ g_hash_table_iter_init (&iter, vf->attributes);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) {
+ if (!nm_sriov_vf_attribute_validate (name, variant, NULL, error)) {
+ g_prefix_error (error, "attribute '%s':", name);
+ return FALSE;
+ }
+ }
+
+ min = g_hash_table_lookup (vf->attributes, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE);
+ max = g_hash_table_lookup (vf->attributes, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE);
+ if ( min
+ && max
+ && g_variant_get_uint32 (min) > g_variant_get_uint32 (max)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ "min_tx_rate is greater than max_tx_rate");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * nm_sriov_vf_add_vlan:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ *
+ * Adds a VLAN to the VF.
+ *
+ * Returns: %TRUE if the VLAN was added; %FALSE if it already existed
+ *
+ * Since: 1.14
+ **/
+gboolean
+nm_sriov_vf_add_vlan (NMSriovVF *vf, guint vlan_id)
+{
+ g_return_val_if_fail (vf, FALSE);
+ g_return_val_if_fail (vf->refcount > 0, FALSE);
+
+ if ( vf->vlans
+ && g_hash_table_contains (vf->vlans, &vlan_id))
+ return FALSE;
+
+ vf_add_vlan (vf, vlan_id, 0, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+
+ return TRUE;
+}
+
+/**
+ * nm_sriov_vf_remove_vlan:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ *
+ * Removes a VLAN from a VF.
+ *
+ * Returns: %TRUE if the VLAN was removed, %FALSE if the VLAN @vlan_id
+ * did not belong to the VF.
+ *
+ * Since: 1.14
+ */
+gboolean
+nm_sriov_vf_remove_vlan (NMSriovVF *vf, guint vlan_id)
+{
+ g_return_val_if_fail (vf, FALSE);
+ g_return_val_if_fail (vf->refcount > 0, FALSE);
+
+ if ( !vf->vlans
+ || !g_hash_table_remove (vf->vlans, &vlan_id))
+ return FALSE;
+
+ g_clear_pointer (&vf->vlan_ids, g_free);
+ return TRUE;
+}
+
+static int
+vlan_id_compare (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ guint id_a = *(guint *) a;
+ guint id_b = *(guint *) b;
+
+ if (id_a < id_b)
+ return -1;
+ else if (id_a > id_b)
+ return 1;
+ else return 0;
+}
+
+/**
+ * nm_sriov_vf_get_vlan_ids:
+ * @vf: the #NMSriovVF
+ * @length: (out) (allow-none): on return, the number of VLANs configured
+ *
+ * Returns the VLANs currently configured on the VF.
+ *
+ * Returns: (transfer none): a list of VLAN ids configured on the VF.
+ *
+ * Since: 1.14
+ */
+const guint *
+nm_sriov_vf_get_vlan_ids (const NMSriovVF *vf, guint *length)
+{
+ GHashTableIter iter;
+ VFVlan *vlan;
+ guint num, i;
+
+ g_return_val_if_fail (vf, NULL);
+ g_return_val_if_fail (vf->refcount > 0, NULL);
+
+ num = vf->vlans ? g_hash_table_size (vf->vlans) : 0u;
+ NM_SET_OUT (length, num);
+
+ if (vf->vlan_ids)
+ return vf->vlan_ids;
+ if (num == 0)
+ return NULL;
+
+ /* vf is const, however, vlan_ids is a mutable field caching the
+ * result ("mutable" in C++ terminology) */
+ ((NMSriovVF *) vf)->vlan_ids = g_new0 (guint, num);
+
+ i = 0;
+ g_hash_table_iter_init (&iter, vf->vlans);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &vlan, NULL))
+ vf->vlan_ids[i++] = vlan->id;
+
+ nm_assert (num == i);
+
+ g_qsort_with_data (vf->vlan_ids, num, sizeof (guint), vlan_id_compare, NULL);
+
+ return vf->vlan_ids;
+}
+
+/**
+ * nm_sriov_vf_set_vlan_qos:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ * @qos: a QoS (priority) value
+ *
+ * Sets a QoS value for the given VLAN.
+ *
+ * Since: 1.14
+ */
+void
+nm_sriov_vf_set_vlan_qos (NMSriovVF *vf, guint vlan_id, guint32 qos)
+{
+ VFVlan *vlan;
+
+ g_return_if_fail (vf);
+ g_return_if_fail (vf->refcount > 0);
+
+ if ( !vf->vlans
+ || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id)))
+ g_return_if_reached ();
+
+ vlan->qos = qos;
+}
+
+/**
+ * nm_sriov_vf_set_vlan_protocol:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ * @protocol: the VLAN protocol
+ *
+ * Sets the protocol for the given VLAN.
+ *
+ * Since: 1.14
+ */
+void
+nm_sriov_vf_set_vlan_protocol (NMSriovVF *vf, guint vlan_id, NMSriovVFVlanProtocol protocol)
+{
+ VFVlan *vlan;
+
+ g_return_if_fail (vf);
+ g_return_if_fail (vf->refcount > 0);
+
+ if ( !vf->vlans
+ || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id)))
+ g_return_if_reached ();
+
+ vlan->protocol = protocol;
+}
+
+/**
+ * nm_sriov_vf_get_vlan_qos:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ *
+ * Returns the QoS value for the given VLAN.
+ *
+ * Returns: the QoS value
+ *
+ * Since: 1.14
+ */
+guint32
+nm_sriov_vf_get_vlan_qos (const NMSriovVF *vf, guint vlan_id)
+{
+ VFVlan *vlan;
+
+ g_return_val_if_fail (vf, 0);
+ g_return_val_if_fail (vf->refcount > 0, 0);
+
+ if ( !vf->vlans
+ || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id)))
+ g_return_val_if_reached (0);
+
+ return vlan->qos;
+}
+
+/*
+ * nm_sriov_vf_get_vlan_protocol:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ *
+ * Returns the configured protocol for the given VLAN.
+ *
+ * Returns: the configured protocol
+ *
+ * Since: 1.14
+ */
+NMSriovVFVlanProtocol
+nm_sriov_vf_get_vlan_protocol (const NMSriovVF *vf, guint vlan_id)
+{
+ VFVlan *vlan;
+
+ g_return_val_if_fail (vf, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+ g_return_val_if_fail (vf->refcount > 0, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+
+ if ( !vf->vlans
+ || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id)))
+ g_return_val_if_reached (NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+
+ return vlan->protocol;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_sriov_new:
+ *
+ * Creates a new #NMSettingSriov object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingSriov object
+ *
+ * Since: 1.14
+ **/
+NMSetting *
+nm_setting_sriov_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_SRIOV, NULL);
+}
+
+/**
+ * nm_setting_sriov_get_total_vfs:
+ * @setting: the #NMSettingSriov
+ *
+ * Returns the value contained in the #NMSettingSriov:total-vfs
+ * property.
+ *
+ * Returns: the total number of SR-IOV virtual functions to create
+ *
+ * Since: 1.14
+ **/
+guint
+nm_setting_sriov_get_total_vfs (NMSettingSriov *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), 0);
+
+ return setting->total_vfs;
+}
+
+/**
+ * nm_setting_sriov_get_num_vfs:
+ * @setting: the #NMSettingSriov
+ *
+ * Returns: the number of configured VFs
+ *
+ * Since: 1.14
+ **/
+guint
+nm_setting_sriov_get_num_vfs (NMSettingSriov *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), 0);
+
+ return setting->vfs->len;
+}
+
+/**
+ * nm_setting_sriov_get_vf:
+ * @setting: the #NMSettingSriov
+ * @idx: index number of the VF to return
+ *
+ * Returns: (transfer none): the VF at index @idx
+ *
+ * Since: 1.14
+ **/
+NMSriovVF *
+nm_setting_sriov_get_vf (NMSettingSriov *setting, guint idx)
+{
+ g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), NULL);
+ g_return_val_if_fail (idx < setting->vfs->len, NULL);
+
+ return setting->vfs->pdata[idx];
+}
+
+/**
+ * nm_setting_sriov_add_vf:
+ * @setting: the #NMSettingSriov
+ * @vf: the VF to add
+ *
+ * Appends a new VF and associated information to the setting. The
+ * given VF is duplicated internally and is not changed by this function.
+ *
+ * Since: 1.14
+ **/
+void
+nm_setting_sriov_add_vf (NMSettingSriov *setting, NMSriovVF *vf)
+{
+ g_return_if_fail (NM_IS_SETTING_SRIOV (setting));
+ g_return_if_fail (vf);
+ g_return_if_fail (vf->refcount > 0);
+
+ g_ptr_array_add (setting->vfs, nm_sriov_vf_dup (vf));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+}
+
+/**
+ * nm_setting_sriov_remove_vf:
+ * @setting: the #NMSettingSriov
+ * @idx: index number of the VF
+ *
+ * Removes the VF at index @idx.
+ *
+ * Since: 1.14
+ **/
+void
+nm_setting_sriov_remove_vf (NMSettingSriov *setting, guint idx)
+{
+ g_return_if_fail (NM_IS_SETTING_SRIOV (setting));
+ g_return_if_fail (idx < setting->vfs->len);
+
+ g_ptr_array_remove_index (setting->vfs, idx);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+}
+
+/**
+ * nm_setting_sriov_remove_vf_by_index:
+ * @setting: the #NMSettingSriov
+ * @index: the VF index of the VF to remove
+ *
+ * Removes the VF with VF index @index.
+ *
+ * Returns: %TRUE if the VF was found and removed; %FALSE if it was not
+ *
+ * Since: 1.14
+ **/
+gboolean
+nm_setting_sriov_remove_vf_by_index (NMSettingSriov *setting,
+ guint index)
+{
+ guint i;
+
+ g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), FALSE);
+
+ for (i = 0; i < setting->vfs->len; i++) {
+ if (nm_sriov_vf_get_index (setting->vfs->pdata[i]) == index) {
+ g_ptr_array_remove_index (setting->vfs, i);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_sriov_clear_vfs:
+ * @setting: the #NMSettingSriov
+ *
+ * Removes all configured VFs.
+ *
+ * Since: 1.14
+ **/
+void
+nm_setting_sriov_clear_vfs (NMSettingSriov *setting)
+{
+ g_return_if_fail (NM_IS_SETTING_SRIOV (setting));
+
+ if (setting->vfs->len != 0) {
+ g_ptr_array_set_size (setting->vfs, 0);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+ }
+}
+
+/**
+ * nm_setting_sriov_get_autoprobe_drivers:
+ * @setting: the #NMSettingSriov
+ *
+ * Returns the value contained in the #NMSettingSriov:autoprobe-drivers
+ * property.
+ *
+ * Returns: the autoprobe-drivers property value
+ *
+ * Since: 1.14
+ **/
+NMTernary
+nm_setting_sriov_get_autoprobe_drivers (NMSettingSriov *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), NM_TERNARY_DEFAULT);
+
+ return setting->autoprobe_drivers;
+}
+
+static gint
+vf_index_compare (gconstpointer a, gconstpointer b)
+{
+ NMSriovVF *vf_a = *(NMSriovVF **) a;
+ NMSriovVF *vf_b = *(NMSriovVF **) b;
+
+ if (vf_a->index < vf_b->index)
+ return -1;
+ else if (vf_a->index > vf_b->index)
+ return 1;
+ else
+ return 0;
+}
+
+gboolean
+_nm_setting_sriov_sort_vfs (NMSettingSriov *setting)
+{
+ gboolean need_sort = FALSE;
+ guint i;
+
+ for (i = 1; i < setting->vfs->len; i++) {
+ NMSriovVF *vf_prev = setting->vfs->pdata[i - 1];
+ NMSriovVF *vf = setting->vfs->pdata[i];
+
+ if (vf->index <= vf_prev->index) {
+ need_sort = TRUE;
+ break;
+ }
+ }
+
+ if (need_sort)
+ g_ptr_array_sort (setting->vfs, vf_index_compare);
+
+ return need_sort;
+}
+
+/*****************************************************************************/
+
+static GVariant *
+vfs_to_dbus (NMSetting *setting, const char *property)
+{
+ gs_unref_ptrarray GPtrArray *vfs = NULL;
+ GVariantBuilder builder;
+ guint i;
+
+ g_object_get (setting, NM_SETTING_SRIOV_VFS, &vfs, NULL);
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+
+ if (vfs) {
+ for (i = 0; i < vfs->len; i++) {
+ gs_free const char **attr_names = NULL;
+ NMSriovVF *vf = vfs->pdata[i];
+ GVariantBuilder vf_builder;
+ const guint *vlan_ids;
+ const char **name;
+ guint num_vlans;
+
+ g_variant_builder_init (&vf_builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add (&vf_builder, "{sv}", "index",
+ g_variant_new_uint32 (nm_sriov_vf_get_index (vf)));
+
+ attr_names = nm_utils_strdict_get_keys (vf->attributes, TRUE, NULL);
+ if (attr_names) {
+ for (name = attr_names; *name; name++) {
+ g_variant_builder_add (&vf_builder,
+ "{sv}",
+ *name,
+ nm_sriov_vf_get_attribute (vf, *name));
+ }
+ }
+
+ /* VLANs are translated into an array of maps, where each map has
+ * keys 'id', 'qos' and 'proto'. This guarantees enough flexibility
+ * to accomodate any future new option. */
+ vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
+ if (num_vlans) {
+ GVariantBuilder vlans_builder;
+ guint j;
+
+ g_variant_builder_init (&vlans_builder, G_VARIANT_TYPE ("aa{sv}"));
+ for (j = 0; j < num_vlans; j++) {
+ GVariantBuilder vlan_builder;
+
+ g_variant_builder_init (&vlan_builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&vlan_builder,
+ "{sv}", "id",
+ g_variant_new_uint32 (vlan_ids[j]));
+ g_variant_builder_add (&vlan_builder,
+ "{sv}", "qos",
+ g_variant_new_uint32 (nm_sriov_vf_get_vlan_qos (vf,
+ vlan_ids[j])));
+ g_variant_builder_add (&vlan_builder,
+ "{sv}", "protocol",
+ g_variant_new_uint32 (nm_sriov_vf_get_vlan_protocol (vf,
+ vlan_ids[j])));
+ g_variant_builder_add (&vlans_builder,
+ "a{sv}",
+ &vlan_builder);
+ }
+ g_variant_builder_add (&vf_builder , "{sv}", "vlans", g_variant_builder_end (&vlans_builder));
+ }
+ g_variant_builder_add (&builder, "a{sv}", &vf_builder);
+ }
+ }
+
+ return g_variant_builder_end (&builder);
+}
+
+static gboolean
+vfs_from_dbus (NMSetting *setting,
+ GVariant *connection_dict,
+ const char *property,
+ GVariant *value,
+ NMSettingParseFlags parse_flags,
+ GError **error)
+{
+ GPtrArray *vfs;
+ GVariantIter vf_iter;
+ GVariant *vf_var;
+
+ g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), FALSE);
+
+ vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+ g_variant_iter_init (&vf_iter, value);
+ while (g_variant_iter_next (&vf_iter, "@a{sv}", &vf_var)) {
+ NMSriovVF *vf;
+ guint32 index;
+ GVariantIter attr_iter;
+ const char *attr_name;
+ GVariant *attr_var, *vlans_var;
+
+ if (!g_variant_lookup (vf_var, "index", "u", &index))
+ goto next;
+
+ vf = nm_sriov_vf_new (index);
+
+ g_variant_iter_init (&attr_iter, vf_var);
+ while (g_variant_iter_next (&attr_iter, "{&sv}", &attr_name, &attr_var)) {
+ if (!NM_IN_STRSET (attr_name, "index", "vlans"))
+ nm_sriov_vf_set_attribute (vf, attr_name, attr_var);
+ g_variant_unref (attr_var);
+ }
+
+ if (g_variant_lookup (vf_var, "vlans", "@aa{sv}", &vlans_var)) {
+ GVariantIter vlan_iter;
+ GVariant *vlan_var;
+
+ g_variant_iter_init (&vlan_iter, vlans_var);
+ while (g_variant_iter_next (&vlan_iter, "@a{sv}", &vlan_var)) {
+ NMSriovVFVlanProtocol proto = NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q;
+ gint64 vlan_id = -1;
+ guint qos = 0;
+
+ g_variant_iter_init (&attr_iter, vlan_var);
+ while (g_variant_iter_next (&attr_iter, "{&sv}", &attr_name, &attr_var)) {
+ if ( nm_streq (attr_name, "id")
+ && g_variant_is_of_type (attr_var, G_VARIANT_TYPE_UINT32))
+ vlan_id = g_variant_get_uint32 (attr_var);
+ else if ( nm_streq (attr_name, "qos")
+ && g_variant_is_of_type (attr_var, G_VARIANT_TYPE_UINT32))
+ qos = g_variant_get_uint32 (attr_var);
+ else if ( nm_streq (attr_name, "protocol")
+ && g_variant_is_of_type (attr_var, G_VARIANT_TYPE_UINT32))
+ proto = g_variant_get_uint32 (attr_var);
+ g_variant_unref (attr_var);
+ }
+ if (vlan_id != -1)
+ vf_add_vlan (vf, vlan_id, qos, proto);
+ g_variant_unref (vlan_var);
+ }
+ g_variant_unref (vlans_var);
+ }
+
+ g_ptr_array_add (vfs, vf);
+next:
+ g_variant_unref (vf_var);
+ }
+
+ g_object_set (setting, NM_SETTING_SRIOV_VFS, vfs, NULL);
+ g_ptr_array_unref (vfs);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static gboolean
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+ NMSettingSriov *self = NM_SETTING_SRIOV (setting);
+ guint i;
+
+ if (self->vfs->len) {
+ gs_unref_hashtable GHashTable *h = NULL;
+
+ h = g_hash_table_new (nm_direct_hash, NULL);
+ for (i = 0; i < self->vfs->len; i++) {
+ NMSriovVF *vf = self->vfs->pdata[i];
+ gs_free_error GError *local = NULL;
+
+ if (vf->index >= self->total_vfs) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("VF with index %u, but the total number of VFs is %u"),
+ vf->index, self->total_vfs);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME,
+ NM_SETTING_SRIOV_VFS);
+ return FALSE;
+ }
+
+ if (!_nm_sriov_vf_attribute_validate_all (vf, &local)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("invalid VF %u: %s"),
+ vf->index,
+ local->message);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME,
+ NM_SETTING_SRIOV_VFS);
+ return FALSE;
+ }
+
+ if (g_hash_table_contains (h, GUINT_TO_POINTER (vf->index))) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("duplicate VF index %u"), vf->index);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME,
+ NM_SETTING_SRIOV_VFS);
+ return FALSE;
+ }
+
+ g_hash_table_add (h, GUINT_TO_POINTER (vf->index));
+ }
+ }
+
+ /* Failures from here on are NORMALIZABLE... */
+
+ if (self->vfs->len) {
+ for (i = 1; i < self->vfs->len; i++) {
+ NMSriovVF *vf_prev = self->vfs->pdata[i - 1];
+ NMSriovVF *vf = self->vfs->pdata[i];
+
+ if (vf->index <= vf_prev->index) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("VFs %d and %d are not sorted by ascending index"),
+ vf_prev->index, vf->index);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME,
+ NM_SETTING_SRIOV_VFS);
+ return NM_SETTING_VERIFY_NORMALIZABLE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingSriov *self = NM_SETTING_SRIOV (object);
+
+ switch (prop_id) {
+ case PROP_TOTAL_VFS:
+ self->total_vfs = g_value_get_uint (value);
+ break;
+ case PROP_VFS:
+ g_ptr_array_unref (self->vfs);
+ self->vfs = _nm_utils_copy_array (g_value_get_boxed (value),
+ (NMUtilsCopyFunc) nm_sriov_vf_dup,
+ (GDestroyNotify) nm_sriov_vf_unref);
+ break;
+ case PROP_AUTOPROBE_DRIVERS:
+ self->autoprobe_drivers = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingSriov *self = NM_SETTING_SRIOV (object);
+
+ switch (prop_id) {
+ case PROP_TOTAL_VFS:
+ g_value_set_uint (value, self->total_vfs);
+ break;
+ case PROP_VFS:
+ g_value_take_boxed (value, _nm_utils_copy_array (self->vfs,
+ (NMUtilsCopyFunc) nm_sriov_vf_dup,
+ (GDestroyNotify) nm_sriov_vf_unref));
+ break;
+ case PROP_AUTOPROBE_DRIVERS:
+ g_value_set_enum (value, self->autoprobe_drivers);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+compare_property (NMSetting *setting,
+ NMSetting *other,
+ const GParamSpec *prop_spec,
+ NMSettingCompareFlags flags)
+{
+ NMSettingSriov *a = NM_SETTING_SRIOV (setting);
+ NMSettingSriov *b = NM_SETTING_SRIOV (other);
+ NMSettingClass *parent_class;
+ guint i;
+
+ if (nm_streq (prop_spec->name, NM_SETTING_SRIOV_VFS)) {
+ if (a->vfs->len != b->vfs->len)
+ return FALSE;
+ for (i = 0; i < a->vfs->len; i++) {
+ if (!nm_sriov_vf_equal (a->vfs->pdata[i], b->vfs->pdata[i]))
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /* Otherwise chain up to parent to handle generic compare */
+ parent_class = NM_SETTING_CLASS (nm_setting_sriov_parent_class);
+ return parent_class->compare_property (setting, other, prop_spec, flags);
+}
+
+static void
+nm_setting_sriov_init (NMSettingSriov *setting)
+{
+ setting->vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingSriov *self = NM_SETTING_SRIOV (object);
+
+ g_ptr_array_unref (self->vfs);
+
+ G_OBJECT_CLASS (nm_setting_sriov_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_sriov_class_init (NMSettingSriovClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ parent_class->compare_property = compare_property;
+ parent_class->verify = verify;
+
+ /**
+ * NMSettingSriov:total-vfs
+ *
+ * The total number of virtual functions to create.
+ *
+ * Since: 1.14
+ **/
+ /* ---ifcfg-rh---
+ * property: total-vfs
+ * variable: SRIOV_TOTAL_VFS(+)
+ * description: The total number of virtual functions to create
+ * example: SRIOV_TOTAL_VFS=16
+ * ---end---
+ */
+ g_object_class_install_property
+ (object_class, PROP_TOTAL_VFS,
+ g_param_spec_uint (NM_SETTING_SRIOV_TOTAL_VFS, "", "",
+ 0, G_MAXUINT32, 0,
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingSriov:vfs: (type GPtrArray(NMSriovVF))
+ *
+ * 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".
+ *
+ * The "vlans" attribute is represented as a semicolor-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.
+ *
+ * Since: 1.14
+ **/
+ /* ---ifcfg-rh---
+ * property: vfs
+ * variable: SRIOV_VF1(+), SRIOV_VF2(+), ...
+ * description: SR-IOV virtual function descriptors
+ * example: SRIOV_VF10="mac=00:11:22:33:44:55", ...
+ * ---end---
+ */
+ g_object_class_install_property
+ (object_class, PROP_VFS,
+ g_param_spec_boxed (NM_SETTING_SRIOV_VFS, "", "",
+ G_TYPE_PTR_ARRAY,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ _nm_setting_class_override_property (parent_class,
+ NM_SETTING_SRIOV_VFS,
+ G_VARIANT_TYPE ("aa{sv}"),
+ vfs_to_dbus,
+ vfs_from_dbus,
+ NULL);
+
+ /**
+ * NMSettingSriov:autoprobe-drivers
+ *
+ * Whether to autoprobe virtual functions by a compatible driver.
+ *
+ * If set to %NM_TERNARY_TRUE, 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 %NM_TERNARY_FALSE, VFs will not be claimed and no
+ * network interfaces will be created for them.
+ *
+ * When set to %NM_TERNARY_DEFAULT, the global default is used; in
+ * case the global default is unspecified it is assumed to be
+ * %NM_TERNARY_TRUE.
+ *
+ * Since: 1.14
+ **/
+ /* ---ifcfg-rh---
+ * property: autoprobe-drivers
+ * variable: SRIOV_AUTOPROBE_DRIVERS(+)
+ * default: missing variable means global default
+ * description: Whether to autoprobe virtual functions by a compatible driver
+ * example: SRIOV_AUTOPROBE_DRIVERS=0,1
+ * ---end---
+ */
+ g_object_class_install_property
+ (object_class, PROP_AUTOPROBE_DRIVERS,
+ g_param_spec_enum (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, "", "",
+ nm_ternary_get_type (),
+ NM_TERNARY_DEFAULT,
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-sriov.h b/libnm-core/nm-setting-sriov.h
new file mode 100644
index 0000000000..2e209964d4
--- /dev/null
+++ b/libnm-core/nm-setting-sriov.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_SRIOV_H
+#define NM_SETTING_SRIOV_H
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_SRIOV (nm_setting_sriov_get_type ())
+#define NM_SETTING_SRIOV(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_SRIOV, NMSettingSriov))
+#define NM_SETTING_SRIOV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_SRIOV, NMSettingSriovClass))
+#define NM_IS_SETTING_SRIOV(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_SRIOV))
+#define NM_IS_SETTING_SRIOV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_SRIOV))
+#define NM_SETTING_SRIOV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_SRIOV, NMSettingSriovClass))
+
+#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_SRIOV_VF_ATTRIBUTE_MAC "mac"
+#define NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK "spoof-check"
+#define NM_SRIOV_VF_ATTRIBUTE_TRUST "trust"
+#define NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE "min-tx-rate"
+#define NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE "max-tx-rate"
+
+typedef struct _NMSettingSriovClass NMSettingSriovClass;
+typedef struct _NMSriovVF NMSriovVF;
+
+/**
+ * NMSriovVFVlanProtocol:
+ * @NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q: use 802.1Q
+ * @NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD: use 802.1ad
+ *
+ * #NMSriovVFVlanProtocol indicates the VLAN protocol to use.
+ *
+ * Since: 1.14
+ */
+typedef enum {
+ NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q = 0,
+ NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD = 1,
+} NMSriovVFVlanProtocol;
+
+NM_AVAILABLE_IN_1_14
+GType nm_setting_sriov_get_type (void);
+NM_AVAILABLE_IN_1_14
+NMSetting *nm_setting_sriov_new (void);
+NM_AVAILABLE_IN_1_14
+guint nm_setting_sriov_get_total_vfs (NMSettingSriov *setting);
+NM_AVAILABLE_IN_1_14
+guint nm_setting_sriov_get_num_vfs (NMSettingSriov *setting);
+NM_AVAILABLE_IN_1_14
+NMSriovVF *nm_setting_sriov_get_vf (NMSettingSriov *setting, guint idx);
+NM_AVAILABLE_IN_1_14
+void nm_setting_sriov_add_vf (NMSettingSriov *setting, NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+void nm_setting_sriov_remove_vf (NMSettingSriov *setting, guint idx);
+NM_AVAILABLE_IN_1_14
+gboolean nm_setting_sriov_remove_vf_by_index (NMSettingSriov *setting, guint index);
+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_14
+gboolean nm_sriov_vf_add_vlan (NMSriovVF *vf, guint vlan_id);
+NM_AVAILABLE_IN_1_14
+gboolean nm_sriov_vf_remove_vlan (NMSriovVF *vf, guint vlan_id);
+NM_AVAILABLE_IN_1_14
+const guint *nm_sriov_vf_get_vlan_ids (const NMSriovVF *vf, guint *length);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_set_vlan_qos (NMSriovVF *vf, guint vlan_id, guint32 qos);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_set_vlan_protocol (NMSriovVF *vf, guint vlan_id, NMSriovVFVlanProtocol protocol);
+NM_AVAILABLE_IN_1_14
+guint32 nm_sriov_vf_get_vlan_qos (const NMSriovVF *vf, guint vlan_id);
+NM_AVAILABLE_IN_1_14
+NMSriovVFVlanProtocol nm_sriov_vf_get_vlan_protocol (const NMSriovVF *vf, guint vlan_id);
+
+NM_AVAILABLE_IN_1_14
+GType nm_sriov_vf_get_type (void);
+NM_AVAILABLE_IN_1_14
+NMSriovVF *nm_sriov_vf_new (guint index);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_ref (NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_unref (NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+gboolean nm_sriov_vf_equal (const NMSriovVF *vf, const NMSriovVF *other);
+NM_AVAILABLE_IN_1_14
+NMSriovVF *nm_sriov_vf_dup (const NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+guint nm_sriov_vf_get_index (const NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_set_attribute (NMSriovVF *vf, const char *name, GVariant *value);
+NM_AVAILABLE_IN_1_14
+const char **nm_sriov_vf_get_attribute_names (const NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+GVariant *nm_sriov_vf_get_attribute (const NMSriovVF *vf, const char *name);
+NM_AVAILABLE_IN_1_14
+gboolean nm_sriov_vf_attribute_validate (const char *name, GVariant *value, gboolean *known, GError **error);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_SRIOV_H */
diff --git a/libnm-core/nm-setting-tc-config.c b/libnm-core/nm-setting-tc-config.c
index 04e2f2d510..588446e5dd 100644
--- a/libnm-core/nm-setting-tc-config.c
+++ b/libnm-core/nm-setting-tc-config.c
@@ -820,18 +820,18 @@ enum {
/**
* NMSettingTCConfig:
*
- * Linux Traffic Contril Settings.
+ * Linux Traffic Control Settings.
*
* Since: 1.12
*/
struct _NMSettingTCConfig {
- NMSetting parent;
+ NMSetting parent;
GPtrArray *qdiscs;
GPtrArray *tfilters;
};
struct _NMSettingTCConfigClass {
- NMSettingClass parent;
+ NMSettingClass parent;
};
G_DEFINE_TYPE_WITH_CODE (NMSettingTCConfig, nm_setting_tc_config, NM_TYPE_SETTING,
@@ -982,8 +982,10 @@ nm_setting_tc_config_clear_qdiscs (NMSettingTCConfig *self)
{
g_return_if_fail (NM_IS_SETTING_TC_CONFIG (self));
- g_ptr_array_set_size (self->qdiscs, 0);
- g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS);
+ if (self->qdiscs->len != 0) {
+ g_ptr_array_set_size (self->qdiscs, 0);
+ g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS);
+ }
}
/*****************************************************************************/
@@ -1116,8 +1118,10 @@ nm_setting_tc_config_clear_tfilters (NMSettingTCConfig *self)
{
g_return_if_fail (NM_IS_SETTING_TC_CONFIG (self));
- g_ptr_array_set_size (self->tfilters, 0);
- g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS);
+ if (self->tfilters->len != 0) {
+ g_ptr_array_set_size (self->tfilters, 0);
+ g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS);
+ }
}
/*****************************************************************************/
@@ -1602,11 +1606,11 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class)
/**
* NMSettingTCConfig:qdiscs: (type GPtrArray(NMTCQdisc))
*
- * Array of TC queuening disciplines.
+ * Array of TC queueing disciplines.
**/
/* ---ifcfg-rh---
* property: qdiscs
- * variable: QDISC1, QDISC2, ...
+ * variable: QDISC1(+), QDISC2(+), ...
* description: Queueing disciplines
* example: QDISC1=ingress, QDISC2="root handle 1234: fq_codel"
* ---end---
@@ -1633,7 +1637,7 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class)
**/
/* ---ifcfg-rh---
* property: qdiscs
- * variable: FILTER1, FILTER2, ...
+ * variable: FILTER1(+), FILTER2(+), ...
* description: Traffic filters
* example: FILTER1="parent ffff: matchall action simple sdata Input", ...
* ---end---
diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c
index e0e5dacaa7..d6412224e2 100644
--- a/libnm-core/nm-setting-team-port.c
+++ b/libnm-core/nm-setting-team-port.c
@@ -322,8 +322,10 @@ nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting)
g_return_if_fail (NM_IS_SETTING_TEAM_PORT (setting));
- g_ptr_array_set_size (priv->link_watchers, 0);
- g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS);
+ if (priv->link_watchers->len != 0) {
+ g_ptr_array_set_size (priv->link_watchers, 0);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS);
+ }
}
static GVariant *
diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c
index b413adaec2..1718b3e55f 100644
--- a/libnm-core/nm-setting-team.c
+++ b/libnm-core/nm-setting-team.c
@@ -1120,8 +1120,10 @@ nm_setting_team_clear_link_watchers (NMSettingTeam *setting) {
g_return_if_fail (NM_IS_SETTING_TEAM (setting));
- g_ptr_array_set_size (priv->link_watchers, 0);
- g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS);
+ if (priv->link_watchers->len != 0) {
+ g_ptr_array_set_size (priv->link_watchers, 0);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS);
+ }
}
static GVariant *
diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c
index fa1f3fdfba..575305e220 100644
--- a/libnm-core/nm-setting.c
+++ b/libnm-core/nm-setting.c
@@ -132,6 +132,7 @@ _register_settings_ensure_types (void)
ENSURE_TYPE (nm_setting_pppoe_get_type);
ENSURE_TYPE (nm_setting_proxy_get_type);
ENSURE_TYPE (nm_setting_serial_get_type);
+ ENSURE_TYPE (nm_setting_sriov_get_type);
ENSURE_TYPE (nm_setting_tc_config_get_type);
ENSURE_TYPE (nm_setting_team_get_type);
ENSURE_TYPE (nm_setting_team_port_get_type);
diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h
index 5d0f83868e..0605d83fa1 100644
--- a/libnm-core/nm-utils-private.h
+++ b/libnm-core/nm-utils-private.h
@@ -83,6 +83,13 @@ char * _nm_utils_hwaddr_canonical_or_invalid (const char *mac, gssize lengt
GPtrArray * _nm_utils_team_link_watchers_from_variant (GVariant *value);
GVariant * _nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers);
+void _nm_utils_format_variant_attributes_full (GString *str,
+ const NMUtilsNamedValue *values,
+ guint num_values,
+ char attr_separator,
+ char key_value_separator);
+gboolean _nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error);
+
/* JSON to GValue conversion macros */
typedef struct {
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 62da974f19..bea6b427b0 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -2657,6 +2657,249 @@ nm_utils_tc_tfilter_from_str (const char *str, GError **error)
/*****************************************************************************/
+extern const NMVariantAttributeSpec *const _nm_sriov_vf_attribute_spec[];
+
+/**
+ * nm_utils_sriov_vf_to_str:
+ * @vf: the %NMSriovVF
+ * @omit_index: if %TRUE, the VF index will be omitted from output string
+ * @error: (out) (allow-none): location to store the error on failure
+ *
+ * Converts a SR-IOV virtual function object to its string representation.
+ *
+ * Returns: a newly allocated string or %NULL on error
+ *
+ * Since: 1.14
+ */
+char *
+nm_utils_sriov_vf_to_str (const NMSriovVF *vf, gboolean omit_index, GError **error)
+{
+ gs_free NMUtilsNamedValue *values = NULL;
+ gs_free const char **names = NULL;
+ const guint *vlan_ids;
+ guint num_vlans, num_attrs;
+ guint i;
+ GString *str;
+
+ str = g_string_new ("");
+ if (!omit_index)
+ g_string_append_printf (str, "%u", nm_sriov_vf_get_index (vf));
+
+ names = nm_sriov_vf_get_attribute_names (vf);
+ num_attrs = names ? g_strv_length ((char **) names) : 0;
+ values = g_new0 (NMUtilsNamedValue, num_attrs);
+
+ for (i = 0; i < num_attrs; i++) {
+ values[i].name = names[i];
+ values[i].value_ptr = nm_sriov_vf_get_attribute (vf, names[i]);
+ }
+
+ if (num_attrs > 0) {
+ if (!omit_index)
+ g_string_append_c (str, ' ');
+ _nm_utils_format_variant_attributes_full (str, values, num_attrs, ' ', '=');
+ }
+
+ vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
+ if (num_vlans != 0) {
+ g_string_append (str, " vlans");
+ for (i = 0; i < num_vlans; i++) {
+ guint32 qos;
+ NMSriovVFVlanProtocol protocol;
+
+ qos = nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]);
+ protocol = nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]);
+
+ g_string_append_c (str, i == 0 ? '=' : ';');
+
+ g_string_append_printf (str, "%u", vlan_ids[i]);
+
+ if ( qos != 0
+ || protocol != NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q) {
+ g_string_append_printf (str,
+ ".%u%s",
+ (unsigned) qos,
+ protocol == NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q ? "" : ".ad");
+ }
+ }
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+gboolean
+_nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error)
+{
+ gs_free const char **vlans = NULL;
+ guint i;
+
+ vlans = nm_utils_strsplit_set (str, ";");
+ if (!vlans) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ "empty VF VLAN");
+ return FALSE;
+ }
+
+ for (i = 0; vlans[i]; i++) {
+ gs_strfreev char **params = NULL;
+ guint id = G_MAXUINT;
+ gint64 qos = -1;
+
+ /* we accept leading/trailing whitespace around vlans[1]. Hence
+ * the nm_str_skip_leading_spaces() and g_strchomp() below.
+ *
+ * However, we don't accept any whitespace inside the specifier.
+ * Hence the NM_STRCHAR_ALL() checks. */
+
+ params = g_strsplit (nm_str_skip_leading_spaces (vlans[i]), ".", 3);
+ if (!params || !params[0] || *params[0] == '\0') {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ "empty VF VLAN");
+ return FALSE;
+ }
+
+ if (!params[1])
+ g_strchomp (params[0]);
+ if (NM_STRCHAR_ALL (params[0], ch, ch == 'x' || g_ascii_isdigit (ch)))
+ id = _nm_utils_ascii_str_to_int64 (params[0], 0, 0, 4095, G_MAXUINT);
+ if (id == G_MAXUINT) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ "invalid VF VLAN id '%s'",
+ params[0]);
+ return FALSE;
+ }
+ if (!nm_sriov_vf_add_vlan (vf, id)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ "duplicate VLAN id %u",
+ id);
+ return FALSE;
+ }
+
+ if (!params[1])
+ continue;
+
+ if (!params[2])
+ g_strchomp (params[1]);
+ if (NM_STRCHAR_ALL (params[1], ch, ch == 'x' || g_ascii_isdigit (ch)))
+ qos = _nm_utils_ascii_str_to_int64 (params[1], 0, 0, G_MAXUINT32, -1);
+ if (qos == -1) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ "invalid VF VLAN QoS '%s'",
+ params[1]);
+ return FALSE;
+ }
+ nm_sriov_vf_set_vlan_qos (vf, id, qos);
+
+ if (!params[2])
+ continue;
+
+ g_strchomp (params[2]);
+
+ if (nm_streq (params[2], "ad"))
+ nm_sriov_vf_set_vlan_protocol (vf, id, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+ else if (nm_streq (params[2], "q"))
+ nm_sriov_vf_set_vlan_protocol (vf, id, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+ else {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ "invalid VF VLAN protocol '%s'",
+ params[2]);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * nm_utils_sriov_vf_from_str:
+ * @str: the input string
+ * @error: (out) (allow-none): location to store the error on failure
+ *
+ * Converts a string to a SR-IOV virtual function object.
+ *
+ * Returns: (transfer full): the virtual function object
+ *
+ * Since: 1.14
+ */
+NMSriovVF *
+nm_utils_sriov_vf_from_str (const char *str, GError **error)
+{
+ gs_free char *index_free = NULL;
+ const char *detail;
+
+ g_return_val_if_fail (str, NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
+
+ while (*str == ' ')
+ str++;
+
+ detail = strchr (str, ' ');
+ if (detail) {
+ index_free = g_strndup (str, detail - str);
+ str = index_free;
+ detail++;
+ }
+
+ return _nm_utils_sriov_vf_from_strparts (str, detail, error);
+}
+
+NMSriovVF *
+_nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError **error)
+{
+ NMSriovVF *vf;
+ guint32 n_index;
+ GHashTableIter iter;
+ char *key;
+ GVariant *variant;
+ gs_unref_hashtable GHashTable *ht = NULL;
+
+ n_index = _nm_utils_ascii_str_to_int64 (index, 10, 0, G_MAXUINT32, 0);
+ if (errno) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_FAILED,
+ "invalid index");
+ return NULL;
+ }
+
+ vf = nm_sriov_vf_new (n_index);
+ if (detail) {
+ ht = nm_utils_parse_variant_attributes (detail, ' ', '=', TRUE, _nm_sriov_vf_attribute_spec, error);
+ if (!ht) {
+ nm_sriov_vf_unref (vf);
+ return NULL;
+ }
+
+ if ((variant = g_hash_table_lookup (ht, "vlans"))) {
+ if (!_nm_sriov_vf_parse_vlans (vf, g_variant_get_string (variant, NULL), error)) {
+ nm_sriov_vf_unref (vf);
+ return NULL;
+ }
+ g_hash_table_remove (ht, "vlans");
+ }
+
+ g_hash_table_iter_init (&iter, ht);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &variant))
+ nm_sriov_vf_set_attribute (vf, key, g_variant_ref_sink (variant));
+ }
+
+ return vf;
+}
+
+/*****************************************************************************/
+
/**
* nm_utils_uuid_generate_buf_:
* @buf: input buffer, must contain at least 37 bytes
@@ -6156,44 +6399,21 @@ next:
return g_steal_pointer (&ht);
}
-/*
- * nm_utils_format_variant_attributes:
- * @attributes: (element-type utf8 GVariant): a #GHashTable mapping attribute names to #GVariant values
- * @attr_separator: the attribute separator character
- * @key_value_separator: character separating key and values
- *
- * Format attributes to a string.
- *
- * Returns: (transfer full): the string representing attributes, or %NULL
- * in case there are no attributes
- *
- * Since: 1.8
- */
-char *
-nm_utils_format_variant_attributes (GHashTable *attributes,
- char attr_separator,
- char key_value_separator)
+void
+_nm_utils_format_variant_attributes_full (GString *str,
+ const NMUtilsNamedValue *values,
+ guint num_values,
+ char attr_separator,
+ char key_value_separator)
{
- GString *str = NULL;
- GVariant *variant;
- char sep = 0;
const char *name, *value;
+ GVariant *variant;
char *escaped;
char buf[64];
- gs_free NMUtilsNamedValue *values = NULL;
- guint i, len;
-
- g_return_val_if_fail (attr_separator, NULL);
- g_return_val_if_fail (key_value_separator, NULL);
-
- if (!attributes || !g_hash_table_size (attributes))
- return NULL;
-
- values = nm_utils_named_values_from_str_dict (attributes, &len);
-
- str = g_string_new ("");
+ char sep = 0;
+ guint i;
- for (i = 0; i < len; i++) {
+ for (i = 0; i < num_values; i++) {
name = values[i].name;
variant = (GVariant *) values[i].value_ptr;
value = NULL;
@@ -6226,7 +6446,44 @@ nm_utils_format_variant_attributes (GHashTable *attributes,
sep = attr_separator;
}
+}
+
+/*
+ * nm_utils_format_variant_attributes:
+ * @attributes: (element-type utf8 GVariant): a #GHashTable mapping attribute names to #GVariant values
+ * @attr_separator: the attribute separator character
+ * @key_value_separator: character separating key and values
+ *
+ * Format attributes to a string.
+ *
+ * Returns: (transfer full): the string representing attributes, or %NULL
+ * in case there are no attributes
+ *
+ * Since: 1.8
+ */
+char *
+nm_utils_format_variant_attributes (GHashTable *attributes,
+ char attr_separator,
+ char key_value_separator)
+{
+ GString *str = NULL;
+ gs_free NMUtilsNamedValue *values = NULL;
+ guint len;
+
+ g_return_val_if_fail (attr_separator, NULL);
+ g_return_val_if_fail (key_value_separator, NULL);
+ if (!attributes || !g_hash_table_size (attributes))
+ return NULL;
+
+ values = nm_utils_named_values_from_str_dict (attributes, &len);
+
+ str = g_string_new ("");
+ _nm_utils_format_variant_attributes_full (str,
+ values,
+ len,
+ attr_separator,
+ key_value_separator);
return g_string_free (str, FALSE);
}
diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h
index 87463ac3cb..cf9572b907 100644
--- a/libnm-core/nm-utils.h
+++ b/libnm-core/nm-utils.h
@@ -34,8 +34,9 @@
#include <linux/if_infiniband.h>
#include "nm-core-enum-types.h"
-#include "nm-setting-wireless-security.h"
+#include "nm-setting-sriov.h"
#include "nm-setting-tc-config.h"
+#include "nm-setting-wireless-security.h"
G_BEGIN_DECLS
@@ -250,9 +251,17 @@ char *nm_utils_tc_tfilter_to_str (NMTCTfilter *tfilter, GError **error
/*****************************************************************************/
+NM_AVAILABLE_IN_1_14
+char *nm_utils_sriov_vf_to_str (const NMSriovVF *vf, gboolean omit_index, GError **error);
+NM_AVAILABLE_IN_1_14
+NMSriovVF *nm_utils_sriov_vf_from_str (const char *str, GError **error);
+
+/*****************************************************************************/
+
NM_AVAILABLE_IN_1_12
gint64 nm_utils_get_timestamp_msec (void);
+
G_END_DECLS
#endif /* __NM_UTILS_H__ */
diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c
index e71d6b7cc2..cb02d1c4a5 100644
--- a/libnm-core/tests/test-setting.c
+++ b/libnm-core/tests/test-setting.c
@@ -23,6 +23,8 @@
#include <string.h>
#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-core-internal.h"
#include "nm-setting-8021x.h"
#include "nm-setting-bond.h"
#include "nm-setting-dcb.h"
@@ -1263,6 +1265,234 @@ test_team_port_full_config (void)
/*****************************************************************************/
static void
+test_sriov_vf (void)
+{
+ NMSriovVF *vf1, *vf2;
+ GError *error = NULL;
+ char *str;
+
+ vf1 = nm_sriov_vf_new (1);
+ nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22:33:44:55"));
+ nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE));
+ nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
+ nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (100));
+ nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (500));
+
+ str = nm_utils_sriov_vf_to_str (vf1, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (str, ==, "1 mac=00:11:22:33:44:55 max-tx-rate=500 min-tx-rate=100 spoof-check=true trust=false");
+ g_free (str);
+
+ vf2 = nm_utils_sriov_vf_from_str (" 1 mac=00:11:22:33:44:55 max-tx-rate=500 min-tx-rate=100", &error);
+ nmtst_assert_success (vf2, error);
+ nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (FALSE));
+ nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE));
+ nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (TRUE));
+ nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, NULL);
+ nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
+
+ g_assert (nm_sriov_vf_equal (vf1, vf2));
+
+ nm_sriov_vf_unref (vf1);
+ nm_sriov_vf_unref (vf2);
+}
+
+static void
+test_sriov_vf_dup (void)
+{
+ NMSriovVF *vf1, *vf2;
+
+ vf1 = nm_sriov_vf_new (1);
+ nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("foobar"));
+ nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
+ nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (10));
+ nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (1000));
+ nm_sriov_vf_add_vlan (vf1, 80);
+ nm_sriov_vf_set_vlan_qos (vf1, 80, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+ vf2 = nm_sriov_vf_dup (vf1);
+ g_assert (nm_sriov_vf_equal (vf1, vf2));
+
+ nm_sriov_vf_unref (vf1);
+ nm_sriov_vf_unref (vf2);
+}
+
+static void
+test_sriov_vf_vlan (void)
+{
+ NMSriovVF *vf;
+ const guint *vlan_ids;
+ guint num;
+ GError *error = NULL;
+ gs_free char *str = NULL;
+
+ vf = nm_sriov_vf_new (19);
+ nm_sriov_vf_set_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22"));
+ g_assert (nm_sriov_vf_add_vlan (vf, 80));
+ g_assert (!nm_sriov_vf_add_vlan (vf, 80));
+ g_assert (nm_sriov_vf_add_vlan (vf, 82));
+ g_assert (nm_sriov_vf_add_vlan (vf, 83));
+ g_assert (nm_sriov_vf_add_vlan (vf, 81));
+ g_assert (!nm_sriov_vf_remove_vlan (vf, 100));
+ g_assert (nm_sriov_vf_remove_vlan (vf, 82));
+ nm_sriov_vf_set_vlan_qos (vf, 81, 0xabba);
+ nm_sriov_vf_set_vlan_protocol (vf, 81, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+ vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num);
+ g_assert (vlan_ids);
+ g_assert_cmpint (num, ==, 3);
+ g_assert_cmpint (vlan_ids[0], ==, 80);
+ g_assert_cmpint (vlan_ids[1], ==, 81);
+ g_assert_cmpint (vlan_ids[2], ==, 83);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 80), ==, 0x0);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 80), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 0xabba);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+ nm_sriov_vf_unref (vf);
+
+ vf = nm_utils_sriov_vf_from_str ("20 spoof-check=false vlans=85.0.q;4000.0x20.ad;81.10;83", &error);
+ nmtst_assert_success (vf, error);
+ vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num);
+ g_assert (vlan_ids);
+ g_assert_cmpint (num, ==, 4);
+ g_assert_cmpint (vlan_ids[0], ==, 81);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 10);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+ g_assert_cmpint (vlan_ids[1], ==, 83);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 83), ==, 0);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 83), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+ g_assert_cmpint (vlan_ids[2], ==, 85);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 85), ==, 0);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 85), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+ g_assert_cmpint (vlan_ids[3], ==, 4000);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 4000), ==, 0x20);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 4000), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+ str = nm_utils_sriov_vf_to_str (vf, FALSE, &error);
+ nmtst_assert_success (str, error);
+ g_assert_cmpstr (str, ==, "20 spoof-check=false vlans=81.10;83;85;4000.32.ad");
+
+ nm_sriov_vf_unref (vf);
+}
+
+static void
+test_sriov_setting (void)
+{
+ gs_unref_object NMConnection *con = NULL;
+ NMSettingConnection *s_con;
+ NMSettingSriov *s_sriov = NULL;
+ NMSriovVF *vf1, *vf2, *vf3;
+ GError *error = NULL;
+ gboolean success;
+
+ con = nm_simple_connection_new ();
+
+ s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ nm_connection_add_setting (con, NM_SETTING (s_con));
+
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_ID, "Test SR-IOV connection",
+ NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
+ NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+ NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0",
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+ NULL);
+
+ nm_connection_add_setting (con, nm_setting_wired_new ());
+
+ s_sriov = (NMSettingSriov *) nm_setting_sriov_new ();
+ nm_connection_add_setting (con, NM_SETTING (s_sriov));
+
+ g_object_set (s_sriov, NM_SETTING_SRIOV_TOTAL_VFS, 16, NULL);
+ nm_setting_sriov_add_vf (s_sriov, (vf1 = nm_sriov_vf_new (0)));
+ nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (4)));
+ nm_setting_sriov_add_vf (s_sriov, (vf3 = nm_sriov_vf_new (10)));
+ g_assert (nm_setting_sriov_remove_vf_by_index (s_sriov, 4));
+ nm_sriov_vf_unref (vf2);
+ nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (2)));
+
+ nmtst_assert_connection_verifies_and_normalizable (con);
+ nmtst_connection_normalize (con);
+ success = nm_setting_verify ((NMSetting *) s_sriov, con, &error);
+ nmtst_assert_success (success, error);
+
+ g_assert_cmpint (nm_setting_sriov_get_num_vfs (s_sriov), ==, 3);
+ g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 0)), ==, 0);
+ g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 1)), ==, 2);
+ g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 2)), ==, 10);
+
+ nm_sriov_vf_unref (vf1);
+ nm_sriov_vf_unref (vf2);
+ nm_sriov_vf_unref (vf3);
+}
+
+typedef struct {
+ guint id;
+ guint qos;
+ bool proto_ad;
+} VlanData;
+
+static void
+_test_sriov_parse_vlan_one (const char *string, gboolean exp_res, VlanData *data, guint data_length)
+{
+ NMSriovVF *vf;
+ gboolean res;
+ guint i, num_vlans;
+ const guint *vlan_ids;
+
+ vf = nm_sriov_vf_new (1);
+ g_assert (vf);
+
+ res = _nm_sriov_vf_parse_vlans (vf, string, NULL);
+ g_assert_cmpint (res, ==, exp_res);
+
+ if (exp_res) {
+ vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
+ g_assert_cmpint (num_vlans, ==, data_length);
+ for (i = 0; i < num_vlans; i++) {
+ g_assert_cmpint (vlan_ids[i], ==, data[i].id);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]), ==, data[i].qos);
+ g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]),
+ ==,
+ data[i].proto_ad ? NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD: NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+ }
+ }
+
+ nm_sriov_vf_unref (vf);
+}
+
+#define test_sriov_parse_vlan_one(string, result, ...) \
+ { \
+ VlanData _data[] = { __VA_ARGS__ }; \
+ guint _length = G_N_ELEMENTS (_data); \
+ \
+ _test_sriov_parse_vlan_one (string, result, _data, _length); \
+ }
+
+static void
+test_sriov_parse_vlans (void)
+{
+ test_sriov_parse_vlan_one ("", FALSE, {});
+ test_sriov_parse_vlan_one ("1", TRUE, {1, 0, 0});
+ test_sriov_parse_vlan_one ("1;2", TRUE, {1, 0, 0}, {2, 0, 0});
+ test_sriov_parse_vlan_one ("4095;;2", TRUE, {2, 0, 0}, {4095, 0, 0});
+ test_sriov_parse_vlan_one ("1 2", FALSE, {});
+ test_sriov_parse_vlan_one ("4096", FALSE, {});
+ test_sriov_parse_vlan_one ("1.10", TRUE, {1, 10, 0});
+ test_sriov_parse_vlan_one ("1.20.ad", TRUE, {1, 20, 1});
+ test_sriov_parse_vlan_one ("1.21.q", TRUE, {1, 21, 0});
+ test_sriov_parse_vlan_one ("9.20.foo", FALSE, {});
+ test_sriov_parse_vlan_one ("1.20.ad.12", FALSE, {});
+ test_sriov_parse_vlan_one ("1;1.10", FALSE, {});
+ test_sriov_parse_vlan_one ("1..1;2", FALSE, {});
+ test_sriov_parse_vlan_one ("1..ad;2", FALSE, {});
+ test_sriov_parse_vlan_one ("1.2.ad;2.0.q;5;3", TRUE, {1, 2, 1}, {2, 0, 0}, {3, 0, 0}, {5, 0, 0});
+}
+
+/*****************************************************************************/
+
+static void
test_tc_config_qdisc (void)
{
NMTCQdisc *qdisc1, *qdisc2;
@@ -1669,6 +1899,12 @@ main (int argc, char **argv)
g_test_add_func ("/libnm/settings/dcb/priorities", test_dcb_priorities_valid);
g_test_add_func ("/libnm/settings/dcb/bandwidth-sums", test_dcb_bandwidth_sums);
+ g_test_add_func ("/libnm/settings/sriov/vf", test_sriov_vf);
+ g_test_add_func ("/libnm/settings/sriov/vf-dup", test_sriov_vf_dup);
+ g_test_add_func ("/libnm/settings/sriov/vf-vlan", test_sriov_vf_vlan);
+ g_test_add_func ("/libnm/settings/sriov/setting", test_sriov_setting);
+ g_test_add_func ("/libnm/settings/sriov/vlans", test_sriov_parse_vlans);
+
g_test_add_func ("/libnm/settings/tc_config/qdisc", test_tc_config_qdisc);
g_test_add_func ("/libnm/settings/tc_config/action", test_tc_config_action);
g_test_add_func ("/libnm/settings/tc_config/tfilter", test_tc_config_tfilter);
diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h
index 73186f74ea..9cecef3acb 100644
--- a/libnm/NetworkManager.h
+++ b/libnm/NetworkManager.h
@@ -89,6 +89,7 @@
#include "nm-setting-pppoe.h"
#include "nm-setting-proxy.h"
#include "nm-setting-serial.h"
+#include "nm-setting-sriov.h"
#include "nm-setting-tc-config.h"
#include "nm-setting-team.h"
#include "nm-setting-team-port.h"
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index 06592f3ab9..d10bf54191 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -1381,9 +1381,41 @@ libnm_1_12_2 {
libnm_1_14_0 {
global:
nm_connection_get_setting_6lowpan;
+ nm_connection_get_setting_sriov;
nm_connection_get_setting_wpan;
nm_device_6lowpan_get_type;
nm_device_wpan_get_type;
nm_setting_6lowpan_get_type;
+ nm_setting_sriov_add_vf;
+ nm_setting_sriov_clear_vfs;
+ nm_setting_sriov_get_autoprobe_drivers;
+ nm_setting_sriov_get_num_vfs;
+ nm_setting_sriov_get_total_vfs;
+ nm_setting_sriov_get_type;
+ nm_setting_sriov_get_vf;
+ nm_setting_sriov_new;
+ nm_setting_sriov_remove_vf;
+ nm_setting_sriov_remove_vf_by_index;
nm_setting_wpan_get_type;
+ nm_sriov_vf_add_vlan;
+ nm_sriov_vf_dup;
+ nm_sriov_vf_equal;
+ nm_sriov_vf_get_attribute;
+ nm_sriov_vf_get_attribute_names;
+ nm_sriov_vf_get_index;
+ nm_sriov_vf_get_type;
+ nm_sriov_vf_get_vlan_ids;
+ nm_sriov_vf_get_vlan_protocol;
+ nm_sriov_vf_get_vlan_qos;
+ nm_sriov_vf_new;
+ nm_sriov_vf_ref;
+ nm_sriov_vf_remove_vlan;
+ nm_sriov_vf_set_attribute;
+ nm_sriov_vf_set_vlan_protocol;
+ nm_sriov_vf_set_vlan_qos;
+ nm_sriov_vf_unref;
+ nm_sriov_vf_vlan_protocol_get_type;
+ nm_ternary_get_type;
+ nm_utils_sriov_vf_from_str;
+ nm_utils_sriov_vf_to_str;
} libnm_1_12_0;
diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml
index 17bc42f34b..34ab6999d3 100644
--- a/man/NetworkManager.conf.xml
+++ b/man/NetworkManager.conf.xml
@@ -728,6 +728,10 @@ ipv6.ip6-privacy=0
</para></listitem>
</varlistentry>
<varlistentry>
+ <term><varname>sriov.autoprobe-drivers</varname></term>
+ <listitem><para>If left unspecified, drivers are autoprobed when the SR-IOV VF gets created.</para></listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>vpn.timeout</varname></term>
<listitem><para>If left unspecified, default value of 60 seconds is used.</para></listitem>
</varlistentry>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f432d5f0d2..fafc4225d2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -83,6 +83,7 @@ libnm-core/nm-setting-ovs-bridge.c
libnm-core/nm-setting-ppp.c
libnm-core/nm-setting-pppoe.c
libnm-core/nm-setting-proxy.c
+libnm-core/nm-setting-sriov.c
libnm-core/nm-setting-tc-config.c
libnm-core/nm-setting-team.c
libnm-core/nm-setting-team-port.c
diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c
index b984a1ff52..148f3753f9 100644
--- a/shared/nm-meta-setting.c
+++ b/shared/nm-meta-setting.c
@@ -287,6 +287,11 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
.setting_name = NM_SETTING_SERIAL_SETTING_NAME,
.get_setting_gtype = nm_setting_serial_get_type,
},
+ [NM_META_SETTING_TYPE_SRIOV] = {
+ .meta_type = NM_META_SETTING_TYPE_SRIOV,
+ .setting_name = NM_SETTING_SRIOV_SETTING_NAME,
+ .get_setting_gtype = nm_setting_sriov_get_type,
+ },
[NM_META_SETTING_TYPE_TC_CONFIG] = {
.meta_type = NM_META_SETTING_TYPE_TC_CONFIG,
.setting_name = NM_SETTING_TC_CONFIG_SETTING_NAME,
diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h
index e8d4db9c92..dd4780c5d9 100644
--- a/shared/nm-meta-setting.h
+++ b/shared/nm-meta-setting.h
@@ -84,6 +84,7 @@ typedef enum {
NM_META_SETTING_TYPE_PPPOE,
NM_META_SETTING_TYPE_PROXY,
NM_META_SETTING_TYPE_SERIAL,
+ NM_META_SETTING_TYPE_SRIOV,
NM_META_SETTING_TYPE_TC_CONFIG,
NM_META_SETTING_TYPE_TEAM,
NM_META_SETTING_TYPE_TEAM_PORT,
diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h
index d0bb6cf9b8..7d9620b96c 100644
--- a/shared/nm-utils/nm-hash-utils.h
+++ b/shared/nm-utils/nm-hash-utils.h
@@ -209,6 +209,15 @@ guint nm_direct_hash (gconstpointer str);
guint nm_hash_str (const char *str);
guint nm_str_hash (gconstpointer str);
+#define nm_hash_val(static_seed, val) \
+ ({ \
+ NMHashState _h; \
+ \
+ nm_hash_init (&_h, static_seed); \
+ nm_hash_update_val (&_h, val); \
+ nm_hash_complete (&_h); \
+ })
+
/*****************************************************************************/
/* nm_pstr_*() are for hashing keys that are pointers to strings,
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index e60bacebdc..5de0536796 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -161,6 +161,23 @@ _nm_auto_unref_gsource (GSource **ptr)
}
#define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource)
+static inline void
+_nm_auto_freev (gpointer ptr)
+{
+ gpointer **p = ptr;
+ gpointer *_ptr;
+
+ if (*p) {
+ for (_ptr = *p; *_ptr; _ptr++)
+ g_free (*_ptr);
+ g_free (*p);
+ }
+}
+/* g_free a NULL terminated array of pointers, with also freeing each
+ * pointer with g_free(). It essentially does the same as
+ * gs_strfreev / g_strfreev(), but not restricted to strv arrays. */
+#define nm_auto_freev nm_auto(_nm_auto_freev)
+
/*****************************************************************************/
/* http://stackoverflow.com/a/11172679 */
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index aa2a389a5d..2117985e1b 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -724,6 +724,7 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_device_state_reason_to_str, NMDeviceStateReason,
NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED, "ovsdb-failed"),
NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE, "ip-address-duplicate"),
NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED, "ip-method-unsupported"),
+ NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED, "sriov-configuration-failed"),
);
#define reason_to_string(reason) \
@@ -3937,7 +3938,7 @@ nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink
}
static void
-device_init_sriov_num_vfs (NMDevice *self)
+device_init_static_sriov_num_vfs (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
gs_free char *value = NULL;
@@ -3951,8 +3952,8 @@ device_init_sriov_num_vfs (NMDevice *self)
NULL);
num_vfs = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32, -1);
if (num_vfs >= 0) {
- nm_platform_link_set_sriov_num_vfs (nm_device_get_platform (self),
- priv->ifindex, num_vfs);
+ nm_platform_link_set_sriov_params (nm_device_get_platform (self),
+ priv->ifindex, num_vfs, -1);
}
}
}
@@ -3971,7 +3972,7 @@ config_changed (NMConfig *config,
priv->ignore_carrier = nm_config_data_get_ignore_carrier (config_data, self);
if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_VALUES))
- device_init_sriov_num_vfs (self);
+ device_init_static_sriov_num_vfs (self);
}
static void
@@ -4115,7 +4116,7 @@ realize_start_setup (NMDevice *self,
nm_device_set_carrier_from_platform (self);
- device_init_sriov_num_vfs (self);
+ device_init_static_sriov_num_vfs (self);
nm_assert (!priv->stats.timeout_id);
real_rate = _stats_refresh_rate_real (priv->stats.refresh_rate_ms);
@@ -5859,9 +5860,140 @@ lldp_rx_enabled (NMDevice *self)
return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX;
}
+static NMPlatformVF *
+sriov_vf_config_to_platform (NMDevice *self,
+ NMSriovVF *vf,
+ GError **error)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ gs_free NMPlatformVF *plat_vf = NULL;
+ const guint *vlan_ids;
+ GVariant *variant;
+ guint i, num_vlans;
+ gsize length;
+
+ g_return_val_if_fail (!error || !*error, FALSE);
+
+ vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
+ plat_vf = g_malloc0 ( sizeof (NMPlatformVF)
+ + sizeof (NMPlatformVFVlan) * num_vlans);
+
+ plat_vf->index = nm_sriov_vf_get_index (vf);
+
+ variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK);
+ if (variant)
+ plat_vf->spoofchk = g_variant_get_boolean (variant);
+ else
+ plat_vf->spoofchk = -1;
+
+ variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_TRUST);
+ if (variant)
+ plat_vf->trust = g_variant_get_boolean (variant);
+ else
+ plat_vf->trust = -1;
+
+ variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC);
+ if (variant) {
+ if (!_nm_utils_hwaddr_aton (g_variant_get_string (variant, NULL),
+ plat_vf->mac.data,
+ sizeof (plat_vf->mac.data),
+ &length)) {
+ g_set_error (error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_FAILED,
+ "invalid MAC %s",
+ g_variant_get_string (variant, NULL));
+ return NULL;
+ }
+ if (length != priv->hw_addr_len) {
+ g_set_error (error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_FAILED,
+ "wrong MAC length %" G_GSIZE_FORMAT ", should be %u",
+ length, priv->hw_addr_len);
+ return NULL;
+ }
+ plat_vf->mac.len = length;
+ }
+
+ variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE);
+ if (variant)
+ plat_vf->min_tx_rate = g_variant_get_uint32 (variant);
+
+ variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE);
+ if (variant)
+ plat_vf->max_tx_rate = g_variant_get_uint32 (variant);
+
+ plat_vf->num_vlans = num_vlans;
+ plat_vf->vlans = (NMPlatformVFVlan *) (&plat_vf[1]);
+ for (i = 0; i < num_vlans; i++) {
+ plat_vf->vlans[i].id = vlan_ids[i];
+ plat_vf->vlans[i].qos = nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]);
+ plat_vf->vlans[i].proto_ad = nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]) == NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD;
+ }
+
+ return g_steal_pointer (&plat_vf);
+}
+
static NMActStageReturn
act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason)
{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMSettingSriov *s_sriov;
+ guint i, num;
+
+ if ( priv->ifindex > 0
+ && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV)
+ && (s_sriov = (NMSettingSriov *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) {
+ nm_auto_freev NMPlatformVF **plat_vfs = NULL;
+ gs_free_error GError *error = NULL;
+ gs_free const char *str = NULL;
+ NMSriovVF *vf;
+ int autoprobe;
+
+ autoprobe = nm_setting_sriov_get_autoprobe_drivers (s_sriov);
+ if (autoprobe == NM_TERNARY_DEFAULT) {
+ str = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+ "sriov.autoprobe-drivers", self);
+ autoprobe = _nm_utils_ascii_str_to_int64 (str, 10,
+ NM_TERNARY_FALSE,
+ NM_TERNARY_TRUE,
+ NM_TERNARY_TRUE);
+ }
+
+ num = nm_setting_sriov_get_num_vfs (s_sriov);
+ plat_vfs = g_new0 (NMPlatformVF *, num + 1);
+ for (i = 0; i < num; i++) {
+ vf = nm_setting_sriov_get_vf (s_sriov, i);
+ plat_vfs[i] = sriov_vf_config_to_platform (self, vf, &error);
+ if (!plat_vfs[i]) {
+ _LOGE (LOGD_DEVICE,
+ "failed to apply SR-IOV VF '%s': %s",
+ nm_utils_sriov_vf_to_str (vf, FALSE, NULL),
+ error->message);
+ NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+ }
+
+ if (!nm_platform_link_set_sriov_params (nm_device_get_platform (self),
+ priv->ifindex,
+ nm_setting_sriov_get_total_vfs (s_sriov),
+ autoprobe)) {
+ _LOGE (LOGD_DEVICE, "failed to apply SR-IOV parameters");
+ NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+
+ if (!nm_platform_link_set_sriov_vfs (nm_device_get_platform (self),
+ priv->ifindex,
+ (const NMPlatformVF *const *) plat_vfs)) {
+ _LOGE (LOGD_DEVICE, "failed to apply SR-IOV VFs");
+ NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+ }
+
return NM_ACT_STAGE_RETURN_SUCCESS;
}
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index f8f4627ed0..d8d192c712 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -602,12 +602,6 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu)
return NM_PLATFORM_ERROR_SUCCESS;
}
-static gboolean
-link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs)
-{
- return TRUE;
-}
-
static const char *
link_get_udi (NMPlatform *platform, int ifindex)
{
@@ -1422,7 +1416,6 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass)
platform_class->link_set_address = link_set_address;
platform_class->link_set_mtu = link_set_mtu;
- platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs;
platform_class->link_get_driver_info = link_get_driver_info;
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index e02269f6eb..641f7d68c0 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -215,6 +215,46 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1));
/*****************************************************************************/
+/* Redefine VF enums and structures that are not available on older kernels. */
+
+#define IFLA_VF_UNSPEC 0
+#define IFLA_VF_MAC 1
+#define IFLA_VF_VLAN 2
+#define IFLA_VF_TX_RATE 3
+#define IFLA_VF_SPOOFCHK 4
+#define IFLA_VF_LINK_STATE 5
+#define IFLA_VF_RATE 6
+#define IFLA_VF_RSS_QUERY_EN 7
+#define IFLA_VF_STATS 8
+#define IFLA_VF_TRUST 9
+#define IFLA_VF_IB_NODE_GUID 10
+#define IFLA_VF_IB_PORT_GUID 11
+#define IFLA_VF_VLAN_LIST 12
+
+#define IFLA_VF_VLAN_INFO_UNSPEC 0
+#define IFLA_VF_VLAN_INFO 1
+
+/* valid for TRUST, SPOOFCHK, LINK_STATE, RSS_QUERY_EN */
+struct _ifla_vf_setting {
+ guint32 vf;
+ guint32 setting;
+};
+
+struct _ifla_vf_rate {
+ guint32 vf;
+ guint32 min_tx_rate;
+ guint32 max_tx_rate;
+};
+
+struct _ifla_vf_vlan_info {
+ guint32 vf;
+ guint32 vlan; /* 0 - 4095, 0 disables VLAN filter */
+ guint32 qos;
+ guint16 vlan_proto; /* VLAN protocol, either 802.1Q or 802.1ad */
+};
+
+/*****************************************************************************/
+
#define _NMLOG_PREFIX_NAME "platform-linux"
#define _NMLOG_DOMAIN LOGD_PLATFORM
#define _NMLOG2_DOMAIN LOGD_PLATFORM
@@ -5528,16 +5568,18 @@ nla_put_failure:
}
static gboolean
-link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs)
+link_set_sriov_params (NMPlatform *platform,
+ int ifindex,
+ guint num_vfs,
+ int autoprobe)
{
nm_auto_pop_netns NMPNetns *netns = NULL;
nm_auto_close int dirfd = -1;
- int total, current;
+ gboolean current_autoprobe;
+ guint total, current_num;
char ifname[IFNAMSIZ];
char buf[64];
- _LOGD ("link: change %d: num VFs: %u", ifindex, num_vfs);
-
if (!nm_platform_netns_push (platform, &netns))
return FALSE;
@@ -5545,46 +5587,69 @@ link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs)
if (!dirfd)
return FALSE;
- total = nm_platform_sysctl_get_int32 (platform,
- NMP_SYSCTL_PATHID_NETDIR (dirfd,
- ifname,
- "device/sriov_totalvfs"),
- -1);
- if (total < 1)
+ total = nm_platform_sysctl_get_int_checked (platform,
+ NMP_SYSCTL_PATHID_NETDIR (dirfd,
+ ifname,
+ "device/sriov_totalvfs"),
+ 10, 0, G_MAXUINT, 0);
+ if (errno)
return FALSE;
if (num_vfs > total) {
_LOGW ("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs);
num_vfs = total;
}
- current = nm_platform_sysctl_get_int32 (platform,
- NMP_SYSCTL_PATHID_NETDIR (dirfd,
- ifname,
- "device/sriov_numvfs"),
- -1);
- if (current == num_vfs)
+ /*
+ * 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 (dirfd,
+ ifname,
+ "device/sriov_numvfs"),
+ 10, 0, G_MAXUINT, 0);
+ current_autoprobe = nm_platform_sysctl_get_int_checked (platform,
+ NMP_SYSCTL_PATHID_NETDIR (dirfd,
+ ifname,
+ "device/sriov_drivers_autoprobe"),
+ 10, 0, G_MAXUINT, 0);
+ if ( current_num == num_vfs
+ && (autoprobe == -1 || current_autoprobe == autoprobe))
return TRUE;
- if (current != 0) {
- /* We need to destroy all other VFs before changing the value */
+ if (current_num != 0) {
+ /* We need to destroy all other VFs before changing any value */
if (!nm_platform_sysctl_set (NM_PLATFORM_GET,
NMP_SYSCTL_PATHID_NETDIR (dirfd,
ifname,
"device/sriov_numvfs"),
"0")) {
- _LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", 0, strerror (errno));
+ _LOGW ("link: couldn't reset SR-IOV num_vfs: %s", strerror (errno));
return FALSE;
}
- if (num_vfs == 0)
- return TRUE;
}
- /* Finally, set the desired value */
+ if (num_vfs == 0)
+ return TRUE;
+
+ if ( autoprobe >= 0
+ && current_autoprobe != autoprobe
+ && !nm_platform_sysctl_set (NM_PLATFORM_GET,
+ NMP_SYSCTL_PATHID_NETDIR (dirfd,
+ ifname,
+ "device/sriov_drivers_autoprobe"),
+ nm_sprintf_buf (buf, "%d", autoprobe))) {
+ _LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", autoprobe, strerror (errno));
+ return FALSE;
+ }
+
if (!nm_platform_sysctl_set (NM_PLATFORM_GET,
NMP_SYSCTL_PATHID_NETDIR (dirfd,
ifname,
"device/sriov_numvfs"),
- nm_sprintf_buf (buf, "%d", num_vfs))) {
+ nm_sprintf_buf (buf, "%u", num_vfs))) {
_LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", num_vfs, strerror (errno));
return FALSE;
}
@@ -5592,6 +5657,101 @@ link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs)
return TRUE;
}
+static gboolean
+link_set_sriov_vfs (NMPlatform *platform, int ifindex, const NMPlatformVF *const *vfs)
+{
+ nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+ struct nlattr *list, *info, *vlan_list;
+ guint i;
+
+ nlmsg = _nl_msg_new_link (RTM_NEWLINK,
+ 0,
+ ifindex,
+ NULL,
+ 0,
+ 0);
+ if (!nlmsg)
+ g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED);
+
+ if (!(list = nla_nest_start (nlmsg, IFLA_VFINFO_LIST)))
+ goto nla_put_failure;
+
+ for (i = 0; vfs[i]; i++) {
+ const NMPlatformVF *vf = vfs[i];
+
+ if (!(info = nla_nest_start (nlmsg, IFLA_VF_INFO)))
+ goto nla_put_failure;
+
+ if (vf->spoofchk >= 0) {
+ struct _ifla_vf_setting ivs = { 0 };
+
+ ivs.vf = vf->index;
+ ivs.setting = vf->spoofchk;
+ NLA_PUT (nlmsg, IFLA_VF_SPOOFCHK, sizeof (ivs), &ivs);
+ }
+
+ if (vf->trust >= 0) {
+ struct _ifla_vf_setting ivs = { 0 };
+
+ ivs.vf = vf->index;
+ ivs.setting = vf->trust;
+ NLA_PUT (nlmsg, IFLA_VF_TRUST, sizeof (ivs), &ivs);
+ }
+
+ if (vf->mac.len) {
+ struct ifla_vf_mac ivm = { 0 };
+
+ ivm.vf = vf->index;
+ memcpy (ivm.mac, vf->mac.data, vf->mac.len);
+ NLA_PUT (nlmsg, IFLA_VF_MAC, sizeof (ivm), &ivm);
+ }
+
+ if (vf->min_tx_rate || vf->max_tx_rate) {
+ struct _ifla_vf_rate ivr = { 0 };
+
+ ivr.vf = vf->index;
+ ivr.min_tx_rate = vf->min_tx_rate;
+ ivr.max_tx_rate = vf->max_tx_rate;
+ NLA_PUT (nlmsg, IFLA_VF_RATE, sizeof (ivr), &ivr);
+ }
+
+ /* Kernel only supports one VLAN per VF now. If this
+ * changes in the future, we need to figure out how to
+ * clear existing VLANs and set new ones in one message
+ * with the new API.*/
+ if (vf->num_vlans > 1) {
+ _LOGW ("multiple VLANs per VF are not supported at the moment");
+ return FALSE;
+ } else {
+ struct _ifla_vf_vlan_info ivvi = { 0 };
+
+ if (!(vlan_list = nla_nest_start (nlmsg, IFLA_VF_VLAN_LIST)))
+ goto nla_put_failure;
+
+ ivvi.vf = vf->index;
+ if (vf->num_vlans == 1) {
+ ivvi.vlan = vf->vlans[0].id;
+ ivvi.qos = vf->vlans[0].qos;
+ ivvi.vlan_proto = htons (vf->vlans[0].proto_ad ? ETH_P_8021AD : ETH_P_8021Q);
+ } else {
+ /* Clear existing VLAN */
+ ivvi.vlan = 0;
+ ivvi.qos = 0;
+ ivvi.vlan_proto = htons (ETH_P_8021Q);
+ }
+
+ NLA_PUT (nlmsg, IFLA_VF_VLAN_INFO, sizeof (ivvi), &ivvi);
+ nla_nest_end (nlmsg, vlan_list);
+ }
+ nla_nest_end (nlmsg, info);
+ }
+ nla_nest_end (nlmsg, list);
+
+ return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS;
+nla_put_failure:
+ g_return_val_if_reached (FALSE);
+}
+
static char *
link_get_physical_port_id (NMPlatform *platform, int ifindex)
{
@@ -7735,7 +7895,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
platform_class->link_get_permanent_address = link_get_permanent_address;
platform_class->link_set_mtu = link_set_mtu;
platform_class->link_set_name = link_set_name;
- platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs;
+ platform_class->link_set_sriov_params = link_set_sriov_params;
+ platform_class->link_set_sriov_vfs = link_set_sriov_vfs;
platform_class->link_get_physical_port_id = link_get_physical_port_id;
platform_class->link_get_dev_id = link_get_dev_id;
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 96465e3dd5..174d863637 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -1463,18 +1463,52 @@ nm_platform_link_supports_sriov (NMPlatform *self, int ifindex)
return klass->link_supports_sriov (self, ifindex);
}
+/**
+ * nm_platform_link_set_sriov_params:
+ * @self: platform instance
+ * @ifindex: the index of the interface to change
+ * @num_vfs: the number of VFs to create
+ * @autoprobe: -1 to keep the current autoprobe-drivers value,
+ * or {0,1} to set a new value
+ */
gboolean
-nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs)
+nm_platform_link_set_sriov_params (NMPlatform *self,
+ int ifindex,
+ guint num_vfs,
+ int autoprobe)
{
_CHECK_SELF (self, klass, FALSE);
g_return_val_if_fail (ifindex > 0, FALSE);
+ g_return_val_if_fail (NM_IN_SET (autoprobe, -1, 0, 1), FALSE);
- _LOGD ("link: setting %u VFs for %s (%d)",
+ _LOGD ("link: setting %u total VFs and autoprobe %d for %s (%d)",
num_vfs,
+ autoprobe,
nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)),
ifindex);
- return klass->link_set_sriov_num_vfs (self, ifindex, num_vfs);
+ return klass->link_set_sriov_params (self, ifindex, num_vfs, autoprobe);
+}
+
+gboolean
+nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs)
+{
+ guint i;
+ _CHECK_SELF (self, klass, FALSE);
+
+ g_return_val_if_fail (ifindex > 0, FALSE);
+
+ _LOGD ("link: setting VFs for \"%s\" (%d):",
+ nm_platform_link_get_name (self, ifindex),
+ ifindex);
+
+ for (i = 0; vfs[i]; i++) {
+ const NMPlatformVF *vf = vfs[i];
+
+ _LOGD ("link: VF %s", nm_platform_vf_to_string (vf, NULL, 0));
+ }
+
+ return klass->link_set_sriov_vfs (self, ifindex, vfs);
}
/**
@@ -6042,6 +6076,56 @@ nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b)
return 0;
}
+const char *
+nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len)
+{
+ char str_mac[128], mac[128];
+ char str_spoof_check[64];
+ char str_trust[64];
+ char str_min_tx_rate[64];
+ char str_max_tx_rate[64];
+ nm_auto_free_gstring GString *gstr_vlans = NULL;
+ guint i;
+
+ if (!nm_utils_to_string_buffer_init_null (vf, &buf, &len))
+ return buf;
+
+ if (vf->mac.len) {
+ nm_utils_hwaddr_ntoa_buf (vf->mac.data, vf->mac.len, TRUE, mac, sizeof (mac));
+ nm_sprintf_buf (str_mac, " mac %s", mac);
+ } else
+ str_mac[0] = '\0';
+
+ if (vf->num_vlans) {
+ gstr_vlans = g_string_new ("");
+ for (i = 0; i < vf->num_vlans; i++) {
+ g_string_append_printf (gstr_vlans, " vlan %u", (unsigned) vf->vlans[i].id);
+ if (vf->vlans[i].qos)
+ g_string_append_printf (gstr_vlans, " qos %u", (unsigned) vf->vlans[i].qos);
+ if (vf->vlans[i].proto_ad)
+ g_string_append (gstr_vlans, " proto 802.1ad");
+ }
+ }
+
+ g_snprintf (buf, len,
+ "%u" /* index */
+ "%s" /* MAC */
+ "%s" /* spoof check */
+ "%s" /* trust */
+ "%s" /* min tx rate */
+ "%s" /* max tx rate */
+ "%s", /* VLANs */
+ vf->index,
+ str_mac,
+ vf->spoofchk >= 0 ? nm_sprintf_buf (str_spoof_check, " spoofchk %d", vf->spoofchk) : "",
+ vf->trust >= 0 ? nm_sprintf_buf (str_trust, " trust %d", vf->trust) : "",
+ vf->min_tx_rate ? nm_sprintf_buf (str_min_tx_rate, " min_tx_rate %u", (unsigned) vf->min_tx_rate) : "",
+ vf->max_tx_rate ? nm_sprintf_buf (str_max_tx_rate, " max_tx_rate %u", (unsigned) vf->max_tx_rate) : "",
+ gstr_vlans ? gstr_vlans->str : "");
+
+ return buf;
+}
+
void
nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h)
{
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 00df7fa31d..7e49e064a9 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -608,6 +608,26 @@ extern const NMPlatformVTableRoute nm_platform_vtable_route_v4;
extern const NMPlatformVTableRoute nm_platform_vtable_route_v6;
typedef struct {
+ guint16 id;
+ guint32 qos;
+ bool proto_ad:1;
+} NMPlatformVFVlan;
+
+typedef struct {
+ guint32 index;
+ guint32 min_tx_rate;
+ guint32 max_tx_rate;
+ guint num_vlans;
+ NMPlatformVFVlan *vlans;
+ struct {
+ guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */
+ guint8 len;
+ } mac;
+ gint8 spoofchk;
+ gint8 trust;
+} NMPlatformVF;
+
+typedef struct {
in_addr_t local;
in_addr_t remote;
int parent_ifindex;
@@ -802,7 +822,8 @@ typedef struct {
NMPlatformError (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length);
NMPlatformError (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu);
gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name);
- gboolean (*link_set_sriov_num_vfs) (NMPlatform *, int ifindex, guint num_vfs);
+ gboolean (*link_set_sriov_params) (NMPlatform *, int ifindex, guint num_vfs, int autoprobe);
+ gboolean (*link_set_sriov_vfs) (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs);
char * (*link_get_physical_port_id) (NMPlatform *, int ifindex);
guint (*link_get_dev_id) (NMPlatform *, int ifindex);
@@ -1193,7 +1214,8 @@ gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex,
NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length);
NMPlatformError nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu);
gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name);
-gboolean nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs);
+gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, int autoprobe);
+gboolean nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs);
char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex);
guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex);
@@ -1434,6 +1456,7 @@ const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, ch
const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len);
const char *nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len);
const char *nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len);
+const char *nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len);
const char *nm_platform_vlan_qos_mapping_to_string (const char *name,
const NMVlanQosMapping *map,
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index 57394791a6..5cea628013 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -1178,7 +1178,7 @@ make_user_setting (shvarFile *ifcfg)
const char *key;
nm_auto_free_gstring GString *str = NULL;
- keys = svGetKeys (ifcfg);
+ keys = svGetKeys (ifcfg, SV_KEY_TYPE_USER);
if (!keys)
return NULL;
@@ -1187,9 +1187,6 @@ make_user_setting (shvarFile *ifcfg)
const char *value;
gs_free char *value_to_free = NULL;
- if (!g_str_has_prefix (key, "NM_USER_"))
- continue;
-
value = svGetValue (ifcfg, key, &value_to_free);
if (!value)
@@ -1982,6 +1979,68 @@ error:
}
static NMSetting *
+make_sriov_setting (shvarFile *ifcfg)
+{
+ gs_unref_hashtable GHashTable *keys = NULL;
+ gs_unref_ptrarray GPtrArray *vfs = NULL;
+ NMTernary autoprobe_drivers;
+ NMSettingSriov *s_sriov;
+ int total_vfs;
+
+ total_vfs = svGetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", 10, 0, G_MAXINT32, 0);
+ if (!total_vfs)
+ return NULL;
+
+ autoprobe_drivers = svGetValueInt64 (ifcfg,
+ "SRIOV_AUTOPROBE_DRIVERS",
+ 10,
+ NM_TERNARY_FALSE,
+ NM_TERNARY_TRUE,
+ NM_TERNARY_DEFAULT);
+
+ keys = svGetKeys (ifcfg, SV_KEY_TYPE_SRIOV_VF);
+ if (keys) {
+ GHashTableIter iter;
+ const char *key;
+
+ g_hash_table_iter_init (&iter, keys);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
+ gs_free_error GError *error = NULL;
+ gs_free char *value_to_free = NULL;
+ const char *value;
+ NMSriovVF *vf;
+
+ nm_assert (g_str_has_prefix (key, "SRIOV_VF"));
+
+ value = svGetValue (ifcfg, key, &value_to_free);
+ if (!value)
+ continue;
+
+ key += NM_STRLEN ("SRIOV_VF");
+
+ vf = _nm_utils_sriov_vf_from_strparts (key, value, &error);
+ if (!vf) {
+ PARSE_WARNING ("ignoring invalid SR-IOV VF '%s %s': %s",
+ key, value, error->message);
+ continue;
+ }
+ if (!vfs)
+ vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+ g_ptr_array_add (vfs, vf);
+ }
+ }
+
+ s_sriov = (NMSettingSriov *) nm_setting_sriov_new ();
+ g_object_set (s_sriov,
+ NM_SETTING_SRIOV_TOTAL_VFS, total_vfs,
+ NM_SETTING_SRIOV_VFS, vfs,
+ NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, (int) autoprobe_drivers,
+ NULL);
+
+ return (NMSetting *) s_sriov;
+}
+
+static NMSetting *
make_tc_setting (shvarFile *ifcfg)
{
NMSettingTCConfig *s_tc = NULL;
@@ -5327,6 +5386,7 @@ connection_from_file_full (const char *filename,
gs_free char *type = NULL;
char *devtype, *bootproto;
NMSetting *s_ip4, *s_ip6, *s_tc, *s_proxy, *s_port, *s_dcb = NULL, *s_user;
+ NMSetting *s_sriov;
const char *ifcfg_name = NULL;
gboolean has_ip4_defroute = FALSE;
gboolean has_complex_routes_v4;
@@ -5582,6 +5642,10 @@ connection_from_file_full (const char *filename,
nm_connection_add_setting (connection, s_ip4);
}
+ s_sriov = make_sriov_setting (parsed);
+ if (s_sriov)
+ nm_connection_add_setting (connection, s_sriov);
+
s_tc = make_tc_setting (parsed);
if (s_tc)
nm_connection_add_setting (connection, s_tc);
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index 8921646597..ab1938f180 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -2135,6 +2135,45 @@ write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
return TRUE;
}
+static void
+write_sriov_setting (NMConnection *connection, shvarFile *ifcfg)
+{
+ NMSettingSriov *s_sriov;
+ guint i, num = 0;
+ NMTernary b;
+ NMSriovVF *vf;
+ char key[32];
+ char *str;
+
+ svUnsetAll (ifcfg, SV_KEY_TYPE_SRIOV_VF);
+
+ s_sriov = nm_connection_get_setting_sriov (connection);
+ if (s_sriov)
+ num = nm_setting_sriov_get_total_vfs (s_sriov);
+ if (num == 0) {
+ svUnsetValue (ifcfg, "SRIOV_TOTAL_VFS");
+ svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS");
+ return;
+ }
+
+ svSetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", num);
+
+ b = nm_setting_sriov_get_autoprobe_drivers (s_sriov);
+ if (b != NM_TERNARY_DEFAULT)
+ svSetValueInt64 (ifcfg, "SRIOV_AUTOPROBE_DRIVERS", b);
+ else
+ svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS");
+
+ num = nm_setting_sriov_get_num_vfs (s_sriov);
+ for (i = 0; i < num; i++) {
+ vf = nm_setting_sriov_get_vf (s_sriov, i);
+ nm_sprintf_buf (key, "SRIOV_VF%u", nm_sriov_vf_get_index (vf));
+ str = nm_utils_sriov_vf_to_str (vf, TRUE, NULL);
+ svSetValueStr (ifcfg, key, str);
+ g_free (str);
+ }
+}
+
static gboolean
write_tc_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
{
@@ -2921,6 +2960,8 @@ do_write_construct (NMConnection *connection,
if (!write_user_setting (connection, ifcfg, error))
return FALSE;
+ write_sriov_setting (connection, ifcfg);
+
if (!write_tc_setting (connection, ifcfg, error))
return FALSE;
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index c739b19dec..f09bc2472f 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -870,8 +870,61 @@ svCreateFile (const char *name)
/*****************************************************************************/
+static gboolean
+_is_all_digits (const char *str)
+{
+ return str[0]
+ && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch));
+}
+
+#define IS_NUMBERED_TAG(key, tab_name) \
+ ({ \
+ const char *_key = (key); \
+ \
+ ( (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \
+ && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \
+ })
+
+static gboolean
+_svKeyMatchesType (const char *key, SvKeyType match_key_type)
+{
+ if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY))
+ return TRUE;
+
+ if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) {
+ if ( IS_NUMBERED_TAG (key, "ADDRESS")
+ || IS_NUMBERED_TAG (key, "NETMASK")
+ || IS_NUMBERED_TAG (key, "GATEWAY")
+ || IS_NUMBERED_TAG (key, "METRIC")
+ || IS_NUMBERED_TAG (key, "OPTIONS"))
+ return TRUE;
+ }
+ if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) {
+ if ( IS_NUMBERED_TAG (key, "IPADDR")
+ || IS_NUMBERED_TAG (key, "PREFIX")
+ || IS_NUMBERED_TAG (key, "NETMASK")
+ || IS_NUMBERED_TAG (key, "GATEWAY"))
+ return TRUE;
+ }
+ if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) {
+ if (g_str_has_prefix (key, "NM_USER_"))
+ return TRUE;
+ }
+ if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_TC)) {
+ if ( IS_NUMBERED_TAG (key, "QDISC")
+ || IS_NUMBERED_TAG (key, "FILTER"))
+ return TRUE;
+ }
+ if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_SRIOV_VF)) {
+ if (IS_NUMBERED_TAG (key, "SRIOV_VF"))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
GHashTable *
-svGetKeys (shvarFile *s)
+svGetKeys (shvarFile *s, SvKeyType match_key_type)
{
GHashTable *keys = NULL;
CList *current;
@@ -881,7 +934,9 @@ svGetKeys (shvarFile *s)
c_list_for_each (current, &s->lst_head) {
line = c_list_entry (current, shvarLine, lst);
- if (line->key && line->line) {
+ if ( line->key
+ && line->line
+ && _svKeyMatchesType (line->key, match_key_type)) {
/* we don't clone the keys. The keys are only valid
* until @s gets modified. */
if (!keys)
@@ -1120,21 +1175,6 @@ svGetValueEnum (shvarFile *s, const char *key,
/*****************************************************************************/
-static gboolean
-_is_all_digits (const char *str)
-{
- return str[0]
- && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch));
-}
-
-#define IS_NUMBERED_TAG(key, tab_name) \
- ({ \
- const char *_key = (key); \
- \
- ( (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \
- && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \
- })
-
gboolean
svUnsetAll (shvarFile *s, SvKeyType match_key_type)
{
@@ -1150,38 +1190,11 @@ svUnsetAll (shvarFile *s, SvKeyType match_key_type)
if (!line->key)
continue;
- if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY))
- goto do_clear;
- if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) {
- if ( IS_NUMBERED_TAG (line->key, "ADDRESS")
- || IS_NUMBERED_TAG (line->key, "NETMASK")
- || IS_NUMBERED_TAG (line->key, "GATEWAY")
- || IS_NUMBERED_TAG (line->key, "METRIC")
- || IS_NUMBERED_TAG (line->key, "OPTIONS"))
- goto do_clear;
- }
- if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) {
- if ( IS_NUMBERED_TAG (line->key, "IPADDR")
- || IS_NUMBERED_TAG (line->key, "PREFIX")
- || IS_NUMBERED_TAG (line->key, "NETMASK")
- || IS_NUMBERED_TAG (line->key, "GATEWAY"))
- goto do_clear;
- }
- if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) {
- if (g_str_has_prefix (line->key, "NM_USER_"))
- goto do_clear;
- }
- if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_TC)) {
- if ( IS_NUMBERED_TAG (line->key, "QDISC")
- || IS_NUMBERED_TAG (line->key, "FILTER"))
- goto do_clear;
- }
-
- continue;
-do_clear:
- if (nm_clear_g_free (&line->line)) {
- ASSERT_shvarLine (line);
- changed = TRUE;
+ if (_svKeyMatchesType (line->key, match_key_type)) {
+ if (nm_clear_g_free (&line->line)) {
+ ASSERT_shvarLine (line);
+ changed = TRUE;
+ }
}
}
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index 838c79cc16..622bb474b1 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -33,6 +33,15 @@
typedef struct _shvarFile shvarFile;
+typedef enum {
+ SV_KEY_TYPE_ANY = (1LL << 0),
+ SV_KEY_TYPE_ROUTE_SVFORMAT = (1LL << 1),
+ SV_KEY_TYPE_IP4_ADDRESS = (1LL << 2),
+ SV_KEY_TYPE_TC = (1LL << 3),
+ SV_KEY_TYPE_USER = (1LL << 4),
+ SV_KEY_TYPE_SRIOV_VF = (1LL << 5),
+} SvKeyType;
+
const char *svFileGetName (const shvarFile *s);
void _nmtst_svFileSetName (shvarFile *s, const char *fileName);
@@ -58,7 +67,7 @@ char *svGetValueStr_cp (shvarFile *s, const char *key);
int svParseBoolean (const char *value, int def);
-GHashTable *svGetKeys (shvarFile *s);
+GHashTable *svGetKeys (shvarFile *s, SvKeyType match_key_type);
/* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true")
* return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false")
@@ -85,15 +94,6 @@ gboolean svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, g
gboolean svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value);
gboolean svUnsetValue (shvarFile *s, const char *key);
-
-typedef enum {
- SV_KEY_TYPE_ANY = (1LL << 0),
- SV_KEY_TYPE_ROUTE_SVFORMAT = (1LL << 1),
- SV_KEY_TYPE_IP4_ADDRESS = (1LL << 2),
- SV_KEY_TYPE_TC = (1LL << 3),
- SV_KEY_TYPE_USER = (1LL << 4),
-} SvKeyType;
-
gboolean svUnsetAll (shvarFile *s, SvKeyType match_key_type);
/* Write the current contents iff modified. Returns FALSE on error
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov
new file mode 100644
index 0000000000..142f56e434
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov
@@ -0,0 +1,19 @@
+TYPE=Ethernet
+DEVICE=eth0
+HWADDR=00:11:22:33:44:55
+BOOTPROTO=none
+ONBOOT=yes
+DNS1=4.2.2.1
+DNS2=4.2.2.2
+IPADDR=192.168.1.5
+PREFIX=24
+NETMASK=255.255.255.0
+GATEWAY=192.168.1.1
+IPV6INIT=no
+NAME=ethernet-sriov
+UUID=acc703b8-e751-44ce-b456-1550bdf2057e
+SRIOV_TOTAL_VFS=16
+SRIOV_AUTOPROBE_DRIVERS=0
+SRIOV_VF15="max-tx-rate=200 mac=01:23:45:67:89:ab vlans=2"
+SRIOV_VF12="trust=false min-tx-rate=100 vlans=1.200.ad"
+SRIOV_VF3="mac=55:44:33:22:11:00 spoof-check=true"
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected
new file mode 100644
index 0000000000..c882c4796e
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected
@@ -0,0 +1,18 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+SRIOV_TOTAL_VFS=64
+SRIOV_AUTOPROBE_DRIVERS=1
+SRIOV_VF2="mac=55:55:55:55:55:55 vlans=3.10.ad;10"
+SRIOV_VF19=spoof-check=true
+BOOTPROTO=none
+IPADDR=1.1.1.3
+PREFIX=24
+GATEWAY=1.1.1.1
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write SR-IOV config"
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 1331391dc2..8d4efbbfd1 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -9632,6 +9632,153 @@ test_utils_ignore (void)
do_test_utils_ignored ("ignored-augtmp", "ifcfg-FooBar" AUGTMP_TAG, TRUE);
}
+/*****************************************************************************/
+
+static void
+test_sriov_read (void)
+{
+ gs_unref_object NMConnection *connection = NULL;
+ NMSettingSriov *s_sriov;
+ NMSriovVF *vf;
+ GVariant *variant;
+ GError *error = NULL;
+ char *str;
+
+ connection = _connection_from_file (TEST_IFCFG_DIR "/ifcfg-test-sriov",
+ NULL, TYPE_ETHERNET,NULL);
+
+ g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "eth0");
+
+ s_sriov = nm_connection_get_setting_sriov (connection);
+ g_assert (s_sriov);
+
+ g_assert_cmpint (nm_setting_sriov_get_total_vfs (s_sriov), ==, 16);
+ g_assert_cmpint (nm_setting_sriov_get_num_vfs (s_sriov), ==, 3);
+ g_assert_cmpint (nm_setting_sriov_get_autoprobe_drivers (s_sriov), ==, NM_TERNARY_FALSE);
+
+ /* VF 3 */
+ vf = nm_setting_sriov_get_vf (s_sriov, 0);
+ g_assert (vf);
+ g_assert_cmpint (nm_sriov_vf_get_index (vf), ==, 3);
+
+ variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC);
+ g_assert (variant);
+ g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING));
+ g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "55:44:33:22:11:00");
+
+ variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK);
+ g_assert (variant);
+ g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN));
+ g_assert_cmpint (g_variant_get_boolean (variant), ==, TRUE);
+
+ /* VF 12 */
+ vf = nm_setting_sriov_get_vf (s_sriov, 1);
+ str = nm_utils_sriov_vf_to_str (vf, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (str, ==, "12 min-tx-rate=100 trust=false vlans=1.200.ad");
+ g_free (str);
+
+ /* VF 15 */
+ vf = nm_setting_sriov_get_vf (s_sriov, 2);
+ str = nm_utils_sriov_vf_to_str (vf, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (str, ==, "15 mac=01:23:45:67:89:ab max-tx-rate=200 vlans=2");
+ g_free (str);
+}
+
+static void
+test_sriov_write (void)
+{
+ nmtst_auto_unlinkfile char *testfile = NULL;
+ gs_unref_object NMConnection *connection = NULL;
+ gs_unref_object NMConnection *reread = NULL;
+ NMSettingConnection *s_con;
+ NMSettingIPConfig *s_ip4;
+ NMSettingIPConfig *s_ip6;
+ NMSettingWired *s_wired;
+ NMSettingSriov *s_sriov;
+ NMSriovVF *vf;
+ gs_unref_ptrarray GPtrArray *vfs = NULL;
+ NMIPAddress *addr;
+ GError *error = NULL;
+
+ connection = nm_simple_connection_new ();
+
+ /* Connection setting */
+ s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_ID, "Test Write SR-IOV config",
+ NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
+ NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+ NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0",
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+ NULL);
+
+ /* Wired setting */
+ s_wired = (NMSettingWired *) nm_setting_wired_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+ /* IP4 setting */
+ s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+ g_object_set (s_ip4,
+ NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+ NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1",
+ NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
+ NULL);
+
+ addr = nm_ip_address_new (AF_INET, "1.1.1.3", 24, &error);
+ g_assert_no_error (error);
+ nm_setting_ip_config_add_address (s_ip4, addr);
+ nm_ip_address_unref (addr);
+
+ /* IP6 setting */
+ s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_ip6));
+
+ g_object_set (s_ip6,
+ NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+ NULL);
+
+ /* SRIOV setting */
+ s_sriov = (NMSettingSriov *) nm_setting_sriov_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_sriov));
+
+ vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+
+ vf = nm_utils_sriov_vf_from_str ("2 mac=55:55:55:55:55:55 vlans=3.10.ad;10", &error);
+ nmtst_assert_success (vf, error);
+ g_ptr_array_add (vfs, vf);
+
+ vf = nm_utils_sriov_vf_from_str ("19 spoof-check=true", &error);
+ nmtst_assert_success (vf, error);
+ g_ptr_array_add (vfs, vf);
+
+ g_object_set (s_sriov,
+ NM_SETTING_SRIOV_TOTAL_VFS, 64,
+ NM_SETTING_SRIOV_VFS, vfs,
+ NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, NM_TERNARY_TRUE,
+ NULL);
+
+ nm_connection_add_setting (connection, nm_setting_proxy_new ());
+
+ nmtst_assert_connection_verifies_without_normalization (connection);
+
+ _writer_new_connec_exp (connection,
+ TEST_SCRATCH_DIR,
+ TEST_IFCFG_DIR "/ifcfg-test-sriov-write.cexpected",
+ &testfile);
+
+ reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
+
+ nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+}
+
+/*****************************************************************************/
+
static void
test_tc_read (void)
{
@@ -10033,6 +10180,9 @@ int main (int argc, char **argv)
g_test_add_func (TPATH "utils/path", test_utils_path);
g_test_add_func (TPATH "utils/ignore", test_utils_ignore);
+ g_test_add_func (TPATH "sriov/read", test_sriov_read);
+ g_test_add_func (TPATH "sriov/write", test_sriov_write);
+
g_test_add_func (TPATH "tc/read", test_tc_read);
g_test_add_func (TPATH "tc/write", test_tc_write);