Skip to content

Commit

Permalink
ethtool: merge pull request #1126 from pspacek/ethtool-S-dynamic
Browse files Browse the repository at this point in the history
ethtool: statistics support

Bug-Url: #1126
  • Loading branch information
svinota authored Oct 27, 2023
2 parents f61f392 + 446f378 commit d25d27d
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 46 deletions.
4 changes: 4 additions & 0 deletions examples/ethtool/ethtool-ioctl_get_infos.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@
print("\n=== Device coalesce: ===")
for name, value in dev.get_coalesce().items():
print("\t{}: {}".format(name, value))

print("\n=== Device statistics: ===")
for name, value in dev.get_statistics():
print("\t{}: {}".format(name, value))
110 changes: 64 additions & 46 deletions pyroute2/ethtool/ioctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ETHTOOL_GLINKSETTINGS = 0x0000004C

ETHTOOL_GSTRINGS = 0x0000001B
ETHTOOL_GSTATS = 0x0000001D
ETH_GSTRING_LEN = 32

ETHTOOL_GRXCSUM = 0x00000014
Expand All @@ -39,6 +40,7 @@

SOPASS_MAX = 6

ETH_SS_STATS = 1
ETH_SS_FEATURES = 4

ETH_FLAG_RXCSUM = 1 << 0
Expand Down Expand Up @@ -299,6 +301,17 @@ class EthtoolSetFeaturesBlock(ctypes.Structure):
_fields_ = [("changed", ctypes.c_uint32), ("active", ctypes.c_uint32)]


def generate_EthtoolGStats(stats_length):
class EthtoolGStats(ctypes.Structure):
_fields_ = [
("cmd", ctypes.c_uint32),
("size", ctypes.c_uint32),
("data", ctypes.c_uint64 * stats_length),
]

return EthtoolGStats


def div_round_up(n, d):
return int(((n) + (d) - 1) / (d))

Expand Down Expand Up @@ -327,34 +340,26 @@ class FeatureState(ctypes.Structure):
_fields_ = [("off_flags", ctypes.c_uint32), ("features", EthtoolGfeatures)]


def generate_IfReqData(gstrings_length):
EthtoolGstrings = generate_EthtoolGstrings(gstrings_length)

class IfReqData(ctypes.Union):
_fields_ = [
("ifr_data", ctypes.POINTER(EthtoolCmd)),
("coalesce", ctypes.POINTER(EthtoolCoalesce)),
("value", ctypes.POINTER(EthtoolValue)),
("sset_info", ctypes.POINTER(EthtoolSsetInfo)),
("gstrings", ctypes.POINTER(EthtoolGstrings)),
("gfeatures", ctypes.POINTER(EthtoolGfeatures)),
("sfeatures", ctypes.POINTER(EthtoolSfeatures)),
("glinksettings", ctypes.POINTER(IoctlEthtoolLinkSettings)),
("wolinfo", ctypes.POINTER(EthtoolWolInfo)),
]

return IfReqData, EthtoolGstrings


def generate_IfReq(gstrings_length):
IfReqData, EthtoolGstrings = generate_IfReqData(gstrings_length)
class IfReqData(ctypes.Union):
dummy = generate_EthtoolGstrings(0)
_fields_ = [
("ifr_data", ctypes.POINTER(EthtoolCmd)),
("coalesce", ctypes.POINTER(EthtoolCoalesce)),
("value", ctypes.POINTER(EthtoolValue)),
("sset_info", ctypes.POINTER(EthtoolSsetInfo)),
("gstrings", ctypes.POINTER(None)),
("gstats", ctypes.POINTER(None)),
("gfeatures", ctypes.POINTER(EthtoolGfeatures)),
("sfeatures", ctypes.POINTER(EthtoolSfeatures)),
("glinksettings", ctypes.POINTER(IoctlEthtoolLinkSettings)),
("wolinfo", ctypes.POINTER(EthtoolWolInfo)),
]

class IfReq(ctypes.Structure):
_pack_ = 1
_anonymous_ = ("u",)
_fields_ = [("ifr_name", ctypes.c_uint8 * IFNAMSIZ), ("u", IfReqData)]

