From 843c5b87d0f70cbfb6ec2db806d287451d3dd995 Mon Sep 17 00:00:00 2001
From: Nirmoy Das <ndas@suse.de>
Date: Tue, 1 Aug 2017 18:56:46 +0200
Subject: ethtool: handle channels parameters (bsc#1043883)


diff --git a/client/compat.c b/client/compat.c
index 4a8a18e8..2becdb4e 100644
--- a/client/compat.c
+++ b/client/compat.c
@@ -364,6 +364,35 @@ ni_compat_generate_eth_eee_node(xml_node_t *parent, const ni_ethtool_eee_t *eee)
 		xml_node_free(node);
 }
 
+/* generate channels information */
+static void
+ni_compat_generate_eth_channels_node(xml_node_t *parent, const ni_ethtool_channels_t *channels)
+{
+	xml_node_t *node;
+
+	if (!parent || !channels)
+		return;
+
+	node = xml_node_new("channels", NULL);
+	if (channels->tx != NI_ETHTOOL_CHANNELS_DEFAULT) {
+		xml_node_new_element_uint("tx", node, channels->tx);
+	}
+	if (channels->rx != NI_ETHTOOL_CHANNELS_DEFAULT) {
+		xml_node_new_element_uint("rx", node, channels->rx);
+	}
+	if (channels->other != NI_ETHTOOL_CHANNELS_DEFAULT) {
+		xml_node_new_element_uint("other", node, channels->other);
+	}
+	if (channels->combined != NI_ETHTOOL_CHANNELS_DEFAULT) {
+		xml_node_new_element_uint("combined", node, channels->combined);
+	}
+
+	if (node->children)
+		xml_node_add_child(parent, node);
+	else
+		xml_node_free(node);
+
+}
 /* generate ring information */
 static void
 ni_compat_generate_eth_ring_node(xml_node_t *parent, const ni_ethtool_ring_t *ring)
@@ -439,6 +468,7 @@ __ni_compat_generate_eth_node(xml_node_t *child, const ni_ethernet_t *eth)
 	ni_compat_generate_eth_eee_node(child, &eth->eee);
 	ni_compat_generate_eth_ring_node(child, &eth->ring);
 	ni_compat_generate_eth_coalesce_node(child, &eth->coalesce);
+	ni_compat_generate_eth_channels_node(child, &eth->channels);
 }
 
 static ni_bool_t
diff --git a/client/suse/compat-suse.c b/client/suse/compat-suse.c
index 8c505834..98745d3a 100644
--- a/client/suse/compat-suse.c
+++ b/client/suse/compat-suse.c
@@ -1985,6 +1985,29 @@ try_add_ethtool_eee(ni_netdev_t *dev, const char *opt, const char *val)
 	}
 }
 
+/* get channels params from wicked config */
+static void
+try_add_ethtool_channels(ni_netdev_t *dev, const char *opt, const char *val)
+{
+
+	ni_ethernet_t *eth = ni_netdev_get_ethernet(dev);
+
+	if (ni_string_eq(opt, "tx")) {
+		ni_parse_uint(val, &eth->channels.tx, 10);
+	} else
+	if (ni_string_eq(opt, "rx")) {
+		ni_parse_uint(val, &eth->channels.rx, 10);
+	} else
+	if (ni_string_eq(opt, "other")) {
+		ni_parse_uint(val, &eth->channels.other, 10);
+	} else
+	if (ni_string_eq(opt, "combined")) {
+		ni_parse_uint(val, &eth->channels.combined, 10);
+	}
+
+
+}
+
 /* get ringparams from wicked config */
 static void
 try_add_ethtool_ring(ni_netdev_t *dev, const char *opt, const char *val)
@@ -2044,6 +2067,12 @@ try_add_ethtool_options(ni_netdev_t *dev, const char *type,
 						opts->data[i + 1]);
 		}
 	}
