mirror of
https://github.com/unraid/webgui.git
synced 2026-05-24 23:29:48 -05:00
Add bind to vfio when selections inuse
This commit is contained in:
@@ -151,14 +151,17 @@ function applyCfg() {
|
||||
var string2 = "VFSETTINGS=";
|
||||
var elements = document.getElementById("vfiopci").elements;
|
||||
for (var i = 0, element; element = elements[i++];) {
|
||||
if (element.type === "checkbox" && element.checked === true && element.className.substring(0, 5) === "iommu")
|
||||
string = string + element.value + " ";
|
||||
if (element.type === "checkbox" && element.className.substring(0, 5) === "iommu") {
|
||||
var elvfio = element.checked;
|
||||
if (elvfio == true) var vfio = "1"; else vfio = "0";
|
||||
string = string + element.value + "|" + vfio + " ";
|
||||
}
|
||||
if (element.type === "checkbox" && element.className.substring(0, 7) === "vfiommu") {
|
||||
var pciId = element.id.replace(/^vfvfio/, "");
|
||||
var mac = document.getElementById("vfmac" + pciId).value;
|
||||
var elvfio = document.getElementById("vfvfio" + pciId).checked;
|
||||
if (elvfio == true) var vfio = "1"; else vfio = "0";
|
||||
string2 = string2 + element.value + "|" + vfio + "|" + mac + " ";
|
||||
var elvfvfio = document.getElementById("vfvfio" + pciId).checked;
|
||||
if (elvfvfio == true) var vfvfio = "1"; else vfvfio = "0";
|
||||
string2 = string2 + element.value + "|" + vfvfio + "|" + mac + " ";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,16 @@ function usb_physical_port($usbbusdev) {
|
||||
return($physical_busid);
|
||||
}
|
||||
|
||||
function createIOMMUCheckBox($pciaddress,$vd,$iommu,$line) {
|
||||
global $iommuinuse,$vfio_cfg_devices;
|
||||
if ((strpos($line, 'Host bridge') === false) && (strpos($line, 'PCI bridge') === false)) {
|
||||
echo in_array($iommu, $iommuinuse) ? '<tr hidden><td><input type="checkbox" value="" title="'._('In use by Unraid').'" disabled ' : '<tr hidden><td><input type="checkbox" hidden class="iommu'.$iommu.'" value="'.$pciaddress."|".$vd.'" ';
|
||||
// check config file for two formats: <Domain:Bus:Device.Function>|<Vendor:Device> or just <Domain:Bus:Device.Function>
|
||||
echo (in_array($pciaddress."|".$vd, $vfio_cfg_devices) || in_array($pciaddress, $vfio_cfg_devices)) ? " checked>" : ">";
|
||||
echo "</td></tr>";
|
||||
}
|
||||
}
|
||||
|
||||
switch ($_POST['table']) {
|
||||
case 't1':
|
||||
$sriov = json_decode(getSriovInfoJson(true),true);
|
||||
@@ -202,23 +212,39 @@ case 't1':
|
||||
$pciidcheck = array_key_first($group);
|
||||
if (in_array($pciidcheck,$sriovvfs)) continue;
|
||||
# Filter devices.
|
||||
if ($showsriov && !array_key_exists($pciidcheck,$sriov)) continue;
|
||||
if ($showsriov && !in_array(substr($sriov[$pciidcheck]['class_id'],0,4),$allowedPCIClass)) continue;
|
||||
|
||||
$iommu = $key;
|
||||
$iommushow = true;
|
||||
foreach ($group as $pciaddress => $line) {
|
||||
if (!$line) continue;
|
||||
[$class, $class_id, $name] = getPciClassNameAndId($pciaddress);
|
||||
$class_prefix = substr(strtolower((string)$class_id), 0, 4);
|
||||
if (in_array($class_prefix, $filter, true)) continue;
|
||||
|
||||
# Do filter checks, but if filtered still create IOMMU check box.
|
||||
$line = preg_replace("/^\t/","",$line);
|
||||
$vd = trim(explode(" ", $line)[0], "[]");
|
||||
|
||||
if ($showsriov && !array_key_exists($pciidcheck,$sriov)) {
|
||||
createIOMMUCheckBox($pciaddress, $vd, $iommu, $line);
|
||||
break;
|
||||
}
|
||||
if ($showsriov && !in_array(substr($sriov[$pciidcheck]['class_id'],0,4),$allowedPCIClass)) {
|
||||
createIOMMUCheckBox($pciaddress, $vd, $iommu, $line);
|
||||
break;
|
||||
}
|
||||
|
||||
if (in_array($class_prefix, $filter, true)) {
|
||||
createIOMMUCheckBox($pciaddress, $vd, $iommu, $line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($iommushow) {
|
||||
if (isset($spacer)) echo "<tr><td colspan='2' class='thin'></td>"; else $spacer = true;
|
||||
echo "</tr><tr><td>IOMMU group $key:</td><td>";
|
||||
$iommushow = false;
|
||||
$append = true;
|
||||
}
|
||||
$line = preg_replace("/^\t/","",$line);
|
||||
$vd = trim(explode(" ", $line)[0], "[]");
|
||||
|
||||
$removed = $line[0]=='R' ? true : false;
|
||||
if ($removed) $line=preg_replace('/R/', '', $line, 1);
|
||||
if (preg_match($BDF_REGEX, $pciaddress)) {
|
||||
@@ -432,7 +458,6 @@ case 't1':
|
||||
echo '<input id="viewlog" type="button" value="'._('View VFIO-PCI Log').'" onclick="openTerminal(\'log\',\'vfio-pci\',\'vfio-pci\')">';
|
||||
}
|
||||
if ($ackparm == "") $ackdisable =" disabled "; else $ackdisable = "";
|
||||
if ($showall == "yes") $applyhidden = ""; else $applyhidden = " hidden ";
|
||||
echo '<input id="applycfg" type="submit"'.$applyhidden.' disabled value="'._('Bind selected to VFIO at Boot').'" onclick="applyCfg();" '.(isset($noiommu) ? "style=\"display:none\"" : "").'>';
|
||||
echo '<span id="warning"></span>';
|
||||
echo '<input id="applypci" type="submit"'.$ackdisable.' value="'._('Acknowledge all PCI changes').'" onclick="ackPCI(\''.htmlentities($ackparm).'\',\'all\')" >';
|
||||
@@ -440,10 +465,6 @@ case 't1':
|
||||
echo <<<EOT
|
||||
<script>
|
||||
$("#t1 input[type='checkbox']").change(function() {
|
||||
// Only allow mass-changing when showall is ON
|
||||
if (!$(".showall").is(":checked")) {
|
||||
return; // showall = filtered → do nothing
|
||||
}
|
||||
var matches = document.querySelectorAll("." + this.className);
|
||||
for (var i=0, len=matches.length|0; i<len; i=i+1|0) {
|
||||
matches[i].checked = this.checked ? true : false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
* Copyright 2012-2023, Bergware International.
|
||||
/* Copyright 2005-2025, Lime Technology
|
||||
* Copyright 2012-2025, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
@@ -15,126 +15,152 @@ $docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
require_once "$docroot/webGui/include/Secure.php";
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
|
||||
function stripVFPrefix($str)
|
||||
{
|
||||
return preg_replace('/^VFSETTINGS=/','', trim($str));
|
||||
/* ============================================================
|
||||
* VFIO CONFIG
|
||||
* Format: BIND=pci|vendor pci|vendor ...
|
||||
* Input : BIND=pci|vendor|1 pci|vendor|0 ...
|
||||
* ============================================================ */
|
||||
|
||||
function parseVFIO($str) {
|
||||
if (!preg_match('/^BIND=(.*)$/', trim($str), $m)) return [];
|
||||
$out = [];
|
||||
foreach (preg_split('/\s+/', trim($m[1])) as $e) {
|
||||
if ($e === '') continue;
|
||||
[$pci, $vd] = array_pad(explode('|', $e, 2), 2, '');
|
||||
if ($pci && $vd) $out[$pci] = $vd;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
function parseVF($str)
|
||||
{
|
||||
$str = stripVFPrefix($str);
|
||||
if ($str === '') return [];
|
||||
function parseVFIOInput($str) {
|
||||
if (!preg_match('/^BIND=(.*)$/', trim($str), $m)) return [];
|
||||
$out = [];
|
||||
foreach (preg_split('/\s+/', trim($m[1])) as $e) {
|
||||
if ($e === '') continue;
|
||||
[$pci, $vd, $req] = array_pad(explode('|', $e, 3), 3, '1');
|
||||
if ($pci && $vd && ($req === '0' || $req === '1')) {
|
||||
$out[$pci] = [$vd, $req];
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
$blocks = preg_split('/\s+/', trim($str));
|
||||
$result = [];
|
||||
function updateVFIO($input, $saved) {
|
||||
$existing = parseVFIO($saved);
|
||||
$requested = parseVFIOInput($input);
|
||||
|
||||
foreach ($blocks as $block) {
|
||||
if ($block === '') continue;
|
||||
|
||||
$parts = explode('|', $block);
|
||||
|
||||
// Normalize fields: pci|vd|fn|mac
|
||||
$pci = $parts[0] ?? '';
|
||||
$vd = $parts[1] ?? '';
|
||||
$fn = $parts[2] ?? '';
|
||||
$mac = $parts[3] ?? '';
|
||||
|
||||
// Unique key: pci + vendor:device
|
||||
$key = $pci . '|' . $vd;
|
||||
$result[$key] = [$fn, $mac];
|
||||
foreach ($requested as $pci => [$vd, $req]) {
|
||||
if ($req === '1') {
|
||||
$existing[$pci] = $vd;
|
||||
} else {
|
||||
unset($existing[$pci]);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
if (empty($existing)) return '';
|
||||
|
||||
ksort($existing, SORT_NATURAL);
|
||||
$out = [];
|
||||
foreach ($existing as $pci => $vd) $out[] = "$pci|$vd";
|
||||
|
||||
return 'BIND=' . implode(' ', $out);
|
||||
}
|
||||
|
||||
function isValidVF($fields)
|
||||
{
|
||||
list($fn, $mac) = $fields;
|
||||
/* ============================================================
|
||||
* SR-IOV CONFIG
|
||||
* Format: VFSETTINGS=pci|vd|fn|mac ...
|
||||
* ============================================================ */
|
||||
|
||||
function stripVFPrefix($str) {
|
||||
return preg_replace('/^VFSETTINGS=/', '', trim($str));
|
||||
}
|
||||
|
||||
function parseVF($str) {
|
||||
$str = stripVFPrefix($str);
|
||||
if ($str === '') return [];
|
||||
$out = [];
|
||||
foreach (preg_split('/\s+/', $str) as $b) {
|
||||
if ($b === '') continue;
|
||||
[$pci, $vd, $fn, $mac] = array_pad(explode('|', $b), 4, '');
|
||||
if ($pci && $vd) $out["$pci|$vd"] = [$fn, $mac];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
function isValidVF($fields) {
|
||||
[$fn, $mac] = $fields;
|
||||
$mac = strtolower(trim($mac));
|
||||
|
||||
// Common empty MAC
|
||||
$isZeroMac = ($mac === '' || $mac === '00:00:00:00:00:00');
|
||||
|
||||
// Rule fixes:
|
||||
// - fn=0 is allowed even if empty MAC
|
||||
// - fn=1 must always be accepted
|
||||
// - fn>1 always accepted
|
||||
if ($fn === '0') return true;
|
||||
if ($fn === '1') return true;
|
||||
if ($fn === '0' || $fn === '1') return true;
|
||||
if (intval($fn) > 1) return true;
|
||||
|
||||
// fallback: require some MAC
|
||||
return !$isZeroMac;
|
||||
return ($mac !== '' && $mac !== '00:00:00:00:00:00');
|
||||
}
|
||||
|
||||
function updateVFSettings($input, $saved)
|
||||
{
|
||||
function updateVFSettings($input, $saved) {
|
||||
$inputParsed = parseVF($input);
|
||||
$savedParsed = parseVF($saved);
|
||||
|
||||
$updated = [];
|
||||
|
||||
// Update existing entries
|
||||
foreach ($savedParsed as $key => $oldFields) {
|
||||
if (isset($inputParsed[$key]) && isValidVF($inputParsed[$key])) {
|
||||
$updated[$key] = $inputParsed[$key];
|
||||
}
|
||||
}
|
||||
|
||||
// Add new entries not in saved
|
||||
foreach ($inputParsed as $key => $fields) {
|
||||
if (!isset($savedParsed[$key]) && isValidVF($fields)) {
|
||||
$updated[$key] = $fields;
|
||||
if (isValidVF($fields)) {
|
||||
$savedParsed[$key] = $fields;
|
||||
}
|
||||
}
|
||||
|
||||
// Reassemble output
|
||||
$result = [];
|
||||
if (empty($savedParsed)) return '';
|
||||
|
||||
foreach ($updated as $key => $fields) {
|
||||
list($pci, $vd) = explode('|', $key);
|
||||
list($fn, $mac) = $fields;
|
||||
|
||||
// Normalize MAC for fn=1 if empty
|
||||
ksort($savedParsed, SORT_NATURAL);
|
||||
$out = [];
|
||||
foreach ($savedParsed as $key => [$fn, $mac]) {
|
||||
[$pci, $vd] = explode('|', $key);
|
||||
if ($fn === '1' && trim($mac) === '') {
|
||||
$mac = '00:00:00:00:00:00';
|
||||
}
|
||||
|
||||
$result[] = "$pci|$vd|$fn|$mac";
|
||||
$out[] = "$pci|$vd|$fn|$mac";
|
||||
}
|
||||
|
||||
if (empty($result)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return "VFSETTINGS=" . implode(' ', $result);
|
||||
return 'VFSETTINGS=' . implode(' ', $out);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* FILE PATHS
|
||||
* ============================================================ */
|
||||
|
||||
$vfio = '/boot/config/vfio-pci.cfg';
|
||||
$vfio = '/boot/config/vfio-pci.cfg';
|
||||
$sriovvfs = '/boot/config/sriovvfs.cfg';
|
||||
$reply = 0;
|
||||
|
||||
#Save Normal VFIOs
|
||||
$old = is_file($vfio) ? rtrim(file_get_contents($vfio)) : '';
|
||||
$new = _var($_POST,'cfg');
|
||||
/* ================= VFIO ================= */
|
||||
|
||||
$reply = 0;
|
||||
if ($new != $old) {
|
||||
if ($old) copy($vfio,"$vfio.bak");
|
||||
if ($new) file_put_contents($vfio,$new); else @unlink($vfio);
|
||||
$reply |= 1;
|
||||
$old_vfio = is_file($vfio) ? rtrim(file_get_contents($vfio)) : '';
|
||||
$new_vfio = _var($_POST, 'cfg');
|
||||
$merged_vfio = updateVFIO($new_vfio, $old_vfio);
|
||||
|
||||
if ($merged_vfio !== $old_vfio) {
|
||||
if ($old_vfio) copy($vfio, "$vfio.bak");
|
||||
if ($merged_vfio) file_put_contents($vfio, $merged_vfio);
|
||||
else @unlink($vfio);
|
||||
}
|
||||
|
||||
#Save SRIOV VFS
|
||||
$oldvfcfg = is_file($sriovvfs) ? rtrim(file_get_contents($sriovvfs)) : '';
|
||||
$newvfcfg = _var($_POST,'vfcfg');
|
||||
$oldvfcfg_updated = updateVFSettings($newvfcfg,$oldvfcfg);
|
||||
if (strpos($oldvfcfg_updated,"VFSETTINGS=") !== 0 && $oldvfcfg_updated != "") $oldvfcfg_updated = "VFSETTINGS=".$oldvfcfg_updated;
|
||||
/* Reply bit 1: differs from boot */
|
||||
$boot_vfio = is_file("$vfio.boot") ? rtrim(file_get_contents("$vfio.boot")) : '';
|
||||
if ($merged_vfio !== $boot_vfio) {
|
||||
$reply |= 1;
|
||||
}
|
||||
|
||||
if ($oldvfcfg_updated != $oldvfcfg) {
|
||||
if ($oldvfcfg) copy($sriovvfs,"$sriovvfs.bak");
|
||||
if ($oldvfcfg_updated) file_put_contents($sriovvfs,$oldvfcfg_updated); else @unlink($sriovvfs);
|
||||
$reply |= 2;
|
||||
/* ================= SR-IOV ================= */
|
||||
|
||||
$old_vfcfg = is_file($sriovvfs) ? rtrim(file_get_contents($sriovvfs)) : '';
|
||||
$new_vfcfg = _var($_POST, 'vfcfg');
|
||||
$merged_vfcfg = updateVFSettings($new_vfcfg, $old_vfcfg);
|
||||
|
||||
if ($merged_vfcfg !== $old_vfcfg) {
|
||||
if ($old_vfcfg) copy($sriovvfs, "$sriovvfs.bak");
|
||||
if ($merged_vfcfg) file_put_contents($sriovvfs, $merged_vfcfg);
|
||||
else @unlink($sriovvfs);
|
||||
|
||||
/* Reply bit 2: changed from previous version */
|
||||
$reply |= 2;
|
||||
}
|
||||
|
||||
echo $reply;
|
||||
|
||||
@@ -22,6 +22,7 @@ fi
|
||||
|
||||
# LimeTech - bind selected devices to vfio-pci
|
||||
/usr/local/sbin/vfio-pci 1>/var/log/vfio-pci 2>/var/log/vfio-pci-errors
|
||||
cp /boot/config/vfio-pci.cfg /boot/config/vfio-pci.boot 2>/dev/null
|
||||
|
||||
# Run the kernel module script. This updates the module dependencies and
|
||||
# also supports manually loading kernel modules through rc.modules.local.
|
||||
|
||||
Reference in New Issue
Block a user