1 #! /usr/bin/env python3
2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright (c) 2020 Microsoft Corporation
4 """Script to query and setup huge pages for DPDK applications."""
13 # Standard binary prefix
16 # systemd mount point for huge pages
17 HUGE_MOUNT = "/dev/hugepages"
21 '''Format memory size in kB into conventional format'''
22 logk = int(log2(kb) / 10)
23 suffix = BINARY_PREFIX[logk]
25 return '{}{}b'.format(int(kb / unit), suffix)
29 '''Convert memory size with suffix to kB'''
30 match = re.match(r'(\d+)([' + BINARY_PREFIX + r']?)$', arg.upper())
32 sys.exit('{} is not a valid page size'.format(arg))
33 num = float(match.group(1))
34 suffix = match.group(2)
36 return int(num / 1024)
37 idx = BINARY_PREFIX.find(suffix)
38 return int(num * (2**(idx * 10)))
42 '''Test if NUMA is necessary on this system'''
43 return os.path.exists('/sys/devices/system/node')
46 def get_valid_page_sizes(path):
47 '''Extract valid hugepage sizes'''
48 dir = os.path.dirname(path)
49 pg_sizes = (d.split("-")[1] for d in os.listdir(dir))
50 return " ".join(pg_sizes)
53 def get_hugepages(path):
54 '''Read number of reserved pages'''
55 with open(path + '/nr_hugepages') as nr_hugepages:
56 return int(nr_hugepages.read())
60 def set_hugepages(path, pages):
61 '''Write the number of reserved huge pages'''
62 filename = path + '/nr_hugepages'
64 with open(filename, 'w') as nr_hugepages:
65 nr_hugepages.write('{}\n'.format(pages))
66 except PermissionError:
67 sys.exit('Permission denied: need to be root!')
68 except FileNotFoundError:
69 sys.exit("Invalid page size. Valid page sizes: {}".format(
70 get_valid_page_sizes(path)))
71 if get_hugepages(path) != pages:
72 sys.exit('Unable to reserve required pages.')
75 def show_numa_pages():
76 '''Show huge page reservations on Numa system'''
77 print('Node Pages Size Total')
78 for numa_path in glob.glob('/sys/devices/system/node/node*'):
79 node = numa_path[29:] # slice after /sys/devices/system/node/node
80 path = numa_path + '/hugepages'
81 if not os.path.exists(path):
83 for hdir in os.listdir(path):
84 pages = get_hugepages(path + '/' + hdir)
86 kb = int(hdir[10:-2]) # slice out of hugepages-NNNkB
87 print('{:<4} {:<5} {:<6} {}'.format(node, pages,
89 fmt_memsize(pages * kb)))
92 def show_non_numa_pages():
93 '''Show huge page reservations on non Numa system'''
94 print('Pages Size Total')
95 path = '/sys/kernel/mm/hugepages'
96 for hdir in os.listdir(path):
97 pages = get_hugepages(path + '/' + hdir)
100 print('{:<5} {:<6} {}'.format(pages, fmt_memsize(kb),
101 fmt_memsize(pages * kb)))
105 '''Show existing huge page settings'''
109 show_non_numa_pages()
113 '''Clear all existing huge page mappings'''
116 '/sys/devices/system/node/node*/hugepages/hugepages-*')
118 dirs = glob.glob('/sys/kernel/mm/hugepages/hugepages-*')
121 set_hugepages(path, 0)
124 def default_pagesize():
125 '''Get default huge page size from /proc/meminfo'''
126 with open('/proc/meminfo') as meminfo:
128 if line.startswith('Hugepagesize:'):
129 return int(line.split()[1])
133 def set_numa_pages(pages, hugepgsz, node=None):
134 '''Set huge page reservation on Numa system'''
136 nodes = ['/sys/devices/system/node/node{}/hugepages'.format(node)]
138 nodes = glob.glob('/sys/devices/system/node/node*/hugepages')
140 for node_path in nodes:
141 huge_path = '{}/hugepages-{}kB'.format(node_path, hugepgsz)
142 set_hugepages(huge_path, pages)
145 def set_non_numa_pages(pages, hugepgsz):
146 '''Set huge page reservation on non Numa system'''
147 path = '/sys/kernel/mm/hugepages/hugepages-{}kB'.format(hugepgsz)
148 set_hugepages(path, pages)
151 def reserve_pages(pages, hugepgsz, node=None):
152 '''Set the number of huge pages to be reserved'''
153 if node or is_numa():
154 set_numa_pages(pages, hugepgsz, node=node)
156 set_non_numa_pages(pages, hugepgsz)
159 def get_mountpoints():
160 '''Get list of where hugepage filesystem is mounted'''
162 with open('/proc/mounts') as mounts:
164 fields = line.split()
165 if fields[2] != 'hugetlbfs':
167 mounted.append(fields[1])
171 def mount_huge(pagesize, mountpoint):
172 '''Mount the huge TLB file system'''
173 if mountpoint in get_mountpoints():
174 print(mountpoint, "already mounted")
176 cmd = "mount -t hugetlbfs"
178 cmd += ' -o pagesize={}'.format(pagesize * 1024)
179 cmd += ' nodev ' + mountpoint
183 def umount_huge(mountpoint):
184 '''Unmount the huge TLB file system (if mounted)'''
185 if mountpoint in get_mountpoints():
186 os.system("umount " + mountpoint)
190 '''Show where huge page filesystem is mounted'''
191 mounted = get_mountpoints()
193 print("Hugepages mounted on", *mounted)
195 print("Hugepages not mounted")
199 '''Process the command line arguments and setup huge pages'''
200 parser = argparse.ArgumentParser(
201 formatter_class=argparse.RawDescriptionHelpFormatter,
202 description="Setup huge pages",
206 To display current huge page settings:
209 To a complete setup of with 2 Gigabyte of 1G huge pages:
210 %(prog)s -p 1G --setup 2G
216 help="print the current huge page configuration")
218 '--clear', '-c', action='store_true', help="clear existing huge pages")
223 help='mount the huge page filesystem')
228 help='unmount the system huge page directory')
230 '--node', '-n', help='select numa node to reserve pages on')
235 help='choose huge page size to use')
240 help='reserve huge pages. Size is in bytes with K, M, or G suffix')
244 help='setup huge pages by doing clear, unmount, reserve and mount')
245 args = parser.parse_args()
250 args.reserve = args.setup
254 pagesize_kb = get_memsize(args.pagesize)
256 pagesize_kb = default_pagesize()
261 umount_huge(HUGE_MOUNT)
264 reserve_kb = get_memsize(args.reserve)
265 if reserve_kb % pagesize_kb != 0:
267 'Huge reservation {}kB is not a multiple of page size {}kB'.
268 format(reserve_kb, pagesize_kb))
270 int(reserve_kb / pagesize_kb), pagesize_kb, node=args.node)
272 mount_huge(pagesize_kb, HUGE_MOUNT)
279 if __name__ == "__main__":