Line data Source code
1 : #include "configure.h"
2 :
3 : #include "../../fd_bootinfo.h"
4 : #include "../../../platform/fd_file_util.h"
5 : #include "../../../platform/fd_sys_util.h"
6 :
7 : #include <unistd.h>
8 : #include <errno.h>
9 : #include <stdio.h>
10 : #include <stdlib.h> /* strtoul */
11 : #include <dirent.h>
12 : #include <sys/stat.h>
13 : #include <sys/mount.h>
14 : #include <linux/capability.h>
15 :
16 : static void
17 : init_perm( fd_cap_chk_t * chk,
18 0 : config_t const * config FD_PARAM_UNUSED ) {
19 0 : fd_cap_chk_root( chk, "hugetlbfs", "increase `/proc/sys/vm/nr_hugepages`" );
20 0 : fd_cap_chk_cap( chk, "hugetlbfs", CAP_SYS_ADMIN, "mount hugetlbfs filesystems" );
21 0 : }
22 :
23 : static void
24 : fini_perm( fd_cap_chk_t * chk,
25 0 : config_t const * config FD_PARAM_UNUSED ) {
26 0 : fd_cap_chk_root( chk, "hugetlbfs", "remove directories from `/mnt`" );
27 0 : fd_cap_chk_cap( chk, "hugetlbfs", CAP_SYS_ADMIN, "unmount hugetlbfs filesystems" );
28 0 : }
29 :
30 : static char const * TOTAL_HUGE_PAGE_PATH[ 2 ] = {
31 : "/sys/devices/system/node/node%lu/hugepages/hugepages-2048kB/nr_hugepages",
32 : "/sys/devices/system/node/node%lu/hugepages/hugepages-1048576kB/nr_hugepages",
33 : };
34 :
35 : static char const * FREE_HUGE_PAGE_PATH[ 2 ] = {
36 : "/sys/devices/system/node/node%lu/hugepages/hugepages-2048kB/free_hugepages",
37 : "/sys/devices/system/node/node%lu/hugepages/hugepages-1048576kB/free_hugepages",
38 : };
39 :
40 : static ulong FD_PAGE_SIZE[ 2 ] = {
41 : 2097152,
42 : 1073741824,
43 : };
44 :
45 : static char const * PAGE_NAMES[ 2 ] = {
46 : "huge",
47 : "gigantic"
48 : };
49 :
50 : static void
51 0 : init( config_t const * config ) {
52 0 : char const * mount_path[ 2 ] = {
53 0 : config->hugetlbfs.huge_page_mount_path,
54 0 : config->hugetlbfs.gigantic_page_mount_path,
55 0 : };
56 :
57 0 : ulong numa_node_cnt = fd_shmem_numa_cnt();
58 0 : for( ulong i=0UL; i<numa_node_cnt; i++ ) {
59 0 : ulong required_pages[ 2 ] = {
60 0 : fd_topo_huge_page_cnt( &config->topo, i, 0 ),
61 0 : fd_topo_gigantic_page_cnt( &config->topo, i ),
62 0 : };
63 :
64 0 : for( ulong j=0UL; j<2UL; j++ ) {
65 0 : char free_page_path[ PATH_MAX ];
66 0 : FD_TEST( fd_cstr_printf_check( free_page_path, PATH_MAX, NULL, FREE_HUGE_PAGE_PATH[ j ], i ) );
67 0 : uint free_pages;
68 0 : if( FD_UNLIKELY( -1==fd_file_util_read_uint( free_page_path, &free_pages ) ) )
69 0 : FD_LOG_ERR(( "could not read `%s`, please confirm your host is configured for gigantic pages (%i-%s)", free_page_path, errno, fd_io_strerror( errno ) ));
70 :
71 : /* There is a TOCTOU race condition here, but it's not avoidable. There's
72 : no way to atomically increment the page count. */
73 0 : FD_TEST( required_pages[ j ]<=UINT_MAX );
74 0 : if( FD_UNLIKELY( free_pages<required_pages[ j ] ) ) {
75 0 : char total_page_path[ PATH_MAX ];
76 0 : FD_TEST( fd_cstr_printf_check( total_page_path, PATH_MAX, NULL, TOTAL_HUGE_PAGE_PATH[ j ], i ) );
77 0 : uint total_pages;
78 0 : if( FD_UNLIKELY( -1==fd_file_util_read_uint( total_page_path, &total_pages ) ) )
79 0 : FD_LOG_ERR(( "could not read `%s`, please confirm your host is configured for gigantic pages (%i-%s)", total_page_path, errno, fd_io_strerror( errno ) ));
80 :
81 0 : ulong additional_pages_needed = required_pages[ j ]-free_pages;
82 :
83 0 : FD_LOG_NOTICE(( "%sRUN: `echo \"%u\" > %s`%s", fd_log_style_dim(), (uint)(total_pages+additional_pages_needed), total_page_path , fd_log_style_normal() ));
84 0 : if( FD_UNLIKELY( -1==fd_file_util_write_uint( total_page_path, (uint)(total_pages+additional_pages_needed) ) ) )
85 0 : FD_LOG_ERR(( "could not increase the number of %s pages on NUMA node %lu (%i-%s)", PAGE_NAMES[ j ], i, errno, fd_io_strerror( errno ) ));
86 :
87 0 : uint raised_free_pages;
88 0 : if( FD_UNLIKELY( -1==fd_file_util_read_uint( free_page_path, &raised_free_pages ) ) )
89 0 : FD_LOG_ERR(( "could not read `%s`, please confirm your host is configured for gigantic pages (%i-%s)", free_page_path, errno, fd_io_strerror( errno ) ));
90 :
91 0 : if( FD_UNLIKELY( raised_free_pages<required_pages[ j ] ) ) {
92 : /* Well.. usually this is due to memory being fragmented,
93 : rather than not having enough memory. See something like
94 : https://tatref.github.io/blog/2023-visual-linux-memory-compact/
95 : for the sequence we do here. */
96 0 : FD_LOG_WARNING(( "ENOMEM-Out of memory when trying to reserve %s pages for Firedancer on NUMA node %lu. Compacting memory before trying again.",
97 0 : PAGE_NAMES[ j ],
98 0 : i ));
99 0 : FD_LOG_NOTICE(( "%sRUN: `echo \"1\" > /proc/sys/vm/compact_memory%s", fd_log_style_dim() , fd_log_style_normal() ));
100 0 : if( FD_UNLIKELY( -1==fd_file_util_write_uint( "/proc/sys/vm/compact_memory", 1 ) ) )
101 0 : FD_LOG_ERR(( "could not write to `%s` (%i-%s)", "/proc/sys/vm/compact_memory", errno, fd_io_strerror( errno ) ));
102 : /* Sleep a little to give the OS some time to perform the
103 : compaction. */
104 0 : FD_TEST( -1!=fd_sys_util_nanosleep( 0, 500000000 /* 500 millis */ ) );
105 0 : FD_LOG_NOTICE(( "%sRUN: `echo \"3\" > /proc/sys/vm/drop_caches%s", fd_log_style_dim() , fd_log_style_normal() ));
106 0 : if( FD_UNLIKELY( -1==fd_file_util_write_uint( "/proc/sys/vm/drop_caches", 3 ) ) )
107 0 : FD_LOG_ERR(( "could not write to `%s` (%i-%s)", "/proc/sys/vm/drop_caches", errno, fd_io_strerror( errno ) ));
108 0 : FD_TEST( -1!=fd_sys_util_nanosleep( 0, 500000000 /* 500 millis */ ) );
109 0 : FD_LOG_NOTICE(( "%sRUN: `echo \"1\" > /proc/sys/vm/compact_memory%s", fd_log_style_dim() , fd_log_style_normal() ));
110 0 : if( FD_UNLIKELY( -1==fd_file_util_write_uint( "/proc/sys/vm/compact_memory", 1 ) ) )
111 0 : FD_LOG_ERR(( "could not write to `%s` (%i-%s)", "/proc/sys/vm/compact_memory", errno, fd_io_strerror( errno ) ));
112 0 : FD_TEST( -1!=fd_sys_util_nanosleep( 0, 500000000 /* 500 millis */ ) );
113 0 : }
114 :
115 0 : FD_LOG_NOTICE(( "%sRUN: `echo \"%u\" > %s`%s", fd_log_style_dim(), (uint)(total_pages+additional_pages_needed), total_page_path , fd_log_style_normal() ));
116 0 : if( FD_UNLIKELY( -1==fd_file_util_write_uint( total_page_path, (uint)(total_pages+additional_pages_needed) ) ) )
117 0 : FD_LOG_ERR(( "could not increase the number of %s pages on NUMA node %lu (%i-%s)", PAGE_NAMES[ j ], i, errno, fd_io_strerror( errno ) ));
118 0 : if( FD_UNLIKELY( -1==fd_file_util_read_uint( free_page_path, &raised_free_pages ) ) )
119 0 : FD_LOG_ERR(( "could not read `%s`, please confirm your host is configured for gigantic pages (%i-%s)", free_page_path, errno, fd_io_strerror( errno ) ));
120 0 : if( FD_UNLIKELY( raised_free_pages<required_pages[ j ] ) ) {
121 0 : FD_LOG_ERR(( "ENOMEM-Out of memory when trying to reserve %s pages for Firedancer on NUMA node %lu. Your Firedancer "
122 0 : "configuration requires %lu GiB of memory total consisting of %lu gigantic (1GiB) pages and %lu huge (2MiB) "
123 0 : "pages on this NUMA node but only %u %s pages were available according to `%s` (raised from %u). If your "
124 0 : "system has the required amount of memory, this can be because it is not configured with %s page support, or "
125 0 : "Firedancer cannot increase the value of `%s` at runtime. You might need to enable huge pages in grub at boot "
126 0 : "time. This error can also happen because system uptime is high and memory is fragmented. You can fix this by "
127 0 : "rebooting the machine and running the `hugetlbfs` stage immediately on boot.",
128 0 : PAGE_NAMES[ j ],
129 0 : i,
130 0 : required_pages[ 1 ] + (required_pages[ 0 ] / 512),
131 0 : required_pages[ 1 ],
132 0 : required_pages[ 0 ],
133 0 : raised_free_pages,
134 0 : PAGE_NAMES[ j ],
135 0 : free_page_path,
136 0 : free_pages,
137 0 : PAGE_NAMES[ j ],
138 0 : total_page_path ));
139 0 : }
140 0 : }
141 0 : }
142 0 : }
143 :
144 : /* Do NOT include anonymous huge pages in the min_size count that
145 : we reserve here, because they do not come from the hugetlbfs.
146 : Counting them towards that reservation would prevent the
147 : anonymous mmap which maps them in from succeeding.
148 :
149 : The kernel min_size option for the hugetlbfs does not include an
150 : option to reserve pages from a specific NUMA node, so we simply
151 : take the sum here and hope they are distributed correctly. If
152 : they are not, creating files in the mount on a specific node may
153 : fail later with ENOMEM. */
154 :
155 0 : ulong min_size[ 2 ] = {0};
156 0 : for( ulong i=0UL; i<numa_node_cnt; i++ ) {
157 0 : min_size[ 0 ] += FD_PAGE_SIZE[ 0 ] * fd_topo_huge_page_cnt( &config->topo, i, 0 );
158 0 : min_size[ 1 ] += FD_PAGE_SIZE[ 1 ] * fd_topo_gigantic_page_cnt( &config->topo, i );
159 0 : }
160 :
161 0 : for( ulong i=0UL; i<2UL; i++ ) {
162 0 : FD_LOG_NOTICE(( "%sRUN: `mkdir -p %s`%s", fd_log_style_dim(), mount_path[ i ] , fd_log_style_normal() ));
163 0 : if( FD_UNLIKELY( -1==fd_file_util_mkdir_all( mount_path[ i ], config->uid, config->gid, 1 ) ) ) {
164 0 : FD_LOG_ERR(( "could not create hugetlbfs mount directory `%s` (%i-%s)", mount_path[ i ], errno, fd_io_strerror( errno ) ));
165 0 : }
166 :
167 0 : char options[ 256 ];
168 0 : if( FD_LIKELY( config->development.hugetlbfs.min_size ) ) {
169 0 : FD_TEST( fd_cstr_printf_check( options, sizeof(options), NULL, "pagesize=%lu,min_size=%lu", FD_PAGE_SIZE[ i ], min_size[ i ] ) );
170 0 : } else {
171 0 : FD_TEST( fd_cstr_printf_check( options, sizeof(options), NULL, "pagesize=%lu", FD_PAGE_SIZE[ i ] ) );
172 0 : }
173 0 : FD_LOG_NOTICE(( "%sRUN: `mount -t hugetlbfs none %s -o %s`%s", fd_log_style_dim(), mount_path[ i ], options , fd_log_style_normal() ));
174 0 : if( FD_UNLIKELY( mount( "none", mount_path[ i ], "hugetlbfs", 0, options) ) )
175 0 : FD_LOG_ERR(( "mount of hugetlbfs at `%s` failed (%i-%s)", mount_path[ i ], errno, fd_io_strerror( errno ) ));
176 0 : if( FD_UNLIKELY( chown( mount_path[ i ], config->uid, config->gid ) ) )
177 0 : FD_LOG_ERR(( "chown of hugetlbfs at `%s` failed (%i-%s)", mount_path[ i ], errno, fd_io_strerror( errno ) ));
178 0 : if( FD_UNLIKELY( chmod( mount_path[ i ], S_IRUSR | S_IWUSR | S_IXUSR ) ) )
179 0 : FD_LOG_ERR(( "chmod of hugetlbfs at `%s` failed (%i-%s)", mount_path[ i ], errno, fd_io_strerror( errno ) ));
180 0 : }
181 0 : }
182 :
183 : static void
184 : cmdline( char * buf,
185 0 : ulong buf_sz ) {
186 0 : FILE * fp = fopen( "/proc/self/cmdline", "r" );
187 0 : if( FD_UNLIKELY( !fp ) ) FD_LOG_ERR(( "error opening `/proc/self/cmdline` (%i-%s)", errno, fd_io_strerror( errno ) ));
188 :
189 0 : ulong read = fread( buf, 1UL, buf_sz - 1UL, fp );
190 0 : if( FD_UNLIKELY( ferror( fp ) ) ) FD_LOG_ERR(( "error reading `/proc/self/cmdline` (%i-%s)", errno, fd_io_strerror( errno ) ));
191 0 : if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "error closing `/proc/self/cmdline` (%i-%s)", errno, fd_io_strerror( errno ) ));
192 :
193 0 : buf[ read ] = '\0';
194 0 : }
195 :
196 : static void
197 0 : warn_mount_users( char const * mount_path ) {
198 0 : DIR * dir = opendir( "/proc" );
199 0 : if( FD_UNLIKELY( !dir ) ) FD_LOG_ERR(( "error opening `/proc` (%i-%s)", errno, fd_io_strerror( errno ) ));
200 :
201 0 : struct dirent * entry;
202 0 : for(;;) {
203 0 : errno = 0;
204 0 : entry = readdir( dir );
205 0 : if( FD_UNLIKELY( !entry ) ) break;
206 0 : if( FD_UNLIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
207 0 : char * endptr;
208 0 : ulong pid = strtoul( entry->d_name, &endptr, 10 );
209 0 : if( FD_UNLIKELY( *endptr ) ) continue;
210 :
211 0 : char path[ PATH_MAX ];
212 0 : FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "/proc/%lu/maps", pid ) );
213 0 : FILE * fp = fopen( path, "r" );
214 0 : if( FD_UNLIKELY( !fp ) ) {
215 0 : if( errno==ENOENT ) continue;
216 0 : FD_LOG_ERR(( "error opening `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
217 0 : }
218 :
219 0 : char self_cmdline[ PATH_MAX ];
220 0 : cmdline( self_cmdline, PATH_MAX );
221 :
222 0 : char line[ 4096 ];
223 0 : while( FD_LIKELY( fgets( line, 4096, fp ) ) ) {
224 0 : if( FD_UNLIKELY( strlen( line )==4095 ) ) FD_LOG_ERR(( "line too long in `%s`", path ));
225 0 : if( FD_UNLIKELY( strstr( line, mount_path ) ) ) {
226 0 : FD_LOG_WARNING(( "process `%lu`:`%s` has a file descriptor open in `%s`", pid, self_cmdline, mount_path ));
227 0 : break;
228 0 : }
229 0 : }
230 0 : if( FD_UNLIKELY( ferror( fp ) ) )
231 0 : FD_LOG_ERR(( "error reading `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
232 0 : if( FD_LIKELY( fclose( fp ) ) )
233 0 : FD_LOG_ERR(( "error closing `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
234 0 : }
235 :
236 0 : if( FD_UNLIKELY( errno ) ) FD_LOG_ERR(( "readdir() (%i-%s)", errno, fd_io_strerror( errno ) ));
237 0 : if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir (%i-%s)", errno, fd_io_strerror( errno ) ));
238 0 : }
239 :
240 : static int
241 : fini( config_t const * config,
242 0 : int pre_init ) {
243 0 : (void)pre_init;
244 :
245 0 : fd_bootinfo_unlink( config );
246 :
247 : /* Not used by fdctl but might be created by other debugging tools
248 : on the system. */
249 :
250 0 : char normal_page_mount_path[ PATH_MAX ];
251 0 : FD_TEST( fd_cstr_printf_check( normal_page_mount_path, PATH_MAX, NULL, "%s/.normal", config->hugetlbfs.mount_path ) );
252 :
253 0 : const char * mount_path[ 3 ] = {
254 0 : config->hugetlbfs.huge_page_mount_path,
255 0 : config->hugetlbfs.gigantic_page_mount_path,
256 0 : normal_page_mount_path,
257 0 : };
258 :
259 0 : for( ulong i=0UL; i<3UL; i++ ) {
260 0 : FILE * fp = fopen( "/proc/self/mounts", "r" );
261 0 : if( FD_UNLIKELY( !fp ) ) FD_LOG_ERR(( "failed to open `/proc/self/mounts`" ));
262 :
263 0 : char line[ 4096 ];
264 0 : while( FD_LIKELY( fgets( line, 4096UL, fp ) ) ) {
265 0 : if( FD_UNLIKELY( strlen( line )==4095UL ) ) FD_LOG_ERR(( "line too long in `/proc/self/mounts`" ));
266 0 : if( FD_UNLIKELY( strstr( line, mount_path[ i ] ) ) ) {
267 0 : FD_LOG_NOTICE(( "%sRUN: `umount %s`%s", fd_log_style_dim(), mount_path[ i ] , fd_log_style_normal() ));
268 0 : if( FD_UNLIKELY( umount( mount_path[ i ] ) ) ) {
269 0 : if( FD_LIKELY( errno==EBUSY ) ) {
270 0 : warn_mount_users( mount_path[ i ] );
271 :
272 0 : FD_LOG_ERR(( "Unmount of hugetlbfs at `%s` failed because the mount is still in use. "
273 0 : "You can unmount it by killing all processes that are actively using files in "
274 0 : "the mount and running `%s configure fini hugetlbfs` again, or unmount "
275 0 : "manually with `umount %s`", mount_path[ i ], FD_BINARY_NAME, mount_path[ i ] ));
276 0 : } else {
277 0 : FD_LOG_ERR(( "umount of hugetlbfs at `%s` failed (%i-%s)", mount_path[ i ], errno, fd_io_strerror( errno ) ));
278 0 : }
279 0 : }
280 0 : }
281 0 : }
282 :
283 0 : if( FD_UNLIKELY( ferror( fp ) ) )
284 0 : FD_LOG_ERR(( "error reading `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
285 0 : if( FD_LIKELY( fclose( fp ) ) )
286 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
287 :
288 0 : FD_LOG_NOTICE(( "%sRUN: `rmdir %s`%s", fd_log_style_dim(), mount_path[ i ] , fd_log_style_normal() ));
289 0 : if( FD_UNLIKELY( rmdir( mount_path[ i ] ) && errno!=ENOENT ) )
290 0 : FD_LOG_ERR(( "error removing hugetlbfs mount at `%s` (%i-%s)", mount_path[ i ], errno, fd_io_strerror( errno ) ));
291 0 : }
292 :
293 0 : FD_LOG_NOTICE(( "%sRUN: `rmdir %s`%s", fd_log_style_dim(), config->hugetlbfs.mount_path , fd_log_style_normal() ));
294 0 : if( FD_UNLIKELY( rmdir( config->hugetlbfs.mount_path ) && errno!=ENOENT ) )
295 0 : FD_LOG_ERR(( "error removing hugetlbfs directory at `%s` (%i-%s)", config->hugetlbfs.mount_path, errno, fd_io_strerror( errno ) ));
296 :
297 0 : return 1;
298 0 : }
299 :
300 : static configure_result_t
301 : check( config_t const * config,
302 0 : int check_type FD_PARAM_UNUSED ) {
303 0 : char const * mount_path[ 2 ] = {
304 0 : config->hugetlbfs.huge_page_mount_path,
305 0 : config->hugetlbfs.gigantic_page_mount_path,
306 0 : };
307 :
308 0 : static char const * MOUNT_PAGE_SIZE[ 2 ] = {
309 0 : "pagesize=2M",
310 0 : "pagesize=1024M",
311 0 : };
312 :
313 0 : ulong numa_node_cnt = fd_shmem_numa_cnt();
314 0 : ulong required_min_size[ 2 ] = {0};
315 0 : for( ulong i=0UL; i<numa_node_cnt; i++ ) {
316 0 : required_min_size[ 0 ] += FD_PAGE_SIZE[ 0 ] * fd_topo_huge_page_cnt( &config->topo, i, 0 );
317 0 : required_min_size[ 1 ] += FD_PAGE_SIZE[ 1 ] * fd_topo_gigantic_page_cnt( &config->topo, i );
318 0 : }
319 :
320 0 : struct stat st;
321 0 : int result1 = stat( mount_path[ 0 ], &st );
322 0 : if( FD_UNLIKELY( result1 && errno!=ENOENT ) )
323 0 : PARTIALLY_CONFIGURED( "failed to stat `%s` (%i-%s)", mount_path[ 0 ], errno, fd_io_strerror( errno ) );
324 0 : int result2 = stat( mount_path[ 1 ], &st );
325 0 : if( FD_UNLIKELY( result2 && errno!=ENOENT ) )
326 0 : PARTIALLY_CONFIGURED( "failed to stat `%s` (%i-%s)", mount_path[ 1 ], errno, fd_io_strerror( errno ) );
327 :
328 0 : if( FD_UNLIKELY( result1 && result2 ) )
329 0 : NOT_CONFIGURED( "mounts `%s` and `%s` do not exist", mount_path[ 0 ], mount_path[ 1 ] );
330 0 : else if( FD_UNLIKELY( result1 ) )
331 0 : PARTIALLY_CONFIGURED( "mount `%s` does not exist", mount_path[ 0 ] );
332 0 : else if( FD_UNLIKELY( result2 ) )
333 0 : PARTIALLY_CONFIGURED( "mount `%s` does not exist", mount_path[ 1 ] );
334 :
335 0 : CHECK( check_dir( config->hugetlbfs.mount_path, config->uid, config->gid, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR ) );
336 0 : for( ulong i=0UL; i<2UL; i++ ) {
337 0 : CHECK( check_dir( mount_path[ i ], config->uid, config->gid, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR ) );
338 :
339 0 : FILE * fp = fopen( "/proc/self/mounts", "r" );
340 0 : if( FD_UNLIKELY( !fp ) ) FD_LOG_ERR(( "failed to open `/proc/self/mounts`" ));
341 :
342 0 : char line[ 4096 ];
343 0 : int found = 0;
344 0 : while( FD_LIKELY( fgets( line, 4096UL, fp ) ) ) {
345 0 : if( FD_UNLIKELY( strlen( line )==4095UL ) ) FD_LOG_ERR(( "line too long in `/proc/self/mounts`" ));
346 0 : if( FD_UNLIKELY( strstr( line, mount_path[ i ] ) ) ) {
347 0 : found = 1;
348 :
349 0 : char * saveptr;
350 0 : char * device = strtok_r( line, " ", &saveptr );
351 0 : if( FD_UNLIKELY( !device ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
352 0 : if( FD_UNLIKELY( strcmp( device, "none" ) ) ) {
353 0 : if( FD_UNLIKELY( fclose( fp ) ) )
354 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
355 0 : PARTIALLY_CONFIGURED( "mount `%s` is on unrecognized device, expected `none`", mount_path[ i ] );
356 0 : }
357 :
358 0 : char * path1 = strtok_r( NULL, " ", &saveptr );
359 0 : if( FD_UNLIKELY( !path1 ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
360 0 : if( FD_UNLIKELY( strcmp( path1, mount_path[ i ] ) ) ) {
361 0 : if( FD_UNLIKELY( fclose( fp ) ) )
362 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
363 0 : PARTIALLY_CONFIGURED( "mount `%s` is on unrecognized path, expected `%s`", path1, mount_path[ i ] );
364 0 : }
365 :
366 0 : char * type = strtok_r( NULL, " ", &saveptr );
367 0 : if( FD_UNLIKELY( !type ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
368 0 : if( FD_UNLIKELY( strcmp( type, "hugetlbfs" ) ) ) {
369 0 : if( FD_UNLIKELY( fclose( fp ) ) )
370 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
371 0 : PARTIALLY_CONFIGURED( "mount `%s` has unrecognized type, expected `hugetlbfs`", mount_path[ i ] );
372 0 : }
373 :
374 0 : char * options = strtok_r( NULL, " ", &saveptr );
375 0 : if( FD_UNLIKELY( !options ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
376 :
377 0 : char * saveptr2;
378 0 : char * rw = strtok_r( options, ",", &saveptr2 );
379 0 : if( FD_UNLIKELY( !rw ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
380 0 : if( FD_UNLIKELY( strcmp( rw, "rw" ) ) ) {
381 0 : if( FD_UNLIKELY( fclose( fp ) ) )
382 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
383 0 : PARTIALLY_CONFIGURED( "mount `%s` is not mounted read/write, expected `rw`", mount_path[ i ] );
384 0 : }
385 :
386 0 : char * seclabel = strtok_r( NULL, ",", &saveptr2 );
387 0 : if( FD_UNLIKELY( !seclabel ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
388 :
389 0 : char * relatime;
390 0 : if( FD_LIKELY( !strcmp( seclabel, "seclabel" ) ) ) {
391 0 : relatime = strtok_r( NULL, ",", &saveptr2 );
392 0 : if( FD_UNLIKELY( !relatime ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
393 0 : } else {
394 0 : relatime = seclabel;
395 0 : }
396 :
397 0 : if( FD_UNLIKELY( strcmp( relatime, "relatime" ) ) ) {
398 0 : if( FD_UNLIKELY( fclose( fp ) ) )
399 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
400 0 : PARTIALLY_CONFIGURED( "mount `%s` is not mounted with `relatime`, expected `relatime`", mount_path[ i ] );
401 0 : }
402 :
403 0 : char * gid = strtok_r( NULL, ",", &saveptr2 );
404 0 : if( FD_UNLIKELY( !gid ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
405 :
406 0 : char * pagesize;
407 0 : if( FD_UNLIKELY( !strncmp( "gid=", gid, 4UL ) ) ) {
408 0 : pagesize = strtok_r( NULL, ",", &saveptr2 );
409 0 : if( FD_UNLIKELY( !pagesize ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
410 0 : } else {
411 0 : pagesize = gid;
412 0 : }
413 :
414 0 : if( FD_UNLIKELY( !pagesize ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line ));
415 0 : if( FD_UNLIKELY( strcmp( pagesize, MOUNT_PAGE_SIZE[ i ] ) ) ) {
416 0 : if( FD_UNLIKELY( fclose( fp ) ) )
417 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
418 0 : PARTIALLY_CONFIGURED( "mount `%s` has unrecognized pagesize, expected `%s` %s", mount_path[ i ], MOUNT_PAGE_SIZE[ i ], pagesize );
419 0 : }
420 :
421 0 : char * _min_size = strtok_r( NULL, ",", &saveptr2 );
422 :
423 0 : if( FD_LIKELY( config->development.hugetlbfs.min_size ) ) {
424 0 : if( FD_UNLIKELY( !_min_size || strncmp( "min_size=", _min_size, 9UL ) ) ) {
425 0 : if( FD_UNLIKELY( fclose( fp ) ) )
426 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
427 0 : PARTIALLY_CONFIGURED( "mount `%s` has unrecognized min_size, expected at least `min_size=%lu`", mount_path[ i ], required_min_size[ i ] );
428 0 : }
429 :
430 0 : char * endptr;
431 0 : ulong min_size = strtoul( _min_size+9, &endptr, 10 );
432 0 : if( FD_UNLIKELY( *endptr ) ) {
433 0 : if( FD_UNLIKELY( fclose( fp ) ) )
434 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
435 0 : PARTIALLY_CONFIGURED( "mount `%s` has malformed min_size, expected `min_size=%lu`", mount_path[ i ], required_min_size[ i ] );
436 0 : }
437 :
438 0 : if( FD_UNLIKELY( min_size<required_min_size[ i ] ) ) {
439 0 : if( FD_UNLIKELY( fclose( fp ) ) )
440 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
441 0 : PARTIALLY_CONFIGURED( "mount `%s` has min_size `%lu`, expected at least `min_size=%lu`", mount_path[ i ], min_size, required_min_size[ i ] );
442 0 : }
443 0 : } else {
444 : /* When mounted with min_size disabled, the mount must NOT have a
445 : min_size= option (so the kernel does not persistently reserve
446 : pages and re-running configure is a no-op). If a stale mount
447 : still has min_size= (e.g. created with min_size enabled),
448 : report it as misconfigured so it gets torn down and recreated. */
449 0 : if( FD_UNLIKELY( _min_size && !strncmp( "min_size=", _min_size, 9UL ) ) ) {
450 0 : if( FD_UNLIKELY( fclose( fp ) ) )
451 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
452 0 : PARTIALLY_CONFIGURED( "mount `%s` has a `min_size` option but development.hugetlbfs.min_size is disabled", mount_path[ i ] );
453 0 : }
454 0 : }
455 :
456 0 : break;
457 0 : }
458 0 : }
459 :
460 0 : if( FD_UNLIKELY( ferror( fp ) ) )
461 0 : FD_LOG_ERR(( "error reading `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
462 0 : if( FD_LIKELY( fclose( fp ) ) )
463 0 : FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, fd_io_strerror( errno ) ));
464 :
465 0 : if( FD_UNLIKELY( !found ) )
466 0 : PARTIALLY_CONFIGURED( "mount `%s` not found in `/proc/self/mounts`", mount_path[ i ] );
467 0 : }
468 :
469 0 : CONFIGURE_OK();
470 0 : }
471 :
472 : configure_stage_t fd_cfg_stage_hugetlbfs = {
473 : .name = "hugetlbfs",
474 : .always_recreate = 0,
475 : .enabled = NULL,
476 : .init_perm = init_perm,
477 : .fini_perm = fini_perm,
478 : .init = init,
479 : .fini = fini,
480 : .check = check,
481 : };
|