If you run:
cat /proc/buddyinfo
… you’ll see output like this:
Node 0, zone DMA 649 2370 2207 1116 428 147 81 40 18 14 129
… that last value is our target… it’s known as an ‘order-10 block’. 2^10 * 4 Kb = 4 MB. Each of those 129 blocks above is 4 MB in size.
The output above is the summation of each column of this command:
cat /proc/pagetypeinfo
Linux handles the other values automatically. If a process requests memory and there’s none to be had from the smaller (lower-order) blocks to the left, it’ll split up one of those order-10 4 MB blocks to handle the request. Thus, over time, the number of order-10 blocks dwindles. When it reaches a low point, Linux compacts memory, restoring contiguous 4 MB blocks available to be split up and used again. The problem is, that low point is usually when the router is under heavy load… the most inopportune time to be doing memory compaction.
We’re going to set it up such that the router does memory compaction before that heavy load hits and exhausts the number of order-10 blocks.
In a terminal window connected via ssh to your router (ssh [email protected], input password when prompted), copy-and-paste the entirety of the code below, then hit Enter:
cat << 'EOF' > /etc/memcompact.sh #!/bin/sh # ===================================================================== # USER CONFIGURATION # Run: cat /proc/buddyinfo # You'll see something like this: # Node 0, zone DMA 649 2370 2207 1116 428 147 81 40 18 14 129 # Note the last value. # Run: awk '/MemTotal:/ {print $2}' /proc/meminfo # You'll see something like this: # 1010484 # This router, with 1010484 / 1024 = 986.8 MB of RAM and 129 blocks * 4MB = 516 MB contiguous free memory, is sitting at ~52.29% free contiguous memory. # Set the target percentage of total RAM that should remain unfragmented. # (e.g., TARGET_PERCENT=20 means "trigger compaction if 4MB blocks drop below 20% of total RAM") # In this case, 1010484 / 1024 * 0.20 = 197.36 MB and 49 4MB contiguous free blocks # This allows the router to compact memory before it is forced to do so (at the worst possible time, under heavy load). # ===================================================================== TARGET_PERCENT=20 # Fetch total memory in Kilobytes from the kernel MEM_TOTAL_KB=$(awk '/MemTotal:/ {print $2}' /proc/meminfo) # Calculate the number of 4MB blocks which equals the target percentage. THRESHOLD=$(( (MEM_TOTAL_KB * TARGET_PERCENT / 100) / 4096 )) # Ensure the threshold is at least 2 4MB blocks on memory-constrained hardware [ "$THRESHOLD" -lt 2 ] && THRESHOLD=2 # Fetch the count of 4MB blocks BLOCKS_4MB=$(awk '{print $NF}' /proc/buddyinfo | awk '{s+=$1} END {print s}') # Conditional compaction logic if [ "$BLOCKS_4MB" -lt "$THRESHOLD" ]; then logger -t kernel_mem "Compacting memory. 4MB blocks: $BLOCKS_4MB | Threshold: $THRESHOLD." # Sync to commit dirty pages to storage (aids memory compaction) sync && sleep 5 && sync && sleep 5 # Execute memory compaction sysctl -w vm.compact_memory=1 && sleep 5 BLOCKS_4MB=$(awk '{print $NF}' /proc/buddyinfo | awk '{s+=$1} END {print s}') logger -t kernel_mem "Memory compacted. 4MB blocks: $BLOCKS_4MB | Threshold: $THRESHOLD." else logger -t kernel_mem "Skipping memory compaction. 4MB blocks: $BLOCKS_4MB | Threshold: $THRESHOLD." fi EOF
Set the TARGET_PERCENT to suit your system. For memory-constrained system, it’ll automatically default to a minimum of two 4MB free, contiguous memory blocks.
Make that file executable:
chmod +x /etc/memcompact.sh
Set up a cron job (System >> Scheduled Tasks) to run memory compaction once a day (or whatever schedule you want):
# ┌───────────── minute (0–59) # │ ┌───────────── hour (0–23) # │ │ ┌───────────── day of month (1–31) # │ │ │ ┌───────────── month (1–12 or JAN–DEC) # │ │ │ │ ┌───────────── day of week (0–7 or SUN–SAT, 0 and 7 = Sunday) # │ │ │ │ │ # * * * * * # Memory compaction * 6 * * * /bin/sh -c /etc/memcompact.sh
In your logs, you’ll see entries like:
user.notice: kernel_mem: Skipping memory compaction. 4MB blocks: 129 | Threshold: 49.
Make it impervious to firmware updates:
Put /etc/memcompact.sh in the list at System >> Backup / Flash Firmware >> Configuration tab, then click the Save button.