@@ -21,6 +21,96 @@ $events = explode('|',$var['smEvents'] ?? $numbers);
2121
2222$default = parse_ini_file("$docroot/webGui/default.cfg",true);
2323$reply = '/var/tmp/luks.reply';
24+ $zfs_conf = '/boot/config/modprobe.d/zfs.conf';
25+ $zfs_arc_max = '';
26+ if (is_file($zfs_conf) && preg_match('/^[[:space:]]*options[[:space:]]+zfs(?:[[:space:]]+[^\r\n#]*)?\bzfs_arc_max\s*=\s*([0-9]+)/mi', file_get_contents($zfs_conf), $match)) {
27+ $zfs_arc_max = $match[1];
28+ }
29+
30+ $mem_total_bytes = 0;
31+ // Prefer physical memory from DMI so ARC fractions align to installed RAM size.
32+ $dmi_memory = dmidecode('Memory Device', 17);
33+ if (is_array($dmi_memory) && count($dmi_memory)) {
34+ $sum = 0;
35+ foreach ($dmi_memory as $device) {
36+ $size = trim((string)($device['Size'] ?? ''));
37+ if (!preg_match('/^(\d+)\s*(MIB|GIB|TIB|MB|GB|TB)$/i', $size, $m)) continue;
38+ $value = (int)$m[1];
39+ $unit = strtoupper($m[2]);
40+ if ($unit === 'MB' || $unit === 'MIB') $sum += $value * 1024 * 1024;
41+ elseif ($unit === 'GB' || $unit === 'GIB') $sum += $value * 1024 * 1024 * 1024;
42+ elseif ($unit === 'TB' || $unit === 'TIB') $sum += $value * 1024 * 1024 * 1024 * 1024;
43+ }
44+ if ($sum > 0) $mem_total_bytes = $sum;
45+ }
46+ if ($mem_total_bytes <= 0 && is_file('/proc/meminfo') && preg_match('/^MemTotal:\s+(\d+)\s+kB$/m', file_get_contents('/proc/meminfo'), $match)) {
47+ $mem_total_bytes = (int)$match[1] * 1024;
48+ }
49+
50+ $zfs_arc_mode = 'percent_20';
51+ $zfs_arc_percent_steps = range(10, 90, 10);
52+ $zfs_arc_custom = '';
53+ if ($zfs_arc_max === '0') {
54+ $zfs_arc_mode = 'dynamic';
55+ } elseif (preg_match('/^[0-9]+$/', $zfs_arc_max)) {
56+ $matched_arc_option = false;
57+ if ($mem_total_bytes > 0) {
58+ if ((string)$mem_total_bytes === $zfs_arc_max) {
59+ // Full-memory ARC is treated as unlimited in the UI.
60+ $zfs_arc_mode = 'dynamic';
61+ $matched_arc_option = true;
62+ } else {
63+ foreach ($zfs_arc_percent_steps as $pct) {
64+ if ((string)intdiv($mem_total_bytes * $pct, 100) === $zfs_arc_max) {
65+ $zfs_arc_mode = "percent_$pct";
66+ $matched_arc_option = true;
67+ break;
68+ }
69+ }
70+ }
71+ }
72+ if (!$matched_arc_option) {
73+ $zfs_arc_mode = 'custom';
74+ $zfs_arc_custom = $zfs_arc_max;
75+ }
76+ }
77+
78+ $zfs_arc_options = [];
79+ foreach ($zfs_arc_percent_steps as $pct) {
80+ $zfs_arc_options[$pct] = $mem_total_bytes > 0 ? (string)intdiv($mem_total_bytes * $pct, 100) : '';
81+ }
82+
83+ $bytes_per_gb = 1024 * 1024 * 1024;
84+ $zfs_arc_custom_limit_bytes = '';
85+ $zfs_arc_custom_limit_gb = '';
86+ if ($mem_total_bytes > 0) {
87+ $zfs_arc_custom_limit = intdiv($mem_total_bytes * 9, 10);
88+ $zfs_arc_custom_limit_gb = (string)ceil(((float)$zfs_arc_custom_limit) / $bytes_per_gb);
89+ $zfs_arc_custom_limit_bytes = (string)((int)$zfs_arc_custom_limit_gb * $bytes_per_gb);
90+ if ($zfs_arc_custom_limit_gb === '') $zfs_arc_custom_limit_gb = '0';
91+ }
92+ $zfs_arc_custom_gb = '';
93+ if ($zfs_arc_custom !== '' && ctype_digit($zfs_arc_custom)) {
94+ $zfs_arc_custom_gb = rtrim(rtrim(number_format(((float)$zfs_arc_custom) / $bytes_per_gb, 3, '.', ''), '0'), '.');
95+ if ($zfs_arc_custom_gb === '') $zfs_arc_custom_gb = '0';
96+ }
97+
98+ $zfs_arc_options_gb = [];
99+ foreach ($zfs_arc_percent_steps as $pct) {
100+ if ($zfs_arc_options[$pct] !== '') {
101+ $zfs_arc_options_gb[$pct] = (string)round(((float)$zfs_arc_options[$pct]) / $bytes_per_gb);
102+ } else {
103+ $zfs_arc_options_gb[$pct] = '';
104+ }
105+ }
106+
107+ $zfs_arc_mode_value = $zfs_arc_mode;
108+ if (preg_match('/^percent_(\d+)$/', $zfs_arc_mode, $match)) {
109+ $pct = (int)$match[1];
110+ if (in_array($pct, $zfs_arc_percent_steps, true)) {
111+ $zfs_arc_mode_value = $zfs_arc_options[$pct] ?: $zfs_arc_max;
112+ }
113+ }
24114$keyfile = is_file($var['luksKeyfile']);
25115$encrypt = false;
26116foreach ($disks as $disk) if (isset($disk['fsType']) && strncmp($disk['fsType'],'luks:',5)===0) $encrypt = true;
@@ -34,6 +124,8 @@ function base64(str) {
34124
35125function doDispatch(form) {
36126 var fields = {};
127+ var arcValue = '';
128+ var maxCustomBytes = <?=($zfs_arc_custom_limit_bytes !== '' ? json_encode((int)$zfs_arc_custom_limit_bytes) : 'null')?>;
37129<?if ($display['unit']=='F'):?>
38130 form.display_hot.value = form.display_hot.value.celsius();
39131 form.display_max.value = form.display_max.value.celsius();
@@ -45,6 +137,62 @@ function doDispatch(form) {
45137 $(form).find('input[name^="display_"]').each(function(){fields[$(this).attr('name')] = $(this).val(); $(this).prop('disabled',true);});
46138 $(form).find('select[name^="display_"]').each(function(){fields[$(this).attr('name')] = $(this).val(); $(this).prop('disabled',true);});
47139 $.post('/webGui/include/Dispatcher.php',fields);
140+
141+ switch (form.zfs_arc_mode.value) {
142+ case 'dynamic':
143+ arcValue = '0';
144+ break;
145+ case 'custom':
146+ var customGb = parseFloat((form.zfs_arc_max_custom.value || '').trim());
147+ var customBytes = Number.isFinite(customGb) && customGb >= 0 ? Math.round(customGb * 1073741824) : NaN;
148+ if (!(Number.isFinite(customBytes) && customBytes >= 0)) {
149+ swal({
150+ title:"_(Invalid value)_",
151+ text:"_(Custom ARC size cannot exceed available system memory)_",
152+ type:'error',
153+ confirmButtonText:"_(Ok)_"
154+ });
155+ form.zfs_arc_max_custom.focus();
156+ return false;
157+ }
158+ if (maxCustomBytes !== null && Number.isFinite(customBytes) && customBytes > maxCustomBytes) {
159+ swal({
160+ title:"_(Invalid value)_",
161+ text:"_(Custom ARC size cannot exceed available system memory)_",
162+ type:'error',
163+ confirmButtonText:"_(Ok)_"
164+ });
165+ form.zfs_arc_max_custom.focus();
166+ return false;
167+ }
168+ arcValue = customBytes.toString();
169+ break;
170+ default:
171+ arcValue = (form.zfs_arc_mode.value || '').trim();
172+ break;
173+ }
174+
175+ // Keep zfs_arc_max in modprobe.d so it persists across boots.
176+ $.post('/update.php',{
177+ '#file':'/boot/config/modprobe.d/zfs.conf',
178+ '#include':'/webGui/include/update.zfs_arc_max.php',
179+ 'zfs_arc_max':arcValue
180+ });
181+
182+ return true;
183+ }
184+
185+ function updateZfsArcInput(form) {
186+ var custom = $(form).find('input[name="zfs_arc_max_custom"]');
187+ var customWrap = $(form).find('#zfs-arc-custom');
188+ var showCustom = form.zfs_arc_mode.value === 'custom';
189+ if (showCustom) {
190+ custom.prop('disabled', false);
191+ customWrap.show('slow');
192+ } else {
193+ custom.prop('disabled', true);
194+ customWrap.hide('slow');
195+ }
48196}
49197
50198function prepareDiskSettings(form) {
@@ -136,6 +284,9 @@ function checkInput(form) {
136284<?if (is_file($reply)):?>
137285<?[$text,$type] = explode("\0",file_get_contents($reply)); unlink($reply);?>
138286$(function() {
287+ var diskForm = $('form[action="/update.htm"]')[0];
288+ if (diskForm && diskForm.zfs_arc_mode) updateZfsArcInput(diskForm);
289+
139290 swal({
140291 title:"_(Encryption Key Update)_",
141292 text:"<?=$text?>",
@@ -233,6 +384,22 @@ _(Tunable (scheduler))_:
233384
234385:disk_tunable_scheduler_help:
235386
387+ _(Tunable (zfs_arc_max))_:
388+ : <select name="zfs_arc_mode" onchange="updateZfsArcInput(this.form)">
389+ <?=mk_option($zfs_arc_mode_value, "dynamic", _('Unlimited (Dynamic)'))?>
390+ <?foreach ($zfs_arc_percent_steps as $pct):?>
391+ <?=mk_option($zfs_arc_mode_value, $zfs_arc_options[$pct], $pct.'% '._('of the memory').($zfs_arc_options_gb[$pct] !== '' ? ' ('.$zfs_arc_options_gb[$pct].' GB)' : ''))?>
392+ <?endforeach;?>
393+ <?=mk_option($zfs_arc_mode_value, "custom", _('Custom value (GB)'))?>
394+ </select>
395+ <div markdown="1" id="zfs-arc-custom" class="extra" style="display:<?=$zfs_arc_mode=='custom'?'block':'none'?>">
396+ _(Custom value)_ (_(GB)_):
397+ : <input type="number" min="0"<?=($zfs_arc_custom_limit_gb !== '' ? ' max="'.$zfs_arc_custom_limit_gb.'"' : '')?> step="0.01" name="zfs_arc_max_custom" value="<?=$zfs_arc_custom_gb?>" placeholder="_(Enter GB)_" <?=$zfs_arc_mode=='custom'?'':'disabled'?>>
398+ <?=($zfs_arc_custom_limit_gb !== '' ? " <span class='input-instructions'>"._('Maximum').": ".$zfs_arc_custom_limit_gb." GB</span>" : '')?>
399+ </div>
400+
401+ :disk_tunable_zfs_arc_max_help:
402+
236403_(Tunable (md_num_stripes))_:
237404: <input type="number" name="md_num_stripes" maxlength="10" value="<?=$var['md_num_stripes']?>" placeholder="<?=$var['md_num_stripes_default']?>">
238405
0 commit comments