diff options
Diffstat (limited to 'libnm-core/nm-setting-sriov.c')
| -rw-r--r-- | libnm-core/nm-setting-sriov.c | 1357 |
1 files changed, 1357 insertions, 0 deletions
diff --git a/libnm-core/nm-setting-sriov.c b/libnm-core/nm-setting-sriov.c new file mode 100644 index 00000000..7228fb0c --- /dev/null +++ b/libnm-core/nm-setting-sriov.c @@ -0,0 +1,1357 @@ +/* -*- 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 (NMSettingSriov, nm_setting_sriov, NM_TYPE_SETTING) + +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 (g_variant_type_equal (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 *setting_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; + } + + setting_class = NM_SETTING_CLASS (nm_setting_sriov_parent_class); + return setting_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 *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingClass *setting_class = NM_SETTING_CLASS (klass); + GArray *properties_override = _nm_sett_info_property_override_create_array (); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + setting_class->compare_property = compare_property; + setting_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)); + + _properties_override_add_override (properties_override, + g_object_class_find_property (G_OBJECT_CLASS (setting_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)); + + _nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_SRIOV, + NULL, properties_override); +} |