89
#define PROCFS_PATH "/proc"
90
#define RESOLV_CONF_FILE "resolv.conf"
91
#define RESOLV_CONF_PATH ETC_PATH "/" RESOLV_CONF_FILE
92
-#define RECLAIM_PATH "/sys/fs/cgroup/memory.reclaim"
92
#define SCSI_DEVICE_PATH "/sys/bus/scsi/devices"
93
#define SCSI_DEVICE_NAME_PREFIX "0:0:0:"
94
#define SCSI_DEVICE_PREFIX SCSI_DEVICE_PATH "/" SCSI_DEVICE_NAME_PREFIX
121
122
int Chroot(const char* Target);
123
125
-void ConfigureMemoryReduction(LX_MINI_INIT_MEMORY_RECLAIM_MODE Mode);
126
-
124
void CreateSwap(unsigned int Lun);
125
126
int CreateTempDirectory(const char* ParentPath, std::string& Path);
143
144
std::string GetMountTarget(const char* Name);
145
149
-long long int GetUserCpuTime(void);
150
-
151
-ssize_t GetMemoryInUse(void);
152
-
146
int ImportFromSocket(const char* Destination, int Socket, int ErrorSocket, unsigned int Flags);
147
148
int Initialize(const char* Hostname);
258
return 0;
259
}
260
268
-void ConfigureMemoryReduction(LX_MINI_INIT_MEMORY_RECLAIM_MODE Mode)
269
-
270
-/*++
271
-
272
-Routine Description:
273
-
274
- This routine configures memory reduction behavior including memory reclaim and compaction.
275
-
276
-Arguments:
277
-
278
- Mode - Supplies the memory reclaim mode.
279
-
280
-Return Value:
281
-
282
- None.
283
-
284
---*/
285
-
286
-try
287
-{
288
- //
289
- // Create a worker thread to periodically check if the VM is idle and performs memory compaction
290
- // and memory reclaim. This ensures that the maximum number of pages can be discarded to the host.
291
- //
292
-
293
- std::thread([Mode]() mutable {
294
- try
295
- {
296
- //
297
- // Set the thread's scheduling policy to idle.
298
- //
299
-
300
- sched_param Parameter{};
301
- Parameter.sched_priority = 0;
302
- THROW_LAST_ERROR_IF(pthread_setschedparam(pthread_self(), SCHED_IDLE, &Parameter) != 0);
303
-
304
- //
305
- // Periodically check if the machine is idle by querying procfs for CPU usage.
306
- // Memory compaction will occur if both of the following conditions are true:
307
- // 1. The CPU time since the last check is greater than the idle threshold.
308
- // 2. The current CPU usage is below the idle threshold. This is measured by taking two readings one second apart.
309
- //
310
-
311
- double MemoryLow = 1024 * 1024 * 1024;
312
- double MemoryHigh = 1.1 * 1024.0 * 1024.0 * 1024.0;
313
- const int IdleThreshold = get_nprocs(); // Change math to adjust if sysconf(_SC_CLK_TCK) != 100? Is 1%
314
- long long int Start, Stop = 0;
315
- auto constexpr SleepDuration = std::chrono::seconds(30);
316
- size_t ReclaimIndex = 0;
317
- long long int const ReclaimThreshold = (get_nprocs() * sysconf(_SC_CLK_TCK) * SleepDuration / std::chrono::seconds(1)) / 200; // 0.5%
318
- long long int ReclaimWindow[20] = {}; // 10 minutes
319
- long long int ReclaimWindowLength = COUNT_OF(ReclaimWindow);
320
- bool ReclaimIdling = false;
321
-
322
- //
323
- // Fall back to drop cache if the required cgroup path is not present.
324
- //
325
-
326
- if (Mode == LxMiniInitMemoryReclaimModeGradual && access(RECLAIM_PATH, W_OK) < 0)
327
- {
328
- LOG_WARNING("access({}, W_OK) failed {}, falling back to autoMemoryReclaim = dropcache", RECLAIM_PATH, errno);
329
- Mode = LxMiniInitMemoryReclaimModeDropCache;
330
- }
331
-
332
- if (Mode == LxMiniInitMemoryReclaimModeGradual)
333
- {
334
- static_assert(COUNT_OF(ReclaimWindow) >= 6);
335
- ReclaimWindowLength = 6; // Set to 3 minutes.
336
- }
337
-
338
- for (auto i = 1; i < ReclaimWindowLength; i++)
339
- {
340
- ReclaimWindow[i] = LLONG_MIN;
341
- }
342
-
343
- std::this_thread::sleep_for(SleepDuration);
344
- for (;;)
345
- {
346
- auto const Target = std::chrono::steady_clock::now() + SleepDuration;
347
- Start = GetUserCpuTime();
348
- THROW_LAST_ERROR_IF(Start == -1);
349
-
350
- if (Mode != LxMiniInitMemoryReclaimModeDisabled)
351
- {
352
- //
353
- // Ensure that utilization is below 0.5% from the last 30 seconds, and last n minutes, of usage.
354
- //
355
-
356
- size_t const LastIndex = (ReclaimIndex + 1) % ReclaimWindowLength;
357
- if ((ReclaimWindow[LastIndex] > Start - ReclaimThreshold * (ReclaimWindowLength + 1)) &&
358
- (ReclaimWindow[ReclaimIndex] > Start - ReclaimThreshold))
359
- {
360
- if (Mode == LxMiniInitMemoryReclaimModeGradual)
361
- {
362
- double MemorySize = GetMemoryInUse();
363
- THROW_LAST_ERROR_IF(MemorySize < 0);
364
-
365
- if (MemorySize > MemoryHigh)
366
- {
367
- ReclaimIdling = false;
368
- }
369
-
370
- if (!ReclaimIdling && MemorySize > MemoryLow)
371
- {
372
- double MemoryTargetSize = MemorySize * 0.97;
373
- std::string MemoryToFree = std::to_string(size_t(MemorySize - MemoryTargetSize));
374
- // EAGAIN Means that it attempted, but was unable to evict sufficient pages.
375
- THROW_LAST_ERROR_IF(WriteToFile(RECLAIM_PATH, MemoryToFree.c_str()) < 0 && errno != EAGAIN);
376
-
377
- if (MemoryTargetSize < MemoryLow)
378
- {
379
- ReclaimIdling = true;
380
- }
381
- }
382
- }
383
- else if (!ReclaimIdling)
384
- {
385
- ReclaimIdling = true;
386
- THROW_LAST_ERROR_IF(WriteToFile(PROCFS_PATH "/sys/vm/drop_caches", "1\n") < 0);
387
- }
388
- }
389
- else
390
- {
391
- ReclaimIdling = false;
392
- }
393
-
394
- ReclaimIndex = LastIndex;
395
- ReclaimWindow[ReclaimIndex] = Start;
396
- }
397
-
398
- //
399
- // Perform memory compaction if the VM is idle.
400
- // This coalesces free pages into larger blocks for more efficient page reporting.
401
- //
402
-
403
- if ((Start - Stop) > IdleThreshold)
404
- {
405
- std::this_thread::sleep_for(std::chrono::seconds(1));
406
- Stop = GetUserCpuTime();
407
- THROW_LAST_ERROR_IF(Stop == -1);
408
- if ((Stop - Start) < IdleThreshold)
409
- {
410
- THROW_LAST_ERROR_IF(WriteToFile(PROCFS_PATH "/sys/vm/compact_memory", "1\n") < 0);
411
- }
412
- }
413
-
414
- std::this_thread::sleep_until(Target);
415
- }
416
- }
417
- CATCH_LOG()
418
- }).detach();
419
-}
420
-CATCH_LOG()
421
-
261
wil::unique_fd CreateNetlinkSocket(void)
262
263
/*++
870
}
871
CATCH_RETURN_ERRNO()
872
1034
-long long int GetUserCpuTime(void)
1035
-
1036
-/*++
1037
-
1038
-Routine Description:
1039
-
1040
- This routine parses /proc/stat to query a summary of all user CPU time.
1041
-
1042
-Arguments:
1043
-
1044
- None.
1045
-
1046
-Return Value:
1047
-
1048
- The current user CPU counter for all cores.
1049
-
1050
---*/
1051
-
1052
-{
1053
- wil::unique_fd Fd{open(PROCFS_PATH "/stat", O_RDONLY)};
1054
- if (!Fd)
1055
- {
1056
- LOG_ERROR("open failed {}", errno);
1057
- return -1;
1058
- }
1059
-
1060
- char Buffer[32];
1061
- int Result = TEMP_FAILURE_RETRY(read(Fd.get(), Buffer, (sizeof(Buffer) - 1)));
1062
- if (Result < 0)
1063
- {
1064
- LOG_ERROR("read failed {}", errno);
1065
- return -1;
1066
- }
1067
-
1068
- //
1069
- // Parse the first line of /proc/stat which is in the format
1070
- // "cpu <counter>".
1071
- //
1072
-
1073
- Buffer[Result] = '\0';
1074
- char* Sp1;
1075
- char* Info = strtok_r(Buffer, " \n", &Sp1);
1076
- Info = strtok_r(nullptr, " \n", &Sp1);
1077
- return strtoll(Info, nullptr, 10);
1078
-}
1079
-
1080
-ssize_t GetMemoryInUse(void)
1081
-
1082
-/*++
1083
-
1084
-Routine Description:
1085
-
1086
- This routine returns the amount memory in use in bytes.
1087
-
1088
-Arguments:
1089
-
1090
- None.
1091
-
1092
-Return Value:
1093
-
1094
- Total memory - Free memory. Includes that used by cache and buffers.
1095
-
1096
---*/
1097
-try
1098
-{
1099
- struct sysinfo Info = {};
1100
- THROW_LAST_ERROR_IF(sysinfo(&Info) < 0);
1101
- return Info.totalram - Info.freeram;
1102
-}
1103
-CATCH_RETURN_ERRNO()
1104
-
873
int ImportFromSocket(const char* Destination, int Socket, int ErrorSocket, unsigned int Flags)
874
875
/*++
3035
// Configure memory reclamation.
3036
//
3037
3270
- ConfigureMemoryReduction(EarlyConfig->MemoryReclaimMode);
3038
+ StartMemoryReductionThread(EarlyConfig->MemoryReclaimMode);
3039
3040
//
3041
// Initialize system distro if supported.