Results: Default latency-histrogram with Spirent TestCenter
[vswitchperf.git] / tools / hugepages.py
1 # Copyright 2015-2017 Intel Corporation.
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 """Automation of hugepages management
16 """
17
18 import os
19 import re
20 import subprocess
21 import logging
22 import locale
23 import math
24
25 from tools import tasks
26 from conf import settings
27
28 _LOGGER = logging.getLogger(__name__)
29 _ALLOCATED_HUGEPAGES = False
30 #
31 # hugepage management
32 #
33
34
35 def get_hugepage_size():
36     """Return the size of the configured hugepages
37     """
38     hugepage_size_re = re.compile(r'^Hugepagesize:\s+(?P<size_hp>\d+)\s+kB',
39                                   re.IGNORECASE)
40     with open('/proc/meminfo', 'r') as result_file:
41         data = result_file.readlines()
42         for line in data:
43             match = hugepage_size_re.search(line)
44             if match:
45                 _LOGGER.info('Hugepages size: %s kb', match.group('size_hp'))
46                 return int(match.group('size_hp'))
47         _LOGGER.error('Could not parse for hugepage size')
48         return 0
49
50
51 def allocate_hugepages():
52     """Allocate hugepages on the fly
53     """
54     hp_size = get_hugepage_size()
55     if hp_size > 0:
56         nr_hp = int(math.ceil(settings.getValue('HUGEPAGE_RAM_ALLOCATION')/hp_size))
57         _LOGGER.info('Will allocate %s hugepages.', nr_hp)
58
59         nr_hugepages = 'vm.nr_hugepages=' + str(nr_hp)
60         try:
61             tasks.run_task(['sudo', 'sysctl', nr_hugepages],
62                            _LOGGER, 'Trying to allocate hugepages..', True)
63         except subprocess.CalledProcessError:
64             _LOGGER.error('Unable to allocate hugepages.')
65             return False
66         # pylint: disable=global-statement
67         global _ALLOCATED_HUGEPAGES
68         _ALLOCATED_HUGEPAGES = True
69         return True
70
71     else:
72         _LOGGER.error('Division by 0 will be supported in next release')
73         return False
74
75 def deallocate_hugepages():
76     """De-allocate hugepages that were allocated on the fly
77     """
78     # pylint: disable=global-statement
79     global _ALLOCATED_HUGEPAGES
80     if _ALLOCATED_HUGEPAGES:
81         nr_hugepages = 'vm.nr_hugepages= 0'
82         try:
83             tasks.run_task(['sudo', 'sysctl', nr_hugepages],
84                            _LOGGER, 'Trying to de-allocate hugepages..', True)
85         except subprocess.CalledProcessError:
86             _LOGGER.error('Unable to de-allocate hugepages.')
87             return False
88         _ALLOCATED_HUGEPAGES = False
89     return True
90
91
92 def get_free_hugepages(socket=None):
93     """Get the free hugepage totals on the system.
94
95     :param socket: optional socket param to get free hugepages on a socket. To
96                    be passed a string.
97     :returns: hugepage amount as int
98     """
99     hugepage_free_re = re.compile(r'HugePages_Free:\s+(?P<free_hp>\d+)$')
100     if socket:
101         if os.path.exists(
102                 '/sys/devices/system/node/node{}/meminfo'.format(socket)):
103             meminfo_path = '/sys/devices/system/node/node{}/meminfo'.format(
104                 socket)
105         else:
106             _LOGGER.info('No hugepage info found for socket %s', socket)
107             return 0
108     else:
109         meminfo_path = '/proc/meminfo'
110
111     with open(meminfo_path, 'r') as result_file:
112         data = result_file.readlines()
113         for line in data:
114             match = hugepage_free_re.search(line)
115             if match:
116                 _LOGGER.info('Hugepages free: %s %s', match.group('free_hp'),
117                              'on socket {}'.format(socket) if socket else '')
118                 return int(match.group('free_hp'))
119         _LOGGER.info('Could not parse for hugepage size')
120         return 0
121
122
123 def is_hugepage_available():
124     """Check if hugepages are configured/available on the system.
125     """
126     hugepage_size_re = re.compile(r'^Hugepagesize:\s+(?P<size_hp>\d+)\s+kB',
127                                   re.IGNORECASE)
128
129     # read in meminfo
130     with open('/proc/meminfo') as mem_file:
131         mem_info = mem_file.readlines()
132
133     # see if the hugepage size is the recommended value
134     for line in mem_info:
135         match_size = hugepage_size_re.match(line)
136         if match_size:
137             if match_size.group('size_hp') != '1048576':
138                 _LOGGER.info(
139                     '%s%s%s kB',
140                     'Hugepages not configured for recommend 1GB size. ',
141                     'Currently set at ', match_size.group('size_hp'))
142     num_huge = get_free_hugepages()
143     if num_huge == 0:
144         _LOGGER.info('No free hugepages.')
145         if not allocate_hugepages():
146             return False
147     else:
148         _LOGGER.info('Found \'%s\' free hugepage(s).', num_huge)
149     return True
150
151
152 def is_hugepage_mounted():
153     """Check if hugepages are mounted.
154     """
155     output = subprocess.check_output(['mount'], shell=True)
156     my_encoding = locale.getdefaultlocale()[1]
157     for line in output.decode(my_encoding).split('\n'):
158         if 'hugetlbfs' in line:
159             return True
160
161     return False
162
163
164 def mount_hugepages():
165     """Ensure hugepages are mounted. Raises RuntimeError if no configured
166     hugepages are available.
167     """
168     if not is_hugepage_available():
169         raise RuntimeError('No Hugepages configured.')
170
171     if is_hugepage_mounted():
172         return
173
174     if not os.path.exists(settings.getValue('HUGEPAGE_DIR')):
175         tasks.run_task(['sudo', 'mkdir', settings.getValue('HUGEPAGE_DIR')], _LOGGER,
176                        'Creating directory ' + settings.getValue('HUGEPAGE_DIR'), True)
177     try:
178         tasks.run_task(['sudo', 'mount', '-t', 'hugetlbfs', 'nodev',
179                         settings.getValue('HUGEPAGE_DIR')],
180                        _LOGGER, 'Mounting hugepages...', True)
181     except subprocess.CalledProcessError:
182         _LOGGER.error('Unable to mount hugepages.')
183
184
185 def umount_hugepages():
186     """Ensure hugepages are unmounted.
187     """
188     if not is_hugepage_mounted():
189         return
190
191     try:
192         tasks.run_task(['sudo', 'umount', settings.getValue('HUGEPAGE_DIR')],
193                        _LOGGER, 'Unmounting hugepages...', True)
194     except subprocess.CalledProcessError:
195         _LOGGER.error('Unable to umount hugepages.')
196
197     if not deallocate_hugepages():
198         _LOGGER.error('Unable to deallocate previously allocated hugepages.')