fix(templates): bump WinRM quotas + desktop heap for parallel Win builds
Parallel MSBuild fan-out (e.g. ns7zip x86/x64/x86-ansi) over pypsrp
Linux→Win fails with MSBuild MSB6003 / Win32 1816 'Not enough quota'
because two default guest limits are too tight for concurrent native
processes via WinRM:
- Plugin Microsoft.PowerShell MaxMemoryPerShellMB = 150 MB
(pypsrp uses the plugin endpoint, not the Shell endpoint that
Deploy was previously bumping)
- Non-interactive desktop heap SharedSection 3rd field = 768 KB
(Session 0 csrss; CreateProcess fails when 3+ cl.exe spawn)
Pre-migration Win→Win went through Invoke-Command on PSSession with
quotas inherited from the local process, so the limits never bit.
Changes:
- Deploy-WinBuild2025.ps1 / Deploy-WinBuild2022.ps1: also set the
plugin-level MaxMemoryPerShellMB=2048, bump MaxProcessesPerShell
25→100, patch SharedSection 3rd field 768→4096 (reboot needed for
csrss to pick up, applied by post-install shutdown).
- Install-CIToolchain-WinBuild2025/2022.ps1: add Assert-Step for
plugin quota, MaxProcessesPerShell≥100, SharedSection≥4096; fail
hard if any are under threshold.
- docs/WINDOWS-TEMPLATE-SETUP.md: new "WinRM quotas e desktop heap"
section explaining the diagnosis + one-shot patch for legacy
templates; updated Step 3 validation row + troubleshooting entry.
- AGENTS.md: error #13 with symptom, root cause, and pointer to docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -197,7 +197,7 @@ Final Pre-snapshot gate — 7 check cross-cutting + no installer leftover
|
||||
| ----- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Assert | `Get-NetFirewallProfile -Profile Domain/Private/Public → Enabled=False` |
|
||||
| 2 | Assert | GPO DisableAntiSpyware=1 (set da Deploy) |
|
||||
| 3 | Assert | WinRM Running+Automatic, AllowUnencrypted=false (HTTPS-only), Auth/Basic=true, MaxMem≥2048 |
|
||||
| 3 | Assert | WinRM Running+Automatic, AllowUnencrypted=false (HTTPS-only), Auth/Basic=true, Shell+Plugin MaxMem≥2048, MaxProcesses≥100, desktop heap SharedSection 3rd≥4096 |
|
||||
| 4 | Operativo | User exists, enabled, PasswordNeverExpires, member of Administrators |
|
||||
| 4b | Assert | EnableLUA=0, LocalAccountTokenFilterPolicy=1 |
|
||||
| 5 | Operativo | Ogni dir Test-Path -PathType Container |
|
||||
@@ -290,6 +290,55 @@ toolchain (.NET, VS Build Tools, Python).
|
||||
|
||||
---
|
||||
|
||||
## WinRM quotas e desktop heap (build paralleli)
|
||||
|
||||
I template Windows escono di default da Windows con quote dimensionate per uso
|
||||
interattivo. Da Linux/`pypsrp` queste quote si rivelano strette appena il build
|
||||
guest fa fan-out di processi nativi (compilatori, linker) in parallelo. Tre
|
||||
configurazioni che il template **deve** applicare prima dello snapshot
|
||||
`BaseClean` (gestite automaticamente da `Deploy-WinBuild2025.ps1` / `2022`):
|
||||
|
||||
| Setting | Default | Valore template | Motivo |
|
||||
| ---------------------------------------------------------------------------------------- | ------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `WSMan:\localhost\Shell\MaxMemoryPerShellMB` | 150 | 2048 | Limite globale memoria per shell WinRM (winrs) |
|
||||
| `WSMan:\localhost\Plugin\Microsoft.PowerShell\Quotas\MaxMemoryPerShellMB` | 150 | 2048 | **pypsrp da Linux usa l'endpoint plugin Microsoft.PowerShell**, non lo Shell. Senza questo, builds paralleli falliscono con `MSB6003 / Not enough quota` anche se Shell è a 2048 |
|
||||
| `WSMan:\localhost\Shell\MaxProcessesPerShell` | 25 | 100 | MSBuild + cl + link + mt + altri tool si moltiplicano in fan-out parallelo |
|
||||
| `HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems\Windows SharedSection` | `…,768` | `…,4096` | Heap del desktop **non-interattivo** (Session 0). CreateProcess fallisce con `ERROR_NOT_ENOUGH_QUOTA` (1816) quando 3+ cl.exe partono insieme. Richiede **reboot** per essere letto da `csrss.exe`. |
|
||||
|
||||
**Importante**: la modifica `SharedSection` è una scrittura di registry che il
|
||||
kernel legge **solo al boot** (`csrss.exe`). Lo `shutdown /s` finale di
|
||||
`post-install.ps1` fa sì che il prossimo avvio del template (per ulteriore
|
||||
manutenzione, o l'invocazione di `Install-CIToolchain`) parta con il valore
|
||||
corretto. Il `BaseClean` snapshot va preso **dopo** un boot completo per essere
|
||||
certi che il valore sia attivo.
|
||||
|
||||
**Perché pre-migrazione (Win host) funzionava**: il path Windows→Windows usava
|
||||
`Invoke-Command` su `PSSession` con la stessa identità del processo locale (act_runner
|
||||
come LocalSystem), ereditando spesso quote più generose tramite GPO e default
|
||||
del client locale. Il path Linux→Windows (`pypsrp`) attinge ai default puri
|
||||
dell'endpoint plugin Microsoft.PowerShell del guest — se il template non li ha
|
||||
bumpati, sono **150 MB / 25 processi / 768 KB heap**.
|
||||
|
||||
**Validazione**: `Install-CIToolchain-WinBuild2025.ps1` Step 3 controlla tutti e
|
||||
quattro i valori e fallisce hard se anche solo uno è sotto soglia. Per
|
||||
template già esistenti, applicare il blocco PowerShell sotto, reboot, shutdown,
|
||||
nuovo snapshot `BaseClean`:
|
||||
|
||||
```powershell
|
||||
# Una tantum su template legacy
|
||||
Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 2048 -Force
|
||||
Set-Item 'WSMan:\localhost\Plugin\Microsoft.PowerShell\Quotas\MaxMemoryPerShellMB' 2048 -Force
|
||||
Set-Item WSMan:\localhost\Shell\MaxProcessesPerShell 100 -Force
|
||||
$k = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems'
|
||||
$cur = (Get-ItemProperty -Path $k -Name Windows).Windows
|
||||
$new = $cur -replace 'SharedSection=(\d+),(\d+),\d+', 'SharedSection=$1,$2,4096'
|
||||
Set-ItemProperty -Path $k -Name Windows -Value $new
|
||||
Restart-Service WinRM -Force
|
||||
Restart-Computer -Force # necessario per il desktop heap
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Sintomo | Diagnosi / Fix |
|
||||
@@ -301,6 +350,7 @@ toolchain (.NET, VS Build Tools, Python).
|
||||
| `Python version match` fail | `$PythonVersion` ≠ versione installata. Aggiorna param o reinstalla |
|
||||
| Post-setup `MSBuild not found` | VS install fallita silenziosa. Check `C:\CI\vs_log.txt` |
|
||||
| Pre-snapshot Final check fail | Stato VM rotto — non prendere snapshot. Re-run o ricomincia da zero |
|
||||
| MSBuild `error MSB6003: CL.exe could not be run` + `Win32Exception (0x80004005): Not enough quota` su build paralleli | Quote WinRM/desktop-heap non bumpate nel template (vedi §"WinRM quotas e desktop heap" sotto). Tipico quando il build interno fa fan-out (es. 3× cl.exe paralleli) via pypsrp Linux→Win |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user