Skip to content

Commit e89bb70

Browse files
committed
devlib/module: Add irq module for stats and affinity manipulation
FEATURE Add module to collect irq configuration, stats, and affinities from target. Enables setting of affinity.
1 parent c60737c commit e89bb70

File tree

1 file changed

+243
-0
lines changed

1 file changed

+243
-0
lines changed

devlib/module/irq.py

+243
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# Copyright 2024 ARM Limited
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import logging
16+
import devlib.utils.asyn as asyn
17+
from devlib.module import Module
18+
from devlib.utils.misc import ranges_to_list
19+
20+
class Irq(object):
21+
def __init__(self, target, intid, data_dict, sysfs_root, procfs_root):
22+
self.target = target
23+
self.intid = intid
24+
self.sysfs_path = self.target.path.join(sysfs_root, str(intid))
25+
self.procfs_path = self.target.path.join(procfs_root, str(intid))
26+
27+
self.irq_info = self._fix_data_dict(data_dict.copy())
28+
29+
def _fix_data_dict(self, data_dict):
30+
clean_dict = data_dict.copy()
31+
32+
self._fix_sysfs_data(clean_dict)
33+
self._fix_procfs_data(clean_dict)
34+
35+
return clean_dict
36+
37+
def _fix_sysfs_data(self, clean_dict):
38+
clean_dict['wakeup'] = 0 if clean_dict['wakeup'] == 'disabled' else 1
39+
40+
if 'hwirq' not in clean_dict:
41+
clean_dict['hwirq'] = -1
42+
else:
43+
clean_dict['hwirq'] = int(clean_dict['hwirq'])
44+
45+
if 'per_cpu_count' in clean_dict:
46+
clean_dict['per_cpu_count'] = clean_dict['per_cpu_count'].split(',')
47+
48+
if 'name' not in clean_dict:
49+
clean_dict['name'] = ''
50+
51+
if 'actions' not in clean_dict:
52+
clean_dict['actions'] = ''
53+
else:
54+
alist = clean_dict['actions'].split(',')
55+
if alist[0] == '(null)':
56+
alist = []
57+
clean_dict['actions'] = alist
58+
59+
def _fix_procfs_data(self, clean_dict):
60+
61+
if 'spurious' not in clean_dict:
62+
clean_dict['spurious'] = ''
63+
else:
64+
temp = clean_dict['spurious'].split('\n')
65+
clean_dict['spurious'] = dict([[i.split(' ')[0], i.split(' ')[1]] for i in temp])
66+
67+
for alist in ['smp_affinity_list', 'effective_affinity_list']:
68+
if alist in clean_dict:
69+
if clean_dict[alist] == '':
70+
clean_dict[alist] = []
71+
continue
72+
clean_dict[alist] = ranges_to_list(clean_dict[alist])
73+
74+
@property
75+
def actions(self):
76+
return self.irq_info['actions']
77+
78+
@property
79+
def chip_name(self):
80+
return self.irq_info['chip_name']
81+
82+
@property
83+
def hwirq(self):
84+
return self.irq_info['hwirq']
85+
86+
@property
87+
def name(self):
88+
return None if self.irq_info['name'] == '' else self.irq_info['name']
89+
90+
@property
91+
def per_cpu_count(self):
92+
return self.irq_info['per_cpu_count']
93+
94+
@per_cpu_count.setter
95+
def per_cpu_count(self, value):
96+
self.irq_info['per_cpu_count'] = value
97+
98+
@property
99+
def type(self):
100+
return self.irq_info['type']
101+
102+
@property
103+
def wakeup(self):
104+
return self.irq_info['wakeup']
105+
106+
@property
107+
def smp_affinity(self):
108+
if 'smp_affinity' in self.irq_info.keys():
109+
return self.irq_info['smp_affinity']
110+
return -1
111+
112+
@smp_affinity.setter
113+
def smp_affinity(self, affinity, verify=True):
114+
aff = str(affinity)
115+
aff_path = self.target.path.join(self.procfs_path, 'smp_affinity')
116+
self.target.write_value(aff_path, aff, verify=verify)
117+
118+
self.update_affinities()
119+
120+
@property
121+
def effective_affinity(self):
122+
if 'effective_affinity' in self.irq_info.keys():
123+
return self.irq_info['effective_affinity']
124+
return -1
125+
126+
def to_dict(self):
127+
return self.irq_info.copy()
128+
129+
@asyn.asyncf
130+
async def update_counts(self):
131+
"""
132+
Updates irq's per-CPU counts.
133+
"""
134+
counts = await self.target.read_value.asyn(self.target.path.join(self.sysfs_path, 'per_cpu_count'))
135+
self.per_cpu_count = counts.split(',')
136+
137+
def update_counts_bulk(self, data_dict):
138+
"""
139+
Update per-CPU counts based on provided data.
140+
141+
:params data_dict: Dictionary with raw procfs data for the interrupt.
142+
:type data_dict: dict
143+
"""
144+
self.irq_info['per_cpu_count'] = self._fix_data_dict(data_dict)['per_cpu_count']
145+
146+
@asyn.asyncf
147+
async def update_affinities(self):
148+
"""Read affinity masks from target."""
149+
proc_data = await self.target.read_tree_values.asyn(self.procfs_path, depth=2, check_exit_code=False)
150+
self._fix_procfs_data(proc_data)
151+
152+
for aff in ['smp_affinity', 'effective_affinity', 'smp_affinity_list', 'effective_affinity_list']:
153+
self.irq_info[aff] = proc_data[aff]
154+
155+
class IrqModule(Module):
156+
name = 'irq'
157+
irq_sysfs_root = '/sys/kernel/irq/'
158+
irq_procfs_root = '/proc/irq/'
159+
160+
@staticmethod
161+
def probe(target):
162+
if target.file_exists(IrqModule.irq_sysfs_root):
163+
if target.file_exists(IrqModule.irq_procfs_root):
164+
return True
165+
166+
def __init__(self, target):
167+
self.logger = logging.getLogger(self.name)
168+
self.logger.debug(f'Initialized {self.name} module')
169+
170+
self.target = target
171+
self.irqs = {}
172+
173+
temp_dict = self._scrape_data(self.target, self.irq_sysfs_root, self.irq_procfs_root)
174+
for irq, data in temp_dict.items():
175+
intid = int(irq)
176+
self.irqs[intid] = Irq(self.target, intid, data, self.irq_sysfs_root, self.irq_procfs_root)
177+
178+
@asyn.asyncf
179+
@staticmethod
180+
async def _scrape_data(cls, target, sysfs_path=None, procfs_path=None):
181+
if sysfs_path and procfs_path:
182+
sysfs_dict = await target.read_tree_values.asyn(sysfs_path, depth=2, check_exit_code=False)
183+
procfs_dict = await target.read_tree_values.asyn(procfs_path, depth=2, check_exit_code=False)
184+
185+
for irq, data in sysfs_dict.items():
186+
if irq in procfs_dict.keys():
187+
sysfs_dict[irq] = {**data, **procfs_dict[irq]}
188+
return sysfs_dict
189+
190+
if sysfs_path:
191+
sysfs_dict = await target.read_tree_values.asyn(sysfs_path, depth=2, check_exit_code=False)
192+
return sysfs_dict
193+
if procfs_path:
194+
procfs_dict = await target.read_tree_values.asyn(procfs_path, depth=1, check_exit_code=False)
195+
return procfs_dict
196+
197+
return None
198+
199+
200+
def get_all_irqs(self):
201+
"""Returns list of all interrupt IDs (list of integers)."""
202+
return list(self.irqs.keys())
203+
204+
def get_all_wakeup_irqs(self):
205+
"""Returns list of all wakeup-enabled interrupt IDs (list of integers)."""
206+
return [irq.intid for intid, irq in self.irqs.items() if irq.wakeup == 1]
207+
208+
def get_per_cpu_counts(self, irq_list=None):
209+
"""
210+
Returns dictionary of per-CPU interrupt counts.
211+
212+
:params irq_list: List of interrupt IDs to include in dict.
213+
:type irq_list: list(int)
214+
215+
:returns: A dict of list(int) indexed by interrupt ID.
216+
"""
217+
if not irq_list:
218+
irq_list = self.get_all_irqs()
219+
return {intid:self.irqs[intid].per_cpu_count for intid in irq_list}
220+
221+
@asyn.asyncf
222+
async def update_counts(self, irq_list=None):
223+
"""
224+
Updates per-CPU counts from target.
225+
226+
:params irq_list: List of interrupt IDs to update.
227+
:type irq_list: list(int)
228+
"""
229+
if not irq_list:
230+
irq_list = self.get_all_irqs()
231+
232+
if len(irq_list) < 3:
233+
async def update_irq_stats(irq):
234+
return await self.irqs[irq].update_counts.asyn()
235+
236+
await self.target.async_manager.map_concurrently(update_irq_stats, irq_list)
237+
return
238+
239+
# read_tree_values() on the target is much faster
240+
data_dict = self._scrape_data(self.target, self.irq_sysfs_root)
241+
for irq, data in data_dict.items():
242+
intid = int(irq)
243+
self.irqs[intid].update_counts_bulk(data)

0 commit comments

Comments
 (0)