diff --git a/pyroute2/ethtool/ethtool.py b/pyroute2/ethtool/ethtool.py index 22955af57..a9d707c20 100644 --- a/pyroute2/ethtool/ethtool.py +++ b/pyroute2/ethtool/ethtool.py @@ -71,6 +71,31 @@ def to_ioctl(ioctl_features, eth_features): ioctl_features[feature.name] = feature.enable +class EthtoolChannels( + namedtuple( + 'EthtoolChannels', + ( + "max_rx", + "max_tx", + "max_other", + "max_combined", + "rx_count", + "tx_count", + "other_count", + "combined_count", + ), + ) +): + @classmethod + def from_ioctl(cls, channels): + return cls(**{k: getattr(channels, k) for k in cls._fields}) + + @staticmethod + def to_ioctl(ioctl_channels, eth_channel): + for key, val in eth_channel.items(): + setattr(ioctl_channels, key, val) + + class EthtoolWakeOnLan(namedtuple('EthtoolWolMode', ('modes', 'sopass'))): @classmethod def from_netlink(cls, nl_wol): @@ -465,6 +490,16 @@ def set_features(self, ifname, features): EthtoolFeatures.to_ioctl(ioctl_features, features) self._with_ioctl.set_features(ioctl_features) + def get_channels(self, ifname): + self._with_ioctl.change_ifname(ifname) + return EthtoolChannels.from_ioctl(self._with_ioctl.get_channels()) + + def set_channels(self, ifname, channels): + self._with_ioctl.change_ifname(ifname) + ioctl_channels = self._with_ioctl.get_channels() + EthtoolChannels.to_ioctl(ioctl_channels, channels) + self._with_ioctl.set_channels(ioctl_channels) + def get_coalesce(self, ifname): self._with_ioctl.change_ifname(ifname) return EthtoolCoalesce.from_ioctl(self._with_ioctl.get_coalesce()) diff --git a/pyroute2/ethtool/ioctl.py b/pyroute2/ethtool/ioctl.py index 72be86f7d..c7363490d 100644 --- a/pyroute2/ethtool/ioctl.py +++ b/pyroute2/ethtool/ioctl.py @@ -17,6 +17,8 @@ ETHTOOL_GFLAGS = 0x00000025 ETHTOOL_GFEATURES = 0x0000003A ETHTOOL_SFEATURES = 0x0000003B +ETHTOOL_GCHANNELS = 0x0000003C +ETHTOOL_SCHANNELS = 0x0000003D ETHTOOL_GLINKSETTINGS = 0x0000004C ETHTOOL_GSTRINGS = 0x0000001B @@ -339,6 +341,21 @@ class EthtoolSfeatures(ctypes.Structure): ] +class EthtoolChannels(DictStruct): + _pack_ = 1 + _fields_ = [ + ("cmd", ctypes.c_uint32), + ("max_rx", ctypes.c_uint32), + ("max_tx", ctypes.c_uint32), + ("max_other", ctypes.c_uint32), + ("max_combined", ctypes.c_uint32), + ("rx_count", ctypes.c_uint32), + ("tx_count", ctypes.c_uint32), + ("other_count", ctypes.c_uint32), + ("combined_count", ctypes.c_uint32), + ] + + class FeatureState(ctypes.Structure): _fields_ = [("off_flags", ctypes.c_uint32), ("features", EthtoolGfeatures)] @@ -369,6 +386,7 @@ class IfReqData(ctypes.Union): ("gstats", ctypes.POINTER(None)), ("gfeatures", ctypes.POINTER(EthtoolGfeatures)), ("sfeatures", ctypes.POINTER(EthtoolSfeatures)), + ("channels", ctypes.POINTER(EthtoolChannels)), ("glinksettings", ctypes.POINTER(IoctlEthtoolLinkSettings)), ("wolinfo", ctypes.POINTER(EthtoolWolInfo)), ("rings", ctypes.POINTER(EthtoolRingParam)), @@ -562,6 +580,17 @@ def set_features(self, features): self.ifreq.sfeatures = ctypes.pointer(features._cmd_set) return self.ioctl() + def get_channels(self): + cmd = EthtoolChannels(cmd=ETHTOOL_GCHANNELS) + self.ifreq.channels = ctypes.pointer(cmd) + self.ioctl() + return cmd + + def set_channels(self, channels): + channels.cmd = ETHTOOL_SCHANNELS + self.ifreq.channels = ctypes.pointer(channels) + return self.ioctl() + def get_cmd(self): cmd = EthtoolCmd(cmd=ETHTOOL_GSET) self.ifreq.ifr_data = ctypes.pointer(cmd)