platform: add nmp_utils_bridge_normalized_vlans_equal()

Add a function to compare two arrays of NMPlatformBridgeVlan. It will
be used in the next commit to compare the VLANs from platform to the
ones we want to set.

To compare in a performant way, the vlans need to be normalized (no
duplicated VLANS, ranges into their minimal expression...). Add the
function nmp_utils_bridge_vlan_normalize.

Co-authored-by: Íñigo Huguet <ihuguet@redhat.com>
This commit is contained in:
Beniamino Galvani
2024-07-26 15:17:58 +02:00
committed by Íñigo Huguet
parent 7ae4660a77
commit 1c43fe5235
5 changed files with 336 additions and 7 deletions

View File

@@ -2275,6 +2275,89 @@ nmp_utils_lifetime_get(guint32 timestamp,
/*****************************************************************************/
static int
bridge_vlan_compare(gconstpointer a, gconstpointer b, gpointer user_data)
{
const NMPlatformBridgeVlan *vlan_a = a;
const NMPlatformBridgeVlan *vlan_b = b;
return (int) vlan_a->vid_start - (int) vlan_b->vid_start;
}
/**
* nmp_utils_bridge_vlan_normalize:
* @vlans: the array of VLAN ranges
* @num_vlans: the number of VLAN ranges in the array. On return, it contains
* the new number.
*
* Sort the VLAN ranges and merge those that are contiguous or overlapping. It
* must not contain invalid data such as 2 overlapping ranges with different
* flags.
*/
void
nmp_utils_bridge_vlan_normalize(NMPlatformBridgeVlan *vlans, guint *num_vlans)
{
guint i;
if (*num_vlans <= 1)
return;
g_qsort_with_data(vlans, *num_vlans, sizeof(NMPlatformBridgeVlan), bridge_vlan_compare, NULL);
/* Merge VLAN ranges that are contiguous or overlap */
i = 0;
while (i < *num_vlans - 1) {
guint j = i + 1;
gboolean can_merge = vlans[j].vid_start <= vlans[i].vid_end + 1
&& vlans[j].pvid == vlans[i].pvid
&& vlans[j].untagged == vlans[i].untagged;
if (can_merge) {
vlans[i].vid_end = NM_MAX(vlans[i].vid_end, vlans[j].vid_end);
for (; j < *num_vlans - 1; j++)
vlans[j] = vlans[j + 1];
*num_vlans -= 1;
} else {
i++;
}
}
}
/**
* nmp_utils_bridge_normalized_vlans_equal:
* @vlans_a: the first array of bridge VLANs
* @num_vlans_a: the number of elements of first array
* @vlans_b: the second array of bridge VLANs
* @num_vlans_b: the number of elements of second array
*
* Given two arrays of bridge VLAN ranges, compare if they are equal,
* i.e. if they represent the same set of VLANs with the same attributes.
* The input arrays must be normalized (sorted and without overlapping or
* duplicated ranges). Normalize with nmp_utils_bridge_vlan_normalize().
*/
gboolean
nmp_utils_bridge_normalized_vlans_equal(const NMPlatformBridgeVlan *vlans_a,
guint num_vlans_a,
const NMPlatformBridgeVlan *vlans_b,
guint num_vlans_b)
{
guint i;
if (num_vlans_a != num_vlans_b)
return FALSE;
for (i = 0; i < num_vlans_a; i++) {
if (vlans_a[i].vid_start != vlans_b[i].vid_start || vlans_a[i].vid_end != vlans_b[i].vid_end
|| vlans_a[i].pvid != vlans_b[i].pvid || vlans_a[i].untagged != vlans_b[i].untagged) {
return FALSE;
}
}
return TRUE;
}
/*****************************************************************************/
static const char *
_trunk_first_line(char *str)
{

View File

@@ -99,4 +99,11 @@ guint32 nmp_utils_lifetime_get(guint32 timestamp,
int nmp_utils_modprobe(GError **error, gboolean suppress_error_logging, const char *arg1, ...)
G_GNUC_NULL_TERMINATED;
void nmp_utils_bridge_vlan_normalize(NMPlatformBridgeVlan *vlans, guint *num_vlans);
gboolean nmp_utils_bridge_normalized_vlans_equal(const NMPlatformBridgeVlan *vlans_a,
guint num_vlans_a,
const NMPlatformBridgeVlan *vlans_b,
guint num_vlans_b);
#endif /* __NM_PLATFORM_UTILS_H__ */

View File

@@ -741,13 +741,6 @@ typedef struct {
gint8 trust;
} NMPlatformVF;
typedef struct {
guint16 vid_start;
guint16 vid_end;
bool untagged : 1;
bool pvid : 1;
} NMPlatformBridgeVlan;
typedef struct {
guint16 vlan_default_pvid_val;
bool vlan_filtering_val : 1;

View File

@@ -38,6 +38,15 @@ typedef enum {
/*****************************************************************************/
typedef struct {
guint16 vid_start;
guint16 vid_end;
bool untagged : 1;
bool pvid : 1;
} NMPlatformBridgeVlan;
/*****************************************************************************/
typedef struct {
/* We don't want to include <linux/ethtool.h> in header files,
* thus create a ABI compatible version of struct ethtool_drvinfo.*/

View File

@@ -190,6 +190,239 @@ test_nmp_link_mode_all_advertised_modes_bits(void)
/*****************************************************************************/
static void
test_nmp_utils_bridge_vlans_normalize(void)
{
NMPlatformBridgeVlan vlans[10];
NMPlatformBridgeVlan expect[10];
guint vlans_len;
/* Single one is unmodified */
vlans[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
expect[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
vlans_len = 1;
nmp_utils_bridge_vlan_normalize(vlans, &vlans_len);
g_assert(vlans_len == 1);
g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len));
/* Not merged if flags are different */
vlans[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
vlans[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 11,
.pvid = TRUE,
};
vlans[2] = (NMPlatformBridgeVlan){
.vid_start = 20,
.vid_end = 25,
};
vlans[3] = (NMPlatformBridgeVlan){
.vid_start = 26,
.vid_end = 30,
.untagged = TRUE,
};
vlans[4] = (NMPlatformBridgeVlan){
.vid_start = 40,
.vid_end = 40,
.untagged = TRUE,
};
vlans[5] = (NMPlatformBridgeVlan){
.vid_start = 40,
.vid_end = 40,
.untagged = TRUE,
.pvid = TRUE,
};
expect[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
expect[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 11,
.pvid = TRUE,
};
expect[2] = (NMPlatformBridgeVlan){
.vid_start = 20,
.vid_end = 25,
};
expect[3] = (NMPlatformBridgeVlan){
.vid_start = 26,
.vid_end = 30,
.untagged = TRUE,
};
expect[4] = (NMPlatformBridgeVlan){
.vid_start = 40,
.vid_end = 40,
.untagged = TRUE,
};
expect[5] = (NMPlatformBridgeVlan){
.vid_start = 40,
.vid_end = 40,
.untagged = TRUE,
.pvid = TRUE,
};
vlans_len = 6;
nmp_utils_bridge_vlan_normalize(vlans, &vlans_len);
g_assert(vlans_len == 6);
g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len));
/* Overlapping and contiguous ranges are merged */
vlans[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
vlans[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 20,
.untagged = TRUE,
};
vlans[2] = (NMPlatformBridgeVlan){
.vid_start = 19,
.vid_end = 30,
.untagged = TRUE,
};
expect[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 30,
.untagged = TRUE,
};
vlans_len = 3;
nmp_utils_bridge_vlan_normalize(vlans, &vlans_len);
g_assert(vlans_len == 1);
g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len));
vlans[0] = (NMPlatformBridgeVlan){
.vid_start = 20,
.vid_end = 20,
};
vlans[1] = (NMPlatformBridgeVlan){
.vid_start = 4,
.vid_end = 4,
.pvid = TRUE,
};
vlans[2] = (NMPlatformBridgeVlan){
.vid_start = 33,
.vid_end = 33,
};
vlans[3] = (NMPlatformBridgeVlan){
.vid_start = 100,
.vid_end = 100,
.untagged = TRUE,
};
vlans[4] = (NMPlatformBridgeVlan){
.vid_start = 34,
.vid_end = 40,
};
vlans[5] = (NMPlatformBridgeVlan){
.vid_start = 21,
.vid_end = 32,
};
expect[0] = (NMPlatformBridgeVlan){
.vid_start = 4,
.vid_end = 4,
.pvid = TRUE,
};
expect[1] = (NMPlatformBridgeVlan){
.vid_start = 20,
.vid_end = 40,
};
expect[2] = (NMPlatformBridgeVlan){
.vid_start = 100,
.vid_end = 100,
.untagged = TRUE,
};
vlans_len = 6;
nmp_utils_bridge_vlan_normalize(vlans, &vlans_len);
g_assert(vlans_len == 3);
g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len));
}
static void
test_nmp_utils_bridge_normalized_vlans_equal(void)
{
NMPlatformBridgeVlan a[10];
NMPlatformBridgeVlan b[10];
/* Both empty */
g_assert(nmp_utils_bridge_normalized_vlans_equal(NULL, 0, NULL, 0));
g_assert(nmp_utils_bridge_normalized_vlans_equal(a, 0, b, 0));
g_assert(nmp_utils_bridge_normalized_vlans_equal(a, 0, NULL, 0));
g_assert(nmp_utils_bridge_normalized_vlans_equal(NULL, 0, b, 0));
/* One empty, other not */
a[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 1, NULL, 0));
g_assert(!nmp_utils_bridge_normalized_vlans_equal(NULL, 0, a, 1));
/* Equal range + VLAN */
a[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
a[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 11,
.pvid = TRUE,
};
b[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 10,
.untagged = TRUE,
};
b[1] = (NMPlatformBridgeVlan){
.vid_start = 11,
.vid_end = 11,
.pvid = TRUE,
};
g_assert(nmp_utils_bridge_normalized_vlans_equal(a, 2, b, 2));
g_assert(nmp_utils_bridge_normalized_vlans_equal(b, 2, a, 2));
/* Different flag */
b[1].pvid = FALSE;
g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 2, b, 2));
g_assert(!nmp_utils_bridge_normalized_vlans_equal(b, 2, a, 2));
/* Different ranges */
a[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 30,
.untagged = TRUE,
};
b[0] = (NMPlatformBridgeVlan){
.vid_start = 1,
.vid_end = 29,
.untagged = TRUE,
};
g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 1, b, 1));
g_assert(!nmp_utils_bridge_normalized_vlans_equal(b, 1, a, 1));
b[0].vid_start = 2;
b[0].vid_end = 30;
g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 1, b, 1));
g_assert(!nmp_utils_bridge_normalized_vlans_equal(b, 1, a, 1));
}
/*****************************************************************************/
static void
test_nmpclass_consistency(void)
{
@@ -252,6 +485,10 @@ main(int argc, char **argv)
g_test_add_func("/nm-platform/test_nmp_link_mode_all_advertised_modes_bits",
test_nmp_link_mode_all_advertised_modes_bits);
g_test_add_func("/nm-platform/test_nmpclass_consistency", test_nmpclass_consistency);
g_test_add_func("/nm-platform/test_nmp_utils_bridge_vlans_normalize",
test_nmp_utils_bridge_vlans_normalize);
g_test_add_func("/nm-platform/nmp-utils-bridge-vlans-equal",
test_nmp_utils_bridge_normalized_vlans_equal);
return g_test_run();
}