|
12 | 12 |
|
13 | 13 | #include <chrono> |
14 | 14 | #include "fpp-json.h" |
| 15 | +#include <cctype> |
15 | 16 | #include <cstdio> |
| 17 | +#include <cstdlib> |
16 | 18 | #include <cstring> |
17 | 19 | #include <fcntl.h> |
18 | 20 | #include <filesystem> |
@@ -935,6 +937,93 @@ void startDiskSwap() { |
935 | 937 | "| while read -r dev; do /sbin/swapon \"$dev\" 2>/dev/null || true; done"); |
936 | 938 | } |
937 | 939 |
|
| 940 | +#ifdef PLATFORM_PI |
| 941 | +// Shrink an oversized vc4-KMS CMA pool on boards that cannot afford it. |
| 942 | +// |
| 943 | +// The image ships "dtoverlay=vc4-kms-v3d,cma-256" for every Pi (FPP_Install.sh |
| 944 | +// adds it to cap the Pi4's 512MB firmware default -- issue #2679). But the |
| 945 | +// overlay's own README documents cma-256, and everything above cma-128, as |
| 946 | +// "needs 1GB", and the firmware does NOT clamp it: a 512MB Pi Zero 2 W really |
| 947 | +// comes up with CmaTotal 262144 kB. That is half the board handed to a GPU pool |
| 948 | +// on a headless controller, and it is enough to break a source rebuild -- it |
| 949 | +// leaves ~163MB usable, well under what cc1plus needs. |
| 950 | +// |
| 951 | +// So cap at cma-128, the largest size the overlay does not gate behind 1GB. |
| 952 | +// FPP's real DMA needs are far below that: the DPI envelope declared below is |
| 953 | +// 1920x997 RGB888, about 7.6MB. |
| 954 | +// |
| 955 | +// Only ever REDUCE: a deliberate smaller override (cma-64) is left alone, since |
| 956 | +// raising it would defeat the purpose. Rewrites `content` in place and sets |
| 957 | +// `changed` so the caller's existing reboot handles it; comparing before writing |
| 958 | +// keeps it idempotent, so it fires once and never loops. |
| 959 | +// MemTotal counts the CMA reservation, so it is the board's usable total, not |
| 960 | +// its marketing size: a 512MB board reports ~425-500MB, a 1GB board ~900MB+. |
| 961 | +// 700MB separates them with room on both sides. |
| 962 | +static constexpr long CMA_ONE_GB_BOARD_KB = 700000; |
| 963 | +static constexpr int CMA_MAX_UNDER_1GB = 128; |
| 964 | + |
| 965 | +// Pure half: no file I/O, so it can be lifted out between the markers below and |
| 966 | +// exercised directly against sample config.txt text. Edits `content` in place |
| 967 | +// and returns true if it changed anything. |
| 968 | +// --- BEGIN capVC4CMALine --- |
| 969 | +static bool capVC4CMALine(std::string& content, long memKB) { |
| 970 | + if (memKB <= 0 || memKB >= CMA_ONE_GB_BOARD_KB) { |
| 971 | + return false; |
| 972 | + } |
| 973 | + // Match the overlay line itself, never a commented-out or indented copy. |
| 974 | + // Covers both the generic overlay and the -pi5 variant. |
| 975 | + size_t vidx = content.find("dtoverlay=vc4-kms-v3d"); |
| 976 | + while (vidx != std::string::npos && vidx != 0 && content[vidx - 1] != '\n') { |
| 977 | + vidx = content.find("dtoverlay=vc4-kms-v3d", vidx + 1); |
| 978 | + } |
| 979 | + if (vidx == std::string::npos) { |
| 980 | + return false; |
| 981 | + } |
| 982 | + size_t eol = content.find("\n", vidx); |
| 983 | + if (eol == std::string::npos) { |
| 984 | + eol = content.size(); |
| 985 | + } |
| 986 | + std::string line = content.substr(vidx, eol - vidx); |
| 987 | + |
| 988 | + size_t cidx = line.find("cma-"); |
| 989 | + if (cidx == std::string::npos) { |
| 990 | + return false; |
| 991 | + } |
| 992 | + size_t dstart = cidx + strlen("cma-"); |
| 993 | + size_t dend = dstart; |
| 994 | + while (dend < line.size() && isdigit(static_cast<unsigned char>(line[dend]))) { |
| 995 | + dend++; |
| 996 | + } |
| 997 | + if (dend == dstart) { |
| 998 | + // "cma-size" (a raw byte count) or some other spelling -- leave it be. |
| 999 | + return false; |
| 1000 | + } |
| 1001 | + int current = static_cast<int>(strtol(line.c_str() + dstart, nullptr, 10)); |
| 1002 | + if (current <= CMA_MAX_UNDER_1GB) { |
| 1003 | + return false; |
| 1004 | + } |
| 1005 | + line.replace(cidx, dend - cidx, "cma-" + std::to_string(CMA_MAX_UNDER_1GB)); |
| 1006 | + printf("FPP - %ldkB board: capping vc4 CMA at %dMB (was %dMB)\n", |
| 1007 | + memKB, CMA_MAX_UNDER_1GB, current); |
| 1008 | + content.replace(vidx, eol - vidx, line); |
| 1009 | + return true; |
| 1010 | +} |
| 1011 | +// --- END capVC4CMALine --- |
| 1012 | + |
| 1013 | +static void capVC4CMAForBoard(std::string& content, bool& changed) { |
| 1014 | + std::string meminfo = GetFileContents("/proc/meminfo"); |
| 1015 | + size_t midx = meminfo.find("MemTotal:"); |
| 1016 | + if (midx == std::string::npos) { |
| 1017 | + return; |
| 1018 | + } |
| 1019 | + long memKB = strtol(meminfo.c_str() + midx + strlen("MemTotal:"), nullptr, 10); |
| 1020 | + if (capVC4CMALine(content, memKB)) { |
| 1021 | + PutFileContents("/boot/firmware/config.txt", content); |
| 1022 | + changed = true; |
| 1023 | + } |
| 1024 | +} |
| 1025 | +#endif |
| 1026 | + |
938 | 1027 | void setupChannelOutputs() { |
939 | 1028 | #ifdef PLATFORM_PI |
940 | 1029 | bool hasDPI = false; |
@@ -1008,6 +1097,7 @@ void setupChannelOutputs() { |
1008 | 1097 | exec("modprobe snd_usb_audio"); |
1009 | 1098 | } |
1010 | 1099 | std::string content = GetFileContents("/boot/firmware/config.txt"); |
| 1100 | + capVC4CMAForBoard(content, changed); |
1011 | 1101 | size_t idx = content.find("dtoverlay=vc4-kms-dpi-fpp"); |
1012 | 1102 | std::string origLine = ""; |
1013 | 1103 | if (idx != std::string::npos) { |
|
0 commit comments