return IfReq, EthtoolGstrings
class IfReq(ctypes.Structure):
_pack_ = 1
_anonymous_ = ("u",)
_fields_ = [("ifr_name", ctypes.c_uint8 * IFNAMSIZ), ("u", IfReqData)]


class IfReqSsetInfo(ctypes.Structure):
Expand Down Expand Up @@ -448,6 +453,7 @@ def __init__(self, ifname=None):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.ifname = None
self.ifreq = None
self.stat_names = None

if ifname is not None:
self.change_ifname(ifname)
Expand All @@ -458,22 +464,9 @@ def close(self):
def change_ifname(self, ifname):
self.ifname = bytearray(ifname, 'utf-8')
self.ifname.extend(b"\0" * (IFNAMSIZ - len(self.ifname)))
sset_info = EthtoolSsetInfo(
cmd=ETHTOOL_GSSET_INFO, reserved=0, sset_mask=1 << ETH_SS_FEATURES
)
ifreq_sset = IfReqSsetInfo()
ifreq_sset.ifr_name = (ctypes.c_uint8 * IFNAMSIZ)(*self.ifname)
ifreq_sset.info = ctypes.pointer(sset_info)
fcntl.ioctl(self.sock, SIOCETHTOOL, ifreq_sset)
if sset_info.sset_mask:
self.gstrings_length = sset_info.data
else:
self.gstrings_length = 256
IfReq, self.EthtoolGstringsType = generate_IfReq(
gstrings_length=self.gstrings_length
)
self.ifreq = IfReq()
self.ifreq.ifr_name = (ctypes.c_uint8 * IFNAMSIZ)(*self.ifname)
self.stat_names = None

def ioctl(self):
try:
Expand All @@ -486,17 +479,42 @@ def ioctl(self):
raise NoSuchDevice(self.ifname.decode("utf-8"))
raise

def get_statistics(self):
"""Statistics in raw format, without names"""
if not self.stat_names:
self.stat_names = self.get_stringset(set_id=ETH_SS_STATS)
gstats = generate_EthtoolGStats(len(self.stat_names))(cmd=ETHTOOL_GSTATS)
self.ifreq.gstats = ctypes.cast(ctypes.pointer(gstats), ctypes.POINTER(None))
self.ioctl()
assert len(self.stat_names) == len(gstats.data)
return list(zip(self.stat_names, gstats.data))

def get_stringset_length(self, set_id):
sset_info = EthtoolSsetInfo(
cmd=ETHTOOL_GSSET_INFO, reserved=0, sset_mask=1 << set_id
)
ifreq_sset = IfReqSsetInfo()
ifreq_sset.ifr_name = (ctypes.c_uint8 * IFNAMSIZ)(*self.ifname)
ifreq_sset.info = ctypes.pointer(sset_info)
fcntl.ioctl(self.sock, SIOCETHTOOL, ifreq_sset)
assert sset_info.sset_mask
return sset_info.data

def get_stringset(
self, set_id=ETH_SS_FEATURES, drvinfo_offset=0, null_terminate=1
):
strings_found = []
gstrings = self.EthtoolGstringsType(
cmd=ETHTOOL_GSTRINGS, string_set=set_id, len=self.gstrings_length
# different sets have potentially different lengthts,
# obtain size dynamically
gstrings_length = self.get_stringset_length(set_id)
EthtoolGstringsType = generate_EthtoolGstrings(gstrings_length)
gstrings = EthtoolGstringsType(
cmd=ETHTOOL_GSTRINGS, string_set=set_id, len=gstrings_length
)
self.ifreq.gstrings = ctypes.pointer(gstrings)
self.ifreq.gstrings = ctypes.cast(ctypes.pointer(gstrings), ctypes.POINTER(None))
self.ioctl()

for i in range(self.gstrings_length):
strings_found = []
for i in range(gstrings_length):
buf = ''
for j in range(ETH_GSTRING_LEN):
code = gstrings.strings[i][j]
Expand All @@ -507,7 +525,7 @@ def get_stringset(
return strings_found

def get_features(self):
stringsset = self.get_stringset()
stringsset = self.get_stringset(set_id=ETH_SS_FEATURES)
cmd = EthtoolGfeatures()
cmd.cmd = ETHTOOL_GFEATURES
cmd.size = feature_bits_to_blocks(len(stringsset))
Expand Down

0 comments on commit d25d27d

Please sign in to comment.