+	if (ni_string_eq(type, "-L") || ni_string_eq(type, "--set-channels")) {
+		for (i = start; (i + 1) < opts->count; i+=2) {
+			try_add_ethtool_channels(dev, opts->data[i],
+						opts->data[i + 1]);
+		}
+	}
 }
 
 static ni_bool_t
diff --git a/include/wicked/ethernet.h b/include/wicked/ethernet.h
index 9bbbaa0e..6c52fe7f 100644
--- a/include/wicked/ethernet.h
+++ b/include/wicked/ethernet.h
@@ -78,6 +78,16 @@ typedef struct ni_ethtool_eee {
 	} tx_lpi;
 } ni_ethtool_eee_t;
 
+#define NI_ETHTOOL_CHANNELS_DEFAULT		-1U
+
+typedef struct ni_ethtool_channels {
+	ni_tristate_t	supported;
+	unsigned int	tx;
+	unsigned int	rx;
+	unsigned int	other;
+	unsigned int	combined;
+} ni_ethtool_channels_t;
+
 #define NI_ETHTOOL_RING_DEFAULT		-1U
 
 typedef struct ni_ethtool_ring {
@@ -133,8 +143,9 @@ struct ni_ethernet {
 	ni_ethernet_wol_t	wol;
 	ni_ethtool_offload_t	offload;
 	ni_ethtool_eee_t	eee;
-	ni_ethtool_ring_t       ring;
-	ni_ethtool_coalesce_t   coalesce;
+	ni_ethtool_ring_t	ring;
+	ni_ethtool_coalesce_t	coalesce;
+	ni_ethtool_channels_t	channels;
 
 	unsigned int		identify_time;
 };
diff --git a/schema/ethernet.xml b/schema/ethernet.xml
index c063c690..70d4e32d 100644
--- a/schema/ethernet.xml
+++ b/schema/ethernet.xml
@@ -63,6 +63,13 @@
       <tx-timer type="uint32"/>
     </eee>
 
+    <channels class="dict">
+      <tx type="uint32"/>
+      <rx type="uint32"/>
+      <other type="uint32"/>
+      <combined type="uint32"/>
+    </channels>
+
     <ring class="dict">
       <tx type="uint32"/>
       <rx type="uint32"/>
diff --git a/src/dbus-objects/ethernet.c b/src/dbus-objects/ethernet.c
index 32e44f47..262b9900 100644
--- a/src/dbus-objects/ethernet.c
+++ b/src/dbus-objects/ethernet.c
@@ -570,6 +570,69 @@ __ni_objectmodel_ethernet_set_eee(ni_dbus_object_t *object,
 }
 
 static dbus_bool_t
+__ni_objectmodel_ethernet_get_channels(const ni_dbus_object_t *object,
+		const ni_dbus_property_t *property,
+		ni_dbus_variant_t *result,
+		DBusError *error)
+{
+	const ni_ethernet_t *eth;
+
+	if (!(eth = __ni_objectmodel_ethernet_read_handle(object, error)))
+		return FALSE;
+
+	if (eth->channels.supported == NI_TRISTATE_DISABLE)
+		return FALSE;
+
+	if (eth->channels.tx != NI_ETHTOOL_CHANNELS_DEFAULT) {
+		ni_dbus_dict_add_int32(result, "tx", eth->channels.tx);
+	}
+
+	if (eth->channels.rx != NI_ETHTOOL_CHANNELS_DEFAULT) {
+		ni_dbus_dict_add_int32(result, "rx", eth->channels.rx);
+	}
+
+	if (eth->channels.other != NI_ETHTOOL_CHANNELS_DEFAULT) {
+		ni_dbus_dict_add_int32(result, "other", eth->channels.other);
+	}
+
+	if (eth->channels.combined != NI_ETHTOOL_CHANNELS_DEFAULT) {
+		ni_dbus_dict_add_int32(result, "combined", eth->channels.combined);
+	}
+
+	return TRUE;
+}
+
+static dbus_bool_t
+__ni_objectmodel_ethernet_set_channels(ni_dbus_object_t *object,
+		const ni_dbus_property_t *property,
+		const ni_dbus_variant_t *argument,
+		DBusError *error)
+{
+	ni_ethernet_t *eth;
+
+	if (!(eth = __ni_objectmodel_ethernet_write_handle(object, error)))
+		return FALSE;
+
+	if (!ni_dbus_dict_get_uint32(argument, "tx", &eth->channels.tx)) {
+		eth->channels.tx = NI_ETHTOOL_CHANNELS_DEFAULT;
+	}
+
+	if (!ni_dbus_dict_get_uint32(argument, "rx", &eth->channels.rx)) {
+		eth->channels.rx = NI_ETHTOOL_CHANNELS_DEFAULT;
+	}
+
+	if (!ni_dbus_dict_get_uint32(argument, "other", &eth->channels.other)) {
+		eth->channels.other = NI_ETHTOOL_CHANNELS_DEFAULT;
+	}
+
+	if (!ni_dbus_dict_get_uint32(argument, "combined", &eth->channels.combined)) {
+		eth->channels.combined = NI_ETHTOOL_CHANNELS_DEFAULT;
+	}
+
+	return TRUE;
+}
+
+static dbus_bool_t
 __ni_objectmodel_ethernet_get_ring(const ni_dbus_object_t *object,
 		const ni_dbus_property_t *property,
 		ni_dbus_variant_t *result,
@@ -803,6 +866,9 @@ const ni_dbus_property_t	ni_objectmodel_ethernet_property_table[] = {
 	__NI_DBUS_PROPERTY(
 			NI_DBUS_DICT_SIGNATURE,
 			coalesce, __ni_objectmodel_ethernet, RO),
+	__NI_DBUS_PROPERTY(
+			NI_DBUS_DICT_SIGNATURE,
+			channels, __ni_objectmodel_ethernet, RO),
 
 
 
diff --git a/src/ethernet.c b/src/ethernet.c
index af0d0bfb..564efd2f 100644
--- a/src/ethernet.c
+++ b/src/ethernet.c
@@ -59,6 +59,7 @@ static void	ni_ethtool_offload_init(ni_ethtool_offload_t *);
 static void	ni_ethtool_eee_init(ni_ethtool_eee_t *);
 static void	ni_ethtool_ring_init(ni_ethtool_ring_t *);
 static void	ni_ethtool_coalesce_init(ni_ethtool_coalesce_t *coalesce);
+static void	ni_ethtool_channels_init(ni_ethtool_channels_t *);
 
 /*
  * Allocate ethernet struct
@@ -77,6 +78,7 @@ ni_ethernet_new(void)
 	ni_ethtool_eee_init(&ether->eee);
 	ni_ethtool_ring_init(&ether->ring);
 	ni_ethtool_coalesce_init(&ether->coalesce);
+	ni_ethtool_channels_init(&ether->channels);
 
 	return ether;
 }
@@ -1090,6 +1092,82 @@ ni_ethtool_set_ring(const char *ifname, ni_ethtool_ring_t *ring)
 	return 0;
 }
 
+	static void
+ni_ethtool_channels_init(ni_ethtool_channels_t *channels)
+{
+	if (channels) {
+		channels->supported = NI_TRISTATE_DEFAULT;
+		channels->tx	= NI_ETHTOOL_CHANNELS_DEFAULT;
+		channels->rx	= NI_ETHTOOL_CHANNELS_DEFAULT;
+		channels->other	= NI_ETHTOOL_CHANNELS_DEFAULT;
+		channels->combined	= NI_ETHTOOL_CHANNELS_DEFAULT;
+	}
+}
+
+static int
+ni_ethtool_get_channels(const char *ifname, ni_ethtool_channels_t *channels)
+{
+	struct ethtool_channels tmp;
+
+	if (channels->supported == NI_TRISTATE_DISABLE)
+		return -1;
+
+	tmp.cmd = ETHTOOL_GCHANNELS;
+	memset(&tmp, 0, sizeof(tmp));
+	if (__ni_ethtool(ifname, ETHTOOL_GCHANNELS, &tmp) < 0) {
+		if (errno != EOPNOTSUPP && errno != ENODEV)
+			ni_warn("%s: getting ethtool.channels options failed: %m", ifname);
+		else
+			ni_debug_verbose(NI_LOG_DEBUG2, NI_TRACE_IFCONFIG,
+				"%s: getting ethtool.channels options failed: %m", ifname);
+
+		if (errno != EOPNOTSUPP)
+			channels->supported = NI_TRISTATE_DISABLE;
+		return -1;
+	}
+
+	channels->tx = tmp.tx_count;
+	channels->rx = tmp.rx_count;
+	channels->other = tmp.other_count;
+	channels->combined = tmp.combined_count;
+	return 0;
+}
+
+static int
+ni_ethtool_set_channels(const char *ifname, ni_ethtool_channels_t *channels)
+{
+	struct ethtool_channels tmp;
+
+	if (channels->supported == NI_TRISTATE_DISABLE)
+		return -1;
+
+	tmp.cmd = ETHTOOL_GCHANNELS;
+	memset(&tmp, 0, sizeof(tmp));
+	if (__ni_ethtool(ifname, ETHTOOL_GCHANNELS, &tmp) < 0) {
+		if (errno != EOPNOTSUPP && errno != ENODEV)
+			ni_warn("%s: getting ethtool.channels options failed: %m", ifname);
+		else
+			ni_debug_verbose(NI_LOG_DEBUG, NI_TRACE_IFCONFIG,
+				"%s: getting ethtool.channels options failed: %m", ifname);
+
+		if (errno != EOPNOTSUPP)
+			channels->supported = NI_TRISTATE_DISABLE;
+		return -1;
+	}
+
+	ni_ethtool_set_uint_param(ifname, &tmp, ETHTOOL_SCHANNELS, "channels",
+			"tx", tmp.max_tx, &tmp.tx_count, channels->tx);
+	ni_ethtool_set_uint_param(ifname, &tmp, ETHTOOL_SCHANNELS, "channels",
+			"rx", tmp.max_rx, &tmp.rx_count, channels->rx);
+	ni_ethtool_set_uint_param(ifname, &tmp, ETHTOOL_SCHANNELS, "channels",
+			"other", tmp.max_other,
+			&tmp.other_count, channels->other);
+	ni_ethtool_set_uint_param(ifname, &tmp, ETHTOOL_SCHANNELS, "channels",
+				"combined", tmp.max_combined,
+				&tmp.combined_count, channels->combined);
+
+	return 0;
+}
 void
 __ni_system_ethernet_get(const char *ifname, ni_ethernet_t *ether)
 {
@@ -1100,6 +1178,7 @@ __ni_system_ethernet_get(const char *ifname, ni_ethernet_t *ether)
 	ni_ethtool_get_eee(ifname, &ether->eee);
 	ni_ethtool_get_ring(ifname, &ether->ring);
 	ni_ethtool_get_coalesce(ifname, &ether->coalesce);
+	ni_ethtool_get_channels(ifname, &ether->channels);
 }
 
 /*
@@ -1294,4 +1373,5 @@ __ni_system_ethernet_set(const char *ifname, ni_ethernet_t *ether)
 	ni_ethtool_set_eee(ifname, &ether->eee);
 	ni_ethtool_set_ring(ifname, &ether->ring);
 	ni_ethtool_set_coalesce(ifname, &ether->coalesce);
+	ni_ethtool_set_channels(ifname, &ether->channels);
 }
-- 
2.13.6

