/carbon/trunk/support
view numa/src/numa_info.py @ 355:a66685b5d147
Add README for NUMA tool.
author | Santosh Jodh <santosh.jodh@citrix.com> |
---|---|
date | Thu, 02 May 2013 19:35:00 -0700 |
parents | |
children |
line source
1 #!/usr/bin/python
3 import sys
4 import os
5 import re
7 HEX='0-9a-fA-F'
9 PCI_PATH_PATTERN=r"^[%s]{2}:[%s]{2}.[0-9]{1}" % (HEX,HEX)
10 PCI_BUS_PATTERN=r"^[%s]{2}:" % (HEX)
12 PCI_PATH_REGEX=re.compile(PCI_PATH_PATTERN)
13 PCI_BUS_REGEX=re.compile(PCI_BUS_PATTERN)
15 class DEVICE_NODE:
16 """A base class for a device node in a tree"""
17 def __init__(self, name):
18 self.name=name
19 self.parent=None # Parent device
20 self.children=[] # List of child devices
21 def add_child(self, child):
22 self.children.append(child)
23 child.parent=self
24 def dump(self, indent):
25 self.dump_one(indent)
26 for child in self.children:
27 child.dump(indent+2)
29 class PCI_DEVICE(DEVICE_NODE):
30 """Class to represent a PCI device (including PCIe ports) in the system"""
31 def __init__(self, name):
32 DEVICE_NODE.__init__(self, name) # domain:bus:device.function
33 self.secondary=None # seconday bus if PCIe port
34 self.firmware_node_path=None # ACPI path if any
35 def set_secondary(self, secondary):
36 self.secondary=secondary
37 def set_firmware_node_path(self, firmware_node_path):
38 self.firmware_node_path=firmware_node_path
39 def get_bus(self):
40 return self.name[0:2]
41 def get_firmware_node_path(self):
42 if not self.firmware_node_path is None:
43 return self.firmware_node_path
44 if not self.parent is None:
45 return self.parent.get_firmware_node_path()
46 return None
47 def is_proximity(self, acpi_tree, proximity):
48 fnp=self.get_firmware_node_path()
49 if not fnp is None:
50 if proximity == get_acpi_proximity_by_path(acpi_tree, fnp):
51 return 1
52 return 0
53 def get_devices_with_proximity(self, acpi_tree, proximity, devices):
54 if self.is_proximity(acpi_tree, proximity):
55 devices.append(self)
56 for device in self.children:
57 device.get_devices_with_proximity(acpi_tree, proximity, devices)
58 def dump_with(self, tag):
59 if not self.secondary is None:
60 print "%s: %s" % (tag, self.secondary)
61 def dump_one(self, indent):
62 s=self.name
63 if not self.secondary is None:
64 s+="(bus="+str(self.secondary)+")"
65 print " " * indent + s
67 class ACPI_SCOPE:
68 def __init__(self, name):
69 self.name=name
70 self.level=0
71 def enter(self):
72 self.level=self.level+1
73 return self.level
74 def leave(self):
75 self.level=self.level-1
76 return self.level
78 class ACPI_DEVICE(DEVICE_NODE):
79 """ACPI device/scope/processor in DSDT ASL"""
80 def __init__(self, name):
81 n=name
82 for i in range(len(name),4):
83 n+="_"
84 DEVICE_NODE.__init__(self, n)
85 self.proximity=None
86 def is_matching(self, name):
87 if name.rstrip('_').lstrip('_') == self.name.rstrip('_').lstrip('_'):
88 return 1
89 return 0
90 def set_proximity(self, proximity):
91 self.proximity=proximity
92 def get_proximity(self):
93 if not self.proximity is None:
94 return self.proximity
95 if not self.parent is None:
96 return self.parent.get_proximity()
97 return None
98 def get_child(self, name):
99 for child in self.children:
100 if child.is_matching(name):
101 return child
102 child=ACPI_DEVICE(name)
103 self.add_child(child)
104 return child
105 def dump_one(self, indent):
106 s=self.name
107 if not self.proximity is None:
108 s+="(_PXM=" + str(self.proximity) + ")"
109 print " " * indent+s
111 class PROCESSOR_AFFINITY:
112 def __init__(self):
113 self.apics=[]
114 def add_apic(self, apic):
115 self.apics.append(apic)
116 def dump(self):
117 print " APIC: %s" % ', '.join(map(str, self.apics))
119 class MEMORY_AFFINITY:
120 def __init__(self):
121 self.ranges=[]
122 def add_range(self, range):
123 self.ranges.append(range)
124 def dump(self):
125 print " Memory: %s" % ', '.join('0x%X-0x%X' % x for x in self.ranges)
127 class NUMA_NODE:
128 def __init__(self, id):
129 self.id = id
130 self.affinities=[]
131 def add_affinity(self, affinity):
132 self.affinities.append(affinity)
133 def dump(self):
134 print "NUMA Node: %d" % (self.id)
135 for a in self.affinities:
136 a.dump()
138 def get_pcie_port_firmware_node_path(pcie_port_sysfs):
139 fn=os.path.join(pcie_port_sysfs, "firmware_node")
140 fp=os.path.join(fn, "path")
141 try:
142 f=open(fp, "r")
143 firmware_node_path=f.read().strip()
144 f.close()
145 except IOError:
146 firmware_node_path=None
147 return firmware_node_path
149 def get_pci_tree(lspciout):
150 pci_devices={}
151 pci_buses={}
152 pcie_port_sysfs="/sys/bus/pci/drivers/pcieport"
153 lspci=open(lspciout, "r")
154 bus_summary=0
155 for line in lspci:
156 if bus_summary:
157 match=PCI_BUS_REGEX.search(line)
158 if match:
159 desc=PCI_BUS_REGEX.split(line)[1]
160 bus=match.group(0)[0:2]
161 if "Entered via" in desc:
162 device=desc.split()[2]
163 pci_devices[device].set_secondary(bus)
164 pci_buses[bus]=pci_devices[device]
165 port_path=os.path.join(pcie_port_sysfs, "0000:"+device)
166 if os.path.exists(port_path):
167 fnp=get_pcie_port_firmware_node_path(port_path)
168 pci_devices[device].set_firmware_node_path(fnp)
169 else:
170 match=PCI_PATH_REGEX.search(line)
171 if match:
172 name=match.group(0)
173 pci_devices[name]=PCI_DEVICE(name)
174 continue
175 if "Summary of buses:" in line:
176 bus_summary=1
177 continue
178 root=PCI_DEVICE("ROOT")
179 for name in sorted(pci_devices.iterkeys()):
180 device=pci_devices[name]
181 bus=device.get_bus()
182 if bus in pci_buses:
183 pci_buses[bus].add_child(device)
184 else:
185 root.add_child(device)
186 return root
188 def get_numa_nodes(asl):
189 srat=open(asl, "r")
190 numa_nodes={}
191 build_node=None
192 type=None
193 for line in srat:
194 #
195 # Determine affinity type
196 #
197 if "Subtable Type" in line:
198 if "Processor" in line:
199 affinity=PROCESSOR_AFFINITY()
200 type=0
201 if "Memory" in line:
202 affinity=MEMORY_AFFINITY()
203 type=1
204 if type is None:
205 continue
206 #
207 # Only include enabled entries
208 #
209 if "Enabled" in line:
210 enabled=int(line.split(":")[1].strip(),16)
211 if enabled == 1 and type == 1:
212 build_node=1
213 #
214 # Processor Affinity
215 #
216 if type == 0:
217 if "Proximity Domain Low" in line:
218 domain_low_8=int(line.split(":")[1].strip(),16)
219 if "Apic ID" in line:
220 apic_id=int(line.split(":")[1].strip(),16)
221 affinity.add_apic(apic_id)
222 if "Proximity Domain High" in line:
223 domain_high_24=int(line.split(":")[1].strip(),16)
224 domain=domain_high_24*256+domain_low_8
225 if enabled == 1:
226 build_node=1
227 #
228 # Memory Affinity
229 #
230 if type == 1:
231 if "Proximity Domain" in line:
232 domain=int(line.split(":")[1].strip(),16)
233 if "Base Address" in line:
234 address=int(line.split(":")[1].strip(),16)
235 if "Address Length" in line:
236 length=int(line.split(":")[1].strip(),16)
237 affinity.add_range((address, address+length))
239 if not build_node is None:
240 if not domain in numa_nodes:
241 numa_nodes[domain]=NUMA_NODE(domain)
242 numa_nodes[domain].add_affinity(affinity)
243 affinity=None
244 build_node=None
245 return numa_nodes
247 def get_acpi_proximity_by_path(acpi_root, path):
248 device=get_acpi_device(acpi_root, path)
249 proximity=device.get_proximity()
250 return proximity
252 def get_acpi_device(acpi_root, path):
253 device=acpi_root
254 path=path[1:]
255 if path != "":
256 parts=path.split(".")
257 for i in range(0,len(parts)):
258 device=device.get_child(parts[i])
259 return device
261 def get_acpi_path(scope, stack):
262 if scope.name.startswith('\\'):
263 path=scope.name
264 else:
265 path=scope.name
266 for s in reversed(stack):
267 if s.name == "\\":
268 path=s.name+path
269 else:
270 path=s.name+"."+path
271 if path.startswith('\\'):
272 break
273 return path
275 def get_acpi_tree(asl):
276 dsdt=open(asl, "r")
277 root=ACPI_DEVICE("\\")
278 scope_stack=[]
279 current=ACPI_SCOPE("\\")
280 for line in dsdt:
281 new_scope=0
282 match=re.search(r"Scope \(|Device \(|Processor \(", line)
283 if not match is None:
284 new_scope=1
285 if new_scope:
286 name=line.split('(')[1].split(')')[0]
287 if "," in name:
288 name=name.split(',')[0]
289 scope=ACPI_SCOPE(name)
290 scope_stack.append(current)
291 current=scope
292 path=get_acpi_path(current, scope_stack)
293 device=get_acpi_device(root, path)
294 if "{" in line:
295 current.enter()
296 if "}" in line:
297 if current.leave() == 0:
298 if len(scope_stack):
299 current=scope_stack.pop()
300 if "_PXM" in line:
301 proximity=line.split(',')[1].split(')')[0].strip()
302 if proximity == "Zero":
303 proximity="0"
304 if proximity == "One":
305 proximity="1"
306 p=int(proximity, 16)
307 path=get_acpi_path(current, scope_stack)
308 device=get_acpi_device(root, path)
309 device.set_proximity(p)
310 return root
312 def main(argv):
313 acpi_tree=get_acpi_tree(argv[2])
314 print "\nACPI device tree"
315 acpi_tree.dump(0)
317 pci_tree=get_pci_tree(argv[3])
318 print "\nPCI device tree"
319 pci_tree.dump(0)
321 numa_nodes=get_numa_nodes(argv[1])
322 print "\nSystem NUMA nodes"
323 for id, node in numa_nodes.iteritems():
324 node.dump()
325 devices=[]
326 pci_tree.get_devices_with_proximity(acpi_tree, node.id, devices)
327 for device in devices:
328 device.dump_with(" PCI BUS")
330 if __name__ == "__main__":
331 main(sys.argv)