Skip to content

Commit

Permalink
devlib/module: Add irq module for stats and affinity manipulation
Browse files Browse the repository at this point in the history
FEATURE

Add module to collect irq configuration, stats, and affinities
from target. Enables setting of affinity.
  • Loading branch information
msrasmussen committed Feb 13, 2024
1 parent a83fe52 commit 019b4ac
Showing 1 changed file with 240 additions and 0 deletions.
240 changes: 240 additions & 0 deletions devlib/module/irq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
# Copyright 2024 ARM Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import devlib.utils.asyn as asyn
from devlib.module import Module
from devlib.utils.misc import ranges_to_list

class Irq(object):
def __init__(self, target, intid, data_dict, sysfs_root, procfs_root):
self.target = target
self.intid = intid
self.sysfs_path = self.target.path.join(sysfs_root, str(intid))
self.procfs_path = self.target.path.join(procfs_root, str(intid))

self.irq_info = self._fix_data_dict(data_dict.copy())

def _fix_data_dict(self, data_dict):
clean_dict = data_dict.copy()

self._fix_sysfs_data(clean_dict)
self._fix_procfs_data(clean_dict)

return clean_dict

def _fix_sysfs_data(self, clean_dict):
clean_dict['wakeup'] = 0 if clean_dict['wakeup'] == 'disabled' else 1

if 'hwirq' not in clean_dict:
clean_dict['hwirq'] = -1
else:
clean_dict['hwirq'] = int(clean_dict['hwirq'])

if 'per_cpu_count' in clean_dict:
del clean_dict['per_cpu_count']

if 'name' not in clean_dict:
clean_dict['name'] = ''

if 'actions' not in clean_dict:
clean_dict['actions'] = ''
else:
alist = clean_dict['actions'].split(',')
if alist[0] == '(null)':
alist = []
clean_dict['actions'] = alist

def _fix_procfs_data(self, clean_dict):

if 'spurious' not in clean_dict:
clean_dict['spurious'] = ''
else:
temp = clean_dict['spurious'].split('\n')
clean_dict['spurious'] = dict([[i.split(' ')[0], i.split(' ')[1]] for i in temp])

for alist in ['smp_affinity_list', 'effective_affinity_list']:
if alist in clean_dict:
if clean_dict[alist] == '':
clean_dict[alist] = []
continue
clean_dict[alist] = ranges_to_list(clean_dict[alist])

@property
def actions(self):
return self.irq_info['actions']

@property
def chip_name(self):
return self.irq_info['chip_name']

@property
def hwirq(self):
return self.irq_info['hwirq']

@property
def name(self):
return None if self.irq_info['name'] == '' else self.irq_info['name']

@property
def type(self):
return self.irq_info['type']

@property
def wakeup(self):
return self.irq_info['wakeup']

@property
def smp_affinity(self):
if 'smp_affinity' in self.irq_info.keys():
return self.irq_info['smp_affinity']
return -1

@smp_affinity.setter
def smp_affinity(self, affinity, verify=True):
aff = str(affinity)
aff_path = self.target.path.join(self.procfs_path, 'smp_affinity')
self.target.write_value(aff_path, aff, verify=verify)

self.update_affinities()

@property
def effective_affinity(self):
if 'effective_affinity' in self.irq_info.keys():
return self.irq_info['effective_affinity']
return -1

def to_dict(self):
return self.irq_info.copy()

@asyn.asyncf
async def update_affinities(self):
"""Read affinity masks from target."""
proc_data = await self.target.read_tree_values.asyn(self.procfs_path, depth=2, check_exit_code=False)
self._fix_procfs_data(proc_data)

for aff in ['smp_affinity', 'effective_affinity', 'smp_affinity_list', 'effective_affinity_list']:
self.irq_info[aff] = proc_data[aff]

class IrqModule(Module):
name = 'irq'
irq_sysfs_root = '/sys/kernel/irq/'
irq_procfs_root = '/proc/irq/'

@staticmethod
def probe(target):
if target.file_exists(IrqModule.irq_sysfs_root):
if target.file_exists(IrqModule.irq_procfs_root):
return True

def __init__(self, target):
self.logger = logging.getLogger(self.name)
self.logger.debug(f'Initialized {self.name} module')

self.target = target
self.irqs = {}

temp_dict = self._scrape_data(self.target, self.irq_sysfs_root, self.irq_procfs_root)
for irq, data in temp_dict.items():
intid = int(irq)
self.irqs[intid] = Irq(self.target, intid, data, self.irq_sysfs_root, self.irq_procfs_root)

@asyn.asyncf
@staticmethod
async def _scrape_data(cls, target, sysfs_path=None, procfs_path=None):
if sysfs_path and procfs_path:
sysfs_dict = await target.read_tree_values.asyn(sysfs_path, depth=2, check_exit_code=False)
procfs_dict = await target.read_tree_values.asyn(procfs_path, depth=2, check_exit_code=False)

for irq, data in sysfs_dict.items():
if irq in procfs_dict.keys():
sysfs_dict[irq] = {**data, **procfs_dict[irq]}
return sysfs_dict

if sysfs_path:
sysfs_dict = await target.read_tree_values.asyn(sysfs_path, depth=2, check_exit_code=False)
return sysfs_dict
if procfs_path:
procfs_dict = await target.read_tree_values.asyn(procfs_path, depth=1, check_exit_code=False)
return procfs_dict

return None


def get_all_irqs(self):
"""Returns list of all interrupt IDs (list of integers)."""
return list(self.irqs.keys())

def get_all_wakeup_irqs(self):
"""Returns list of all wakeup-enabled interrupt IDs (list of integers)."""
return [irq.intid for intid, irq in self.irqs.items() if irq.wakeup == 1]

@asyn.asyncf
async def get_raw_stats(self):
"""Return raw interrupt stats from procfs on target."""
raw_stats = await self.target.read_value.asyn('/proc/interrupts')
return raw_stats

@asyn.asyncf
async def get_stats_dict(self):
"""Returns dict of dicts of irq and IPI stats."""
raw_stats = await self.get_raw_stats.asyn()

nr_cpus = self.target.number_of_cpus

d_irq = {
'intid' : [],
'chip_name' : [],
'hwirq' : [],
'type' : [],
'actions' : [],
}

d_ipi = {
'id' : [],
'purpose' : [],
}

for cpu in range(0, nr_cpus):
d_irq[f'cpu{cpu}'] = []
d_ipi[f'cpu{cpu}'] = []

for line in self.target.irq.get_raw_stats().splitlines()[1:-1]:
intid, data = line.split(':', 1)
data = data.split()

if 'IPI' in intid:
d_ipi['id'].append(int(intid[3:]))

for cpu in range(0, nr_cpus):
d_ipi[f'cpu{cpu}'].append(int(data[cpu]))

d_ipi['purpose'].append(' '.join(data[nr_cpus:]))
else:
d_irq['intid'].append(int(intid))
d_irq['chip_name'].append(data[nr_cpus])

for cpu in range(0, nr_cpus):
d_irq[f'cpu{cpu}'].append(int(data[cpu]))

if 'Level' in data[nr_cpus+1] or 'Edge' in data[nr_cpus+1]:
d_irq['hwirq'].append(None)
d_irq['type'].append(data[nr_cpus+1])
d_irq['actions'].append(data[nr_cpus+2:])
else:
d_irq['hwirq'].append(int(data[nr_cpus+1]))
d_irq['type'].append(data[nr_cpus+2])
d_irq['actions'].append(data[nr_cpus+3:])

return {'irq' : d_irq, 'ipi' : d_ipi}

0 comments on commit 019b4ac

Please sign in to comment.