From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 285121FF142 for ; Fri, 05 Jun 2026 21:41:13 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 8B55E3318A; Fri, 5 Jun 2026 21:40:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=budzowski.pl; s=default; t=1780675559; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=7zkN0dUYcOkZ7Jw975/H7aPe1uLjT1pl8x34r5JxgZc=; b=tfGH4W7rr0IMjUupi6k/5relnE1I7lJPY1mH2ZOsEDUrtT+PBd07ZxHXuPANdqswIu4iKC It4rD2PT7wRz+LOMEswnDheuc0EoiDnF+kOflNYGDcW3fWCDLCu8gqmeLAauDBUbRoCw+t lPNb3scXjQTANNWVUWT1o56sATL4Egb0Y/MtFAHliP0r9VjEUP5e7DAfoFs8tsW/tZ2vTp PkQkgCJnPfQiRQ7nnixhraek8zmhKLJzBXCY5epOTzMjfs+FDuNGj7Wv+6ZKZRIj/eBA8K i8tIAuCChk/R+W0aCtCnefHyymNi2VmwrO7vt2wGTYxaSY8mfb7huMWftELCgQ== Authentication-Results: mail.alfaline.pl; auth=pass smtp.auth=dominik@budzowski.pl smtp.mailfrom=dominik@budzowski.pl From: Dominik Budzowski To: pve-devel@lists.proxmox.com Subject: [PATCH pve-manager v2] ui: qemu: HDEdit: add iothread-vq-mapping support Date: Fri, 5 Jun 2026 18:05:58 +0200 Message-ID: <20260605160558.15783-1-dominik@budzowski.pl> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_PASS -0.1 DMARC pass policy SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record X-MailFrom: dominik@budzowski.pl X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation Message-ID-Hash: HS2X27ULGH2XPCIBRUXRSWB2YLVSWIWQ X-Message-ID-Hash: HS2X27ULGH2XPCIBRUXRSWB2YLVSWIWQ X-Mailman-Approved-At: Fri, 05 Jun 2026 21:40:56 +0200 CC: Dominik Budzowski X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Replace the 'IO thread' checkbox with a proxmoxKVComboBox offering three mutually exclusive modes: - disabled (no iothread, default) - IO thread (classic single iothread) - VQ mapping (pool of N iothreads via iothread-vq-mapping) A 'VQ count' spinner (2-16, default 2) appears only when 'VQ mapping' is selected, keeping the panel compact otherwise. In create/wizard mode the selector auto-defaults to 'IO thread' for virtio and virtio-scsi-single controllers via a ViewModel formula, preserving the previous checkbox behaviour. Resolves: https://bugzilla.proxmox.com/show_bug.cgi?id=6350 Signed-off-by: Dominik Budzowski --- Changes in v2: - Replace two mutually exclusive checkboxes (IO thread + VQ Mapping) with a single proxmoxKVComboBox, following the pattern of Cache/AIO selectors - VQ count field is now hidden (not just disabled) when VQ mapping is not selected, reducing visual clutter - Default VQ count changed from 4 to 2 - Fix label capitalisation: 'IO thread', 'VQ mapping' (lowercase) www/manager6/qemu/HDEdit.js | 58 ++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/www/manager6/qemu/HDEdit.js b/www/manager6/qemu/HDEdit.js index 1bb2bfd..b1c2220 100644 --- a/www/manager6/qemu/HDEdit.js +++ b/www/manager6/qemu/HDEdit.js @@ -19,6 +19,10 @@ Ext.define('PVE.qemu.HDInputPanel', { isVirtIO: false, isSCSISingle: false, }, + formulas: { + defaultIothreadMode: get => + (get('isVirtIO') || (get('isSCSI') && get('isSCSISingle'))) ? 'iothread' : '__default__', + }, }, controller: { @@ -92,7 +96,14 @@ Ext.define('PVE.qemu.HDInputPanel', { PVE.Utils.propertyStringSet(me.drive, values.noreplicate, 'replicate', 'no'); PVE.Utils.propertyStringSet(me.drive, values.discard, 'discard', 'on'); PVE.Utils.propertyStringSet(me.drive, values.ssd, 'ssd', 'on'); - PVE.Utils.propertyStringSet(me.drive, values.iothread, 'iothread', 'on'); + let iothreadCombo = me.down('[name=iothread_mode]'); + let iothreadMode = (iothreadCombo && !iothreadCombo.disabled) ? values.iothread_mode : '__default__'; + PVE.Utils.propertyStringSet(me.drive, iothreadMode === 'iothread', 'iothread', 'on'); + PVE.Utils.propertyStringSet( + me.drive, + iothreadMode === 'vq-mapping' ? values.iothread_vq_mapping : undefined, + 'iothread_vq_mapping', + ); PVE.Utils.propertyStringSet(me.drive, values.readOnly, 'ro', 'on'); PVE.Utils.propertyStringSet(me.drive, values.cache, 'cache'); PVE.Utils.propertyStringSet(me.drive, values.aio, 'aio'); @@ -153,7 +164,14 @@ Ext.define('PVE.qemu.HDInputPanel', { values.cache = drive.cache || '__default__'; values.discard = drive.discard === 'on'; values.ssd = PVE.Parser.parseBoolean(drive.ssd); - values.iothread = PVE.Parser.parseBoolean(drive.iothread); + let iothreadMode = '__default__'; + if (drive.iothread_vq_mapping) { + iothreadMode = 'vq-mapping'; + } else if (PVE.Parser.parseBoolean(drive.iothread)) { + iothreadMode = 'iothread'; + } + values.iothread_mode = iothreadMode; + values.iothread_vq_mapping = drive.iothread_vq_mapping ? parseInt(drive.iothread_vq_mapping, 10) : 2; values.readOnly = PVE.Parser.parseBoolean(drive.ro); values.aio = drive.aio || '__default__'; @@ -318,20 +336,44 @@ Ext.define('PVE.qemu.HDInputPanel', { name: 'discard', }, { - xtype: 'proxmoxcheckbox', - name: 'iothread', - fieldLabel: 'IO thread', - clearOnDisable: true, + xtype: 'proxmoxKVComboBox', + name: 'iothread_mode', + fieldLabel: gettext('IO thread'), + deleteEmpty: false, + value: '__default__', + comboItems: [ + ['__default__', gettext('disabled')], + ['iothread', gettext('IO thread')], + ['vq-mapping', gettext('VQ mapping')], + ], bind: me.insideWizard || me.isCreate ? { disabled: '{!isVirtIO && !isSCSI}', - // Checkbox.setValue handles Arrays in a different way, therefore cast to bool - value: '{!!isVirtIO || (isSCSI && isSCSISingle)}', + value: '{defaultIothreadMode}', } : { disabled: '{!isVirtIO && !isSCSI}', }, + listeners: { + change: function(combo, val) { + let countField = combo.up('window').down('[name=iothread_vq_mapping]'); + if (countField) { + countField.setVisible(val === 'vq-mapping'); + if (val !== 'vq-mapping') countField.setValue(2); + } + }, + }, + }, + { + xtype: 'proxmoxintegerfield', + name: 'iothread_vq_mapping', + fieldLabel: gettext('VQ count'), + minValue: 2, + maxValue: 16, + value: 2, + hidden: true, + allowBlank: false, }, ); -- 2.47.3