(feat)hugepage: add v10 CMA reservoir management#37
Open
HuJK wants to merge 5 commits into
Open
Conversation
BigfootACA
requested changes
Jul 10, 2026
Comment on lines
+743
to
+752
| @NonNull | ||
| private static String join(@NonNull String... parts) { | ||
| var sb = new StringBuilder(); | ||
| for (var p : parts) { | ||
| if (p.isEmpty()) continue; | ||
| if (sb.length() > 0) sb.append(' '); | ||
| sb.append(p); | ||
| } | ||
| return sb.toString(); | ||
| } |
Comment on lines
+633
to
+634
| var r = run("chmod 755 %s && timeout %d %s %d", | ||
| escapedString(bin), timeoutSec, escapedString(bin), floorMb); |
Comment on lines
201
to
208
| private Snapshot(boolean installed, boolean loaded, boolean statsOk, boolean bootEnabled, | ||
| long targetIdeal, long built, long free, long lent, long deficit, | ||
| boolean acquiring, int acquireMode, boolean hasPoolWant, | ||
| boolean softDisabled, @NonNull String state, @NonNull String totalServed, | ||
| boolean softDisabled, boolean hasCma, long wantWithCma, | ||
| long cmaPool, long availCmaAble, int cmaPbOrder, | ||
| @NonNull String state, @NonNull String totalServed, | ||
| @NonNull String totalRefilled, @NonNull String activeVms, | ||
| @NonNull String acquireStopReason) { |
Member
There was a problem hiding this comment.
参数过多,请转为空构造器然后逐个赋值。如:
[...]
var s = new Snapshot();
s.installed = installed;
s.loaded = false;
[...]默认参数等可以写到变量声明。如:
[...]
final long deficit = 0;
@NonNull final String state = "-";
[...]
Comment on lines
285
to
311
| boolean hasCma = s.containsKey("pool_want_with_cma"); | ||
| long wantWithCma = getLong(s, "pool_want_with_cma", 0); | ||
| long cmaPool = getLong(s, "pool_cma", 0); | ||
| long availCmaAble = getLong(s, "pool_avail_cma_able", -1); | ||
| int cmaPbOrder = (int) getLong(s, "cma_pb_order", -1); | ||
| // What acquire still has to do, mirroring the module's own "already at | ||
| // target" test (acquire_set): it runs when the POOL is short | ||
| // (avail + served < pool_want) OR the RESERVOIR is short | ||
| // (avail + served + pool_cma < pool_want_with_cma). Those are separate | ||
| // shortfalls: raising pool_want while the reservoir already covers the | ||
| // total leaves no total deficit, yet acquire must still stage pages in | ||
| // from the reservoir to fill the pool. Reporting only the total would | ||
| // grey out the acquire buttons exactly then. | ||
| long poolDeficit = Math.max(0, want - free - lent); | ||
| long reservoirDeficit = wantWithCma > 0 | ||
| ? Math.max(0, wantWithCma - free - lent - cmaPool) : 0; | ||
| long deficit = Math.max(poolDeficit, reservoirDeficit); | ||
| boolean acquiring = "1".equals(s.get("acquire_active")); | ||
| int mode = (int) getLong(s, "acquire_mode", -1); | ||
| boolean softDisabled = hasWant && rawWant <= 1; | ||
| return new Snapshot(installed, true, statsOk, bootEnabled, | ||
| want, built, free, lent, deficit, acquiring, mode, hasWant, softDisabled, | ||
| hasCma, wantWithCma, cmaPool, availCmaAble, cmaPbOrder, | ||
| s.getOrDefault("state", "-"), s.getOrDefault("total_served", "-"), | ||
| s.getOrDefault("total_refilled", "-"), s.getOrDefault("active_vms", "-"), | ||
| s.getOrDefault("acquire_stop_reason", "-")); | ||
| } |
Member
There was a problem hiding this comment.
这个可以放一个独立构造器将s传入Snapshot处理
private Snapshot(Map<String, String> s) {
[...]
}
Comment on lines
+123
to
+151
| <cn.classfun.droidvm.ui.widgets.row.TextInputRowWidget | ||
| android:id="@+id/input_pool_size" | ||
| android:layout_width="0dp" | ||
| android:layout_height="wrap_content" | ||
| android:layout_weight="1" | ||
| android:hint="@string/hugepage_pool_size" | ||
| android:icon="@drawable/ic_resize" | ||
| android:inputType="number" | ||
| app:ti_max="16GiB" | ||
| app:ti_min="0MiB" | ||
| app:ti_mode="size" | ||
| app:ti_precision="2MiB" | ||
| app:ti_unit="GiB" /> | ||
|
|
||
| <cn.classfun.droidvm.ui.widgets.row.TextInputRowWidget | ||
| android:id="@+id/input_cma_size" | ||
| android:layout_width="0dp" | ||
| android:layout_height="wrap_content" | ||
| android:layout_weight="1" | ||
| android:hint="@string/hugepage_cma_pool_size" | ||
| android:inputType="number" | ||
| android:visibility="gone" | ||
| app:ti_max="24GiB" | ||
| app:ti_min="0MiB" | ||
| app:ti_mode="size" | ||
| app:ti_precision="2MiB" | ||
| app:ti_unit="GiB" | ||
| tools:visibility="visible" /> | ||
| </LinearLayout> |
Comment on lines
+168
to
+176
| public void setStorage( | ||
| @NonNull int[] usedColors, @NonNull float[] usedValues, | ||
| @Nullable String[] usedLabels, | ||
| float avail, @Nullable String availLabel, | ||
| float availNonCma, int availNonCmaColor, | ||
| float cmaFree, @Nullable String cmaFreeLabel, int cmaFreeColor, | ||
| float cmaOther, @Nullable String cmaOtherLabel, int cmaOtherColor, | ||
| int deficitColor, float deficit, @Nullable String deficitLabel, | ||
| float want |
Member
There was a problem hiding this comment.
参数过多,请引入独立的存储结构来传递数据(如class)
When no unit fits exactly (findUnit) or is at least the value (findFloatUnit), fall back to the smallest unit in the *provided* list rather than hardcoding SizeUnit.B, so a restricted picker (e.g. a GiB-only list) never yields a unit outside it. For the full list units[0] is B, so the classic behaviour is unchanged. Split out of the v10 CMA reservoir work as an independent change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARourbDczkCYsKUUMC9EFv
FrameLayout.setEnabled alone left the child MaterialSwitch enabled and directly draggable, so a "disabled" row could still fire its change listener. Override setEnabled to propagate to the switch, and gate the row's click-to-toggle on switchView.isEnabled() so a disabled switch can't be toggled by tapping the row. Split out of the v10 CMA reservoir work as an independent change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARourbDczkCYsKUUMC9EFv
Two new size-mode options:
- ti_unit pins the picker to exactly one unit (e.g. "GiB"): values always
display and are entered in it, no other unit is offered.
- ti_showUnit hides the picker button in narrow layouts while the fixed/
derived unit still applies to typed values; setUnitButtonVisible toggles
it at runtime.
Split out of the v10 CMA reservoir work as an independent change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARourbDczkCYsKUUMC9EFv
Manage the module's CMA reservoir (pool_want_with_cma) from the hugepage
screen, and fix the pieces of the existing UI that the second target broke.
CMA switch + consumability probe:
- A switch above "Enable Module" reads cma_probe_result from settings.prop:
a recorded pass enables directly, anything else offers the probe.
- The probe raises pool_want_with_cma (never lowers an existing bigger total
- that would demolish reservoir the device is lending out), then empties
the pool with a live pool_want=0 so its blocks flip straight into the
reservoir, and lets a v3 acquire top it up as far as it can.
- Coming up short is not a distinct failure: the reservoir is assembled out
of the pool itself, so a fresh boot - unfragmented - simply succeeds.
- pool_want is only ever written live, so an app killed mid-probe cannot
leave the pool soft-disabled past a reboot; every exit restores it.
- A pass pins the pool to 512 MB and keeps the rest as reservoir; a denial or
unreadable result is put to the user. settings.prop writes became a locked
read-modify-write so the probe thread and the pool-size save can't wipe
each other's keys.
Runtime insmod now matches the boot-time one: prefer the module's own load.sh
(single source of truth for the preflight), else reconstruct only v9's
kapi_check ABI guard. insmod args are joined via StringUtils.joinNonEmpty.
Bar and captions: [VMs][available][CMA free][CMA lent][waiting], denominated
by pool_want_with_cma with the reservoir counted as filled. SegmentedBar's
storage bar takes a StorageSpec holder instead of an 18-argument call.
Snapshot parses a refill_stat key=value map in a dedicated constructor, with
field defaults standing in for the not-loaded view.
Acquire gating follows the module's own rule (acquire_set): work exists when
the pool is short OR the reservoir is short. A CMA-era grow kicks v3, not v1.
Pool size is a GiB-only input (ti_unit), split into pool / with-CMA total when
the reservoir is on, two-way linked so the pair keeps
pool_want <= pool_want_with_cma.
VM bar colours are keyed by rank among the live pids rather than by pid.
Co-Authored-By: lateautumn233 <lateautumn233@foxmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARourbDczkCYsKUUMC9EFv
The v11 gh_hugepage_reserve module can open the movable->CMA redirect
directly, so the app no longer needs the balloon consumability probe to
decide whether apps can use the reservoir.
Remove the probe subsystem entirely: runCmaProbe and all of its dialog/worker
plumbing, the balloon runner, the cma_probe_result verdict, and the
reboot-to-finish pending prompt.
New flow when the CMA switch is turned on:
- if moveable_to_cma_vender_already_allowed reads 1, the vendor kernel
already redirects movable->CMA: enable directly, no risk;
otherwise warn, then offer two named actions:
* "Remove CMA Restriction" arms a lever LIVE ONLY (can crash/reboot the
phone). On 6.1 the restrict_cma_redirect flag is side-effect-free so it
is tried first (falling back to the gfp hook); on 6.6/6.12 the same key
also backs cma_has_pcplist(), so the narrower gfp hook is used directly;
* "Module CMA" builds the reservoir without arming any lever - no crash
risk; the reserve still serves VMs via stage-in, and apps consume it
only where the vendor kernel already allows movable->CMA;
- once it is running, offer to save the setting to settings.prop for next
boot. Splitting apply from save is the safety net: a live apply that
crashes leaves nothing persisted, so the next boot comes up clean. The
persisted key (cma_movable_lever) is app-owned; a future load.sh reads it.
Disabling reverts whichever lever was armed (the module no-ops these when the
vendor already redirects) and forgets the saved choice.
Pool-size UI: the pool size and the with-CMA total are now two stacked rows
instead of a cramped side-by-side pair; the with-CMA row is greyed and
non-editable while CMA is off.
A stale cma_probe_result from an older app version is cleared on screen load
so the unchanged boot script doesn't keep the module's CMA side cold.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARourbDczkCYsKUUMC9EFv
BigfootACA
approved these changes
Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
搭配 v10 模組,新增 CMA 管理功能,允許把保留池記憶體借給外部 app 用,需要時 acquire 收回
搭配 v11 模組,移除探測步驟,改由 hook 執行移除 CMA 限制