Merge "mrg_buff_doc: Add documentation on mergable buffer option"
[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 _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 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 kb', 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     if hp_size > 0:
57        nr_hp = int(math.ceil(settings.getValue('HUGEPAGE_RAM_ALLOCATION')/hp_size))
58        _LOGGER.info('Will allocate %s hugepages.', nr_hp)
59
60        nr_hugepages = 'vm.nr_hugepages=' + str(nr_hp)
61        try:
62            tasks.run_task(['sudo', 'sysctl', nr_hugepages],
63                           _LOGGER, 'Trying to allocate hugepages..', True)
64        except subprocess.CalledProcessError:
65            _LOGGER.error('Unable to allocate hugepages.')
66            return False
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     global _allocated_hugepages
79     if _allocated_hugepages:
80         nr_hugepages = 'vm.nr_hugepages= 0'
81         try:
82             tasks.run_task(['sudo', 'sysctl', nr_hugepages],
83                            _LOGGER, 'Trying to de-allocate hugepages..', True)
84         except subprocess.CalledProcessError:
85             _LOGGER.error('Unable to de-allocate hugepages.')
86             return False
87         _allocated_hugepages = False
88     return True
89
90
91 def get_free_hugepages(socket=None):
92     """Get the free hugepage totals on the system.
93
94     :param socket: optional socket param to get free hugepages on a socket. To
95                    be passed a string.
96     :returns: hugepage amount as int
97     """
98     hugepage_free_re = re.compile(r'HugePages_Free:\s+(?P<free_hp>\d+)$')
99     if socket:
100         if os.path.exists(
101                 '/sys/devices/system/node/node{}/meminfo'.format(socket)):
102             meminfo_path = '/sys/devices/system/node/node{}/meminfo'.format(
103                 socket)
104         else:
105             _LOGGER.info('No hugepage info found for socket {}'.format(socket))
106             return 0
107     else:
108         meminfo_path = '/proc/meminfo'
109
110     with open(meminfo_path, 'r') as fh:
111         data = fh.readlines()
112         for line in data:
113             match = hugepage_free_re.search(line)
114             if match:
115                 _LOGGER.info('Hugepages free: %s %s', match.group('free_hp'),
116                              'on socket {}'.format(socket) if socket else '')
117                 return int(match.group('free_hp'))
118         else:
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.')