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