mirror of
https://github.com/unraid/webgui.git
synced 2026-05-07 04:41:03 -05:00
WIP Progress updates.
This commit is contained in:
@@ -25,22 +25,15 @@ $(function(){
|
||||
function applyCfg() {
|
||||
var message = "_(System Devices)_: _(A reboot is required to apply changes)_";
|
||||
var string = "BIND=";
|
||||
var vfstring = "BIND=";
|
||||
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.checked === true && element.className.substring(0, 7) === "vfiommu")
|
||||
vfstring = vfstring + element.value + " ";
|
||||
}
|
||||
string = string.trim();
|
||||
if (string === "BIND=") {
|
||||
string = "";
|
||||
}
|
||||
vfstring = vfstring.trim();
|
||||
if (vfstring === "BIND=") {
|
||||
vfstring = "";
|
||||
}
|
||||
$.post( "/plugins/dynamix/include/update.vfio-pci-cfg.php", { cfg: string } )
|
||||
.done(function(d) {
|
||||
if (d==1) {
|
||||
@@ -54,24 +47,24 @@ function applyCfg() {
|
||||
});
|
||||
}
|
||||
|
||||
function saveVFSettingsConfig(pciId,vd) {
|
||||
function saveVFSettingsConfig(pciId,vd,interactive=1) {
|
||||
var message = "_(System Devices)_: _(A reboot is required to apply changes)_";
|
||||
var mac = document.getElementById("vfmac" + pciId).value;
|
||||
var vfio = document.getElementById("vfio" + pciId).value;
|
||||
var vfio = document.getElementById("vfvfio" + pciId).checked;
|
||||
|
||||
$.post( "/plugins/dynamix/include/update.sriov-cfg.php", { type:"sriovmac", pciid: pciId, vd:vd, vfio:vfio, mac:mac } )
|
||||
$.post( "/plugins/dynamix/include/update.sriov-cfg.php", { type:"sriovsettings", pciid: pciId, vd:vd, vfio:vfio, mac:mac } )
|
||||
.done(function(d) {
|
||||
if (d==1) {
|
||||
addRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML = "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
|
||||
swal("MACs Saved", "success");
|
||||
if (interactive == 1) swal("MACs Saved","", "success");
|
||||
} else {
|
||||
removeRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
|
||||
}
|
||||
});
|
||||
}
|
||||
function saveVFsConfig(pciId,vd) {
|
||||
function saveVFsConfig(pciId,vd,interactive=1) {
|
||||
var message = "_(System Devices)_: _(A reboot is required to apply changes)_";
|
||||
var numvfs = document.getElementById("vf" + pciId).value;
|
||||
|
||||
@@ -80,13 +73,14 @@ function saveVFsConfig(pciId,vd) {
|
||||
if (d==1) {
|
||||
addRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML = "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
|
||||
swal("VFs Saved ","success");
|
||||
docum
|
||||
if (interactive == 1) swal("VFs Saved ","","success");
|
||||
} else {
|
||||
removeRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
|
||||
}
|
||||
$('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function generateMAC(pciId) {
|
||||
@@ -148,6 +142,112 @@ function generateMAC(pciId) {
|
||||
});
|
||||
}
|
||||
|
||||
function applyVFSettings(pciId,vd) {
|
||||
saveVFSettingsConfig(pciId,vd,0);
|
||||
var message = "_(System Devices)_: _(A reboot is required to apply changes)_";
|
||||
var mac = document.getElementById("vfmac" + pciId).value;
|
||||
var vfio = document.getElementById("vfvfio" + pciId).checked;
|
||||
|
||||
$.post( "/plugins/dynamix/include/apply.sriov-cfg.php", { type:"sriovsettings", pciid: pciId, vd:vd, vfio:vfio, mac:mac } )
|
||||
.done(function(d) {
|
||||
if (d==1) {
|
||||
addRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML = "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
|
||||
swal("VFs Saved ","","success");
|
||||
} else {
|
||||
removeRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function applyVFsConfig(pciId, vd, vfs) {
|
||||
saveVFsConfig(pciId,vd,0);
|
||||
var message = "_(System Devices)_: _(A reboot is required to apply changes)_";
|
||||
var numvfs = document.getElementById("vf" + pciId).value;
|
||||
|
||||
// Case 1: VFs will be removed
|
||||
if (vfs != 0 && numvfs == 0) {
|
||||
swal({
|
||||
title: "VFs will be removed",
|
||||
text: "Card will reset.",
|
||||
type: "warning",
|
||||
showCancelButton: false,
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
closeOnConfirm: true
|
||||
}, function() {
|
||||
doApply(pciId, vd, numvfs, message);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Case 2: Number of VFs changed
|
||||
if (vfs != numvfs && numvfs != 0) {
|
||||
swal({
|
||||
title: "Number of VFs changed",
|
||||
text: "Will need to remove and re-add VFs.",
|
||||
type: "warning",
|
||||
showCancelButton: false,
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
closeOnConfirm: true
|
||||
}, function() {
|
||||
doApply(pciId, vd, numvfs, message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function doApply(pciId, vd, numvfs, message) {
|
||||
// Show "updating" alert immediately
|
||||
swal({
|
||||
title: "Updating...",
|
||||
text: "Please wait while configuration is applied.",
|
||||
type: "info",
|
||||
showConfirmButton: false,
|
||||
allowOutsideClick: false
|
||||
});
|
||||
|
||||
// Perform the POST
|
||||
$.post("/plugins/dynamix/include/apply.sriov-cfg.php", {
|
||||
type: "sriov",
|
||||
pciid: pciId,
|
||||
vd: vd,
|
||||
numvfs: numvfs
|
||||
})
|
||||
.done(function (d) {
|
||||
// Update UI based on server response
|
||||
if (d == 1) {
|
||||
addRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML =
|
||||
"<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
|
||||
} else {
|
||||
removeRebootNotice(message);
|
||||
document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
|
||||
}
|
||||
|
||||
// Replace the "updating" alert with "done"
|
||||
swal({
|
||||
title: "Update Complete",
|
||||
text: "Configuration successfully applied.",
|
||||
type: "success",
|
||||
timer: 3000, // show for 3 seconds
|
||||
showConfirmButton: false // hide OK button
|
||||
});
|
||||
|
||||
// Reload table
|
||||
$('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
|
||||
})
|
||||
.fail(function () {
|
||||
swal({
|
||||
title: "Error",
|
||||
text: "Failed to apply configuration.",
|
||||
type: "error",
|
||||
showConfirmButton: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function formatFullInput(input) {
|
||||
return input
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
<?PHP
|
||||
/* 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,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
|
||||
#$allowedPCIClass = ['0x02','0x03'];
|
||||
$allowedPCIClass = ['0x02'];
|
||||
|
||||
/**
|
||||
* Enumerate SR-IOV capable PCI devices (keyed by PCI address).
|
||||
*
|
||||
* Example JSON:
|
||||
* {
|
||||
* "0000:03:00.0": {
|
||||
* "class": "Ethernet controller",
|
||||
* "class_id": "0x0200",
|
||||
* "name": "Intel Corporation X710 for 10GbE SFP+",
|
||||
* "driver": "i40e",
|
||||
* "module": "i40e",
|
||||
* "vf_param": "max_vfs",
|
||||
* "total_vfs": 64,
|
||||
* "num_vfs": 8,
|
||||
* "vfs": [
|
||||
* {"pci": "0000:03:10.0", "iface": "enp3s0f0v0", "mac": "52:54:00:aa:00:01"}
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
function getSriovInfoJson(bool $includeVfDetails = true): string {
|
||||
$results = [];
|
||||
$paths = glob('/sys/bus/pci/devices/*/sriov_totalvfs') ?: [];
|
||||
|
||||
foreach ($paths as $totalvfFile) {
|
||||
$devdir = dirname($totalvfFile);
|
||||
$pci = basename($devdir);
|
||||
|
||||
$total_vfs = (int) @file_get_contents($totalvfFile);
|
||||
$num_vfs = (int) @file_get_contents("$devdir/sriov_numvfs");
|
||||
|
||||
// Driver/module detection
|
||||
$driver = $module = $vf_param = null;
|
||||
$driver_link = "$devdir/driver";
|
||||
if (is_link($driver_link)) {
|
||||
$driver = basename(readlink($driver_link));
|
||||
$module_link = "$driver_link/module";
|
||||
$module = is_link($module_link) ? basename(readlink($module_link)) : $driver;
|
||||
$vf_param = detectVfParam($driver);
|
||||
}
|
||||
|
||||
// Device class + numeric class + name
|
||||
[$class, $class_id, $name] = getPciClassNameAndId($pci);
|
||||
|
||||
// Virtual functions
|
||||
$vfs = [];
|
||||
foreach (glob("$devdir/virtfn*") as $vf) {
|
||||
if (!is_link($vf)) continue;
|
||||
$vf_pci = basename(readlink($vf));
|
||||
$vf_entry = ['pci' => $vf_pci];
|
||||
|
||||
if ($includeVfDetails) {
|
||||
// Network interface info
|
||||
$net = glob("/sys/bus/pci/devices/{$vf_pci}/net/*");
|
||||
if ($net && isset($net[0])) {
|
||||
$iface = basename($net[0]);
|
||||
$vf_entry['iface'] = $iface;
|
||||
$macFile = "/sys/class/net/{$iface}/address";
|
||||
if (is_readable($macFile)) {
|
||||
$vf_entry['mac'] = trim(file_get_contents($macFile));
|
||||
}
|
||||
}
|
||||
|
||||
// IOMMU group
|
||||
$iommu_link = "/sys/bus/pci/devices/{$vf_pci}/iommu_group";
|
||||
if (is_link($iommu_link)) {
|
||||
$vf_entry['iommu_group'] = basename(readlink($iommu_link));
|
||||
} else {
|
||||
$vf_entry['iommu_group'] = null;
|
||||
}
|
||||
|
||||
// --- Current driver ---
|
||||
$driver_link = "/sys/bus/pci/devices/{$vf_pci}/driver";
|
||||
if (is_link($driver_link)) {
|
||||
$vf_entry['driver'] = basename(readlink($driver_link));
|
||||
} else {
|
||||
$vf_entry['driver'] = null; // no driver bound
|
||||
}
|
||||
}
|
||||
$vfs[] = $vf_entry;
|
||||
}
|
||||
|
||||
$results[$pci] = [
|
||||
'class' => $class,
|
||||
'class_id' => $class_id,
|
||||
'name' => $name,
|
||||
'driver' => $driver,
|
||||
'module' => $module,
|
||||
'vf_param' => $vf_param,
|
||||
'total_vfs' => $total_vfs,
|
||||
'num_vfs' => $num_vfs,
|
||||
'vfs' => $vfs
|
||||
];
|
||||
}
|
||||
|
||||
ksort($results, SORT_NATURAL);
|
||||
return json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
function detectVfParam(string $driver): ?string {
|
||||
if (!function_exists('shell_exec')) return null;
|
||||
$out = @shell_exec('modinfo ' . escapeshellarg($driver) . ' 2>/dev/null');
|
||||
if (!$out) return null;
|
||||
|
||||
$lines = explode("\n", strtolower($out));
|
||||
$params = [];
|
||||
foreach ($lines as $line) {
|
||||
if (preg_match('/^parm:\s+(\S+)/', $line, $m)) $params[] = $m[1];
|
||||
}
|
||||
|
||||
foreach (['max_vfs', 'num_vfs', 'sriov_numvfs', 'sriov_vfs'] as $key)
|
||||
if (in_array($key, $params, true)) return $key;
|
||||
|
||||
foreach ($params as $p)
|
||||
if (preg_match('/vf/', $p)) return $p;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Robustly get PCI class (text + numeric ID) and device name from lspci/sysfs.
|
||||
*/
|
||||
function getPciClassNameAndId(string $pci): array {
|
||||
$class = 'Unknown';
|
||||
$class_id = null;
|
||||
$name = 'Unknown';
|
||||
|
||||
// Numeric class code from sysfs
|
||||
$classFile = "/sys/bus/pci/devices/{$pci}/class";
|
||||
if (is_readable($classFile)) {
|
||||
$raw = trim(file_get_contents($classFile));
|
||||
$class_id = sprintf("0x%04x", (hexdec($raw) >> 8) & 0xFFFF);
|
||||
}
|
||||
|
||||
// Try lspci -mm for machine-readable info
|
||||
$out = trim(@shell_exec('lspci -mm -s ' . escapeshellarg($pci) . ' 2>/dev/null'));
|
||||
if ($out && preg_match('/"([^"]+)"\s+"([^"]+)"\s+"([^"]+)"/', $out, $m)) {
|
||||
$class = $m[1];
|
||||
$name = trim($m[3]);
|
||||
return [$class, $class_id, $name];
|
||||
}
|
||||
|
||||
// Fallback to regular lspci output
|
||||
$alt = trim(@shell_exec('lspci -s ' . escapeshellarg($pci) . ' 2>/dev/null'));
|
||||
if ($alt && preg_match('/^[\da-fA-F:.]+\s+([^:]+):\s+(.+)/', $alt, $m)) {
|
||||
$class = trim($m[1]);
|
||||
$name = trim($m[2]);
|
||||
}
|
||||
|
||||
return [$class, $class_id, $name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all VFs and group them by IOMMU group
|
||||
* Output: associative array or JSON with keys like "IOMMU group 29"
|
||||
*/
|
||||
function getVfListByIommuGroup(): array {
|
||||
$groups = [];
|
||||
|
||||
foreach (glob('/sys/bus/pci/devices/*/physfn') as $vf_physfn) {
|
||||
$vf_dir = dirname($vf_physfn);
|
||||
$vf_pci = basename($vf_dir);
|
||||
|
||||
$iommu_link = "$vf_dir/iommu_group";
|
||||
if (is_link($iommu_link)) {
|
||||
$iommu_group = basename(readlink($iommu_link));
|
||||
} else {
|
||||
$iommu_group = "unknown";
|
||||
}
|
||||
|
||||
$groups[] = "IOMMU group " . $iommu_group;
|
||||
$groups[] = $vf_pci;
|
||||
}
|
||||
|
||||
ksort($groups, SORT_NATURAL);
|
||||
return $groups;
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// Parse SR-IOV VF counts
|
||||
// ----------------------
|
||||
function parseVFvalues() {
|
||||
$sriov_devices = [];
|
||||
$DBDF_SRIOV_REGEX = '/^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.[[:xdigit:]]\|[[:xdigit:]]{4}:[[:xdigit:]]{4}\|[[:digit:]]+$/';
|
||||
if (is_file("/boot/config/sriov.cfg")) {
|
||||
$file = trim(file_get_contents("/boot/config/sriov.cfg"));
|
||||
$file = preg_replace('/^VFS=/', '', $file); // Remove prefix
|
||||
$entries = preg_split('/\s+/', $file, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if (preg_match($DBDF_SRIOV_REGEX, $entry)) {
|
||||
// Format: <DBDF>|<Vendor:Device>|<VF_count>
|
||||
[$dbdf, $ven_dev, $vf_count] = explode('|', $entry);
|
||||
$sriov_devices[$dbdf] = [
|
||||
'dbdf' => $dbdf,
|
||||
'vendor' => $ven_dev,
|
||||
'vf_count' => (int)$vf_count,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
# $sriov_devices = array_values(array_unique($sriov_devices, SORT_REGULAR));
|
||||
return $sriov_devices;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// Parse SR-IOV VF settings (VFIO+MAC)
|
||||
// ---------------------------------
|
||||
function parseVFSettings() {
|
||||
$sriov_devices_settings = [];
|
||||
$DBDF_SRIOV_SETTINGS_REGEX = '/^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.[[:xdigit:]]\|[[:xdigit:]]{4}:[[:xdigit:]]{4}\|[01]\|([[:xdigit:]]{2}:){5}[[:xdigit:]]{2}$/';
|
||||
if (is_file("/boot/config/sriovvfs.cfg")) {
|
||||
$file = trim(file_get_contents("/boot/config/sriovvfs.cfg"));
|
||||
$file = preg_replace('/^VFSETTINGS=/', '', $file); // Remove prefix
|
||||
$entries = preg_split('/\s+/', $file, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if (preg_match($DBDF_SRIOV_SETTINGS_REGEX, $entry)) {
|
||||
// Format: <DBDF>|<Vendor:Device>|<VFIO_flag>|<MAC>
|
||||
[$dbdf, $ven_dev, $vfio_flag, $mac] = explode('|', $entry);
|
||||
$sriov_devices_settings[$dbdf] = [
|
||||
'dbdf' => $dbdf,
|
||||
'vendor' => $ven_dev,
|
||||
'vfio' => (int)$vfio_flag,
|
||||
'mac' => strtoupper($mac),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
# $sriov_devices_settings = array_values(array_unique($sriov_devices_settings, SORT_REGULAR));
|
||||
return $sriov_devices_settings;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,6 +1,7 @@
|
||||
<?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,
|
||||
@@ -13,6 +14,7 @@
|
||||
<?
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
require_once "$docroot/webGui/include/SriovHelpers.php";
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = 'tools';
|
||||
@@ -37,189 +39,11 @@ function usb_physical_port($usbbusdev) {
|
||||
return($physical_busid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate SR-IOV capable PCI devices (keyed by PCI address).
|
||||
*
|
||||
* Example JSON:
|
||||
* {
|
||||
* "0000:03:00.0": {
|
||||
* "class": "Ethernet controller",
|
||||
* "class_id": "0x0200",
|
||||
* "name": "Intel Corporation X710 for 10GbE SFP+",
|
||||
* "driver": "i40e",
|
||||
* "module": "i40e",
|
||||
* "vf_param": "max_vfs",
|
||||
* "total_vfs": 64,
|
||||
* "num_vfs": 8,
|
||||
* "vfs": [
|
||||
* {"pci": "0000:03:10.0", "iface": "enp3s0f0v0", "mac": "52:54:00:aa:00:01"}
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
function getSriovInfoJson(bool $includeVfDetails = true): string {
|
||||
$results = [];
|
||||
$paths = glob('/sys/bus/pci/devices/*/sriov_totalvfs') ?: [];
|
||||
|
||||
foreach ($paths as $totalvfFile) {
|
||||
$devdir = dirname($totalvfFile);
|
||||
$pci = basename($devdir);
|
||||
|
||||
$total_vfs = (int) @file_get_contents($totalvfFile);
|
||||
$num_vfs = (int) @file_get_contents("$devdir/sriov_numvfs");
|
||||
|
||||
// Driver/module detection
|
||||
$driver = $module = $vf_param = null;
|
||||
$driver_link = "$devdir/driver";
|
||||
if (is_link($driver_link)) {
|
||||
$driver = basename(readlink($driver_link));
|
||||
$module_link = "$driver_link/module";
|
||||
$module = is_link($module_link) ? basename(readlink($module_link)) : $driver;
|
||||
$vf_param = detectVfParam($driver);
|
||||
}
|
||||
|
||||
// Device class + numeric class + name
|
||||
[$class, $class_id, $name] = getPciClassNameAndId($pci);
|
||||
|
||||
// Virtual functions
|
||||
$vfs = [];
|
||||
foreach (glob("$devdir/virtfn*") as $vf) {
|
||||
if (!is_link($vf)) continue;
|
||||
$vf_pci = basename(readlink($vf));
|
||||
$vf_entry = ['pci' => $vf_pci];
|
||||
|
||||
if ($includeVfDetails) {
|
||||
// Network interface info
|
||||
$net = glob("/sys/bus/pci/devices/{$vf_pci}/net/*");
|
||||
if ($net && isset($net[0])) {
|
||||
$iface = basename($net[0]);
|
||||
$vf_entry['iface'] = $iface;
|
||||
$macFile = "/sys/class/net/{$iface}/address";
|
||||
if (is_readable($macFile)) {
|
||||
$vf_entry['mac'] = trim(file_get_contents($macFile));
|
||||
}
|
||||
}
|
||||
|
||||
// IOMMU group
|
||||
$iommu_link = "/sys/bus/pci/devices/{$vf_pci}/iommu_group";
|
||||
if (is_link($iommu_link)) {
|
||||
$vf_entry['iommu_group'] = basename(readlink($iommu_link));
|
||||
} else {
|
||||
$vf_entry['iommu_group'] = null;
|
||||
}
|
||||
|
||||
// --- Current driver ---
|
||||
$driver_link = "/sys/bus/pci/devices/{$vf_pci}/driver";
|
||||
if (is_link($driver_link)) {
|
||||
$vf_entry['driver'] = basename(readlink($driver_link));
|
||||
} else {
|
||||
$vf_entry['driver'] = null; // no driver bound
|
||||
}
|
||||
}
|
||||
$vfs[] = $vf_entry;
|
||||
}
|
||||
|
||||
$results[$pci] = [
|
||||
'class' => $class,
|
||||
'class_id' => $class_id,
|
||||
'name' => $name,
|
||||
'driver' => $driver,
|
||||
'module' => $module,
|
||||
'vf_param' => $vf_param,
|
||||
'total_vfs' => $total_vfs,
|
||||
'num_vfs' => $num_vfs,
|
||||
'vfs' => $vfs
|
||||
];
|
||||
}
|
||||
|
||||
ksort($results, SORT_NATURAL);
|
||||
return json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
function detectVfParam(string $driver): ?string {
|
||||
if (!function_exists('shell_exec')) return null;
|
||||
$out = @shell_exec('modinfo ' . escapeshellarg($driver) . ' 2>/dev/null');
|
||||
if (!$out) return null;
|
||||
|
||||
$lines = explode("\n", strtolower($out));
|
||||
$params = [];
|
||||
foreach ($lines as $line) {
|
||||
if (preg_match('/^parm:\s+(\S+)/', $line, $m)) $params[] = $m[1];
|
||||
}
|
||||
|
||||
foreach (['max_vfs', 'num_vfs', 'sriov_numvfs', 'sriov_vfs'] as $key)
|
||||
if (in_array($key, $params, true)) return $key;
|
||||
|
||||
foreach ($params as $p)
|
||||
if (preg_match('/vf/', $p)) return $p;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Robustly get PCI class (text + numeric ID) and device name from lspci/sysfs.
|
||||
*/
|
||||
function getPciClassNameAndId(string $pci): array {
|
||||
$class = 'Unknown';
|
||||
$class_id = null;
|
||||
$name = 'Unknown';
|
||||
|
||||
// Numeric class code from sysfs
|
||||
$classFile = "/sys/bus/pci/devices/{$pci}/class";
|
||||
if (is_readable($classFile)) {
|
||||
$raw = trim(file_get_contents($classFile));
|
||||
$class_id = sprintf("0x%04x", (hexdec($raw) >> 8) & 0xFFFF);
|
||||
}
|
||||
|
||||
// Try lspci -mm for machine-readable info
|
||||
$out = trim(@shell_exec('lspci -mm -s ' . escapeshellarg($pci) . ' 2>/dev/null'));
|
||||
if ($out && preg_match('/"([^"]+)"\s+"([^"]+)"\s+"([^"]+)"/', $out, $m)) {
|
||||
$class = $m[1];
|
||||
$name = trim($m[3]);
|
||||
return [$class, $class_id, $name];
|
||||
}
|
||||
|
||||
// Fallback to regular lspci output
|
||||
$alt = trim(@shell_exec('lspci -s ' . escapeshellarg($pci) . ' 2>/dev/null'));
|
||||
if ($alt && preg_match('/^[\da-fA-F:.]+\s+([^:]+):\s+(.+)/', $alt, $m)) {
|
||||
$class = trim($m[1]);
|
||||
$name = trim($m[2]);
|
||||
}
|
||||
|
||||
return [$class, $class_id, $name];
|
||||
}
|
||||
|
||||
/* --- CLI Entry --- */
|
||||
$sriov = json_decode(getSriovInfoJson(true),true);
|
||||
|
||||
/**
|
||||
* Enumerate all VFs and group them by IOMMU group
|
||||
* Output: associative array or JSON with keys like "IOMMU group 29"
|
||||
*/
|
||||
function getVfListByIommuGroup(): array {
|
||||
$groups = [];
|
||||
|
||||
foreach (glob('/sys/bus/pci/devices/*/physfn') as $vf_physfn) {
|
||||
$vf_dir = dirname($vf_physfn);
|
||||
$vf_pci = basename($vf_dir);
|
||||
|
||||
$iommu_link = "$vf_dir/iommu_group";
|
||||
if (is_link($iommu_link)) {
|
||||
$iommu_group = basename(readlink($iommu_link));
|
||||
} else {
|
||||
$iommu_group = "unknown";
|
||||
}
|
||||
|
||||
$groups[] = "IOMMU group " . $iommu_group;
|
||||
$groups[] = $vf_pci;
|
||||
}
|
||||
|
||||
ksort($groups, SORT_NATURAL);
|
||||
return $groups;
|
||||
}
|
||||
|
||||
$sriov = json_decode(getSriovInfoJson(true),true);
|
||||
$sriovvfs = getVfListByIommuGroup();
|
||||
$sriov_devices=parseVFvalues();
|
||||
$sriov_devices_settings=parseVFSettings();
|
||||
|
||||
|
||||
switch ($_POST['table']) {
|
||||
@@ -258,60 +82,6 @@ case 't1':
|
||||
$vfio_cfg_devices = array_values(array_unique($vfio_cfg_devices, SORT_STRING));
|
||||
}
|
||||
|
||||
$DBDF_SRIOV_REGEX = '/^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.[[:xdigit:]]\|[[:xdigit:]]{4}:[[:xdigit:]]{4}\|[[:digit:]]+$/';
|
||||
$DBDF_SRIOV_SETTINGS_REGEX = '/^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.[[:xdigit:]]\|[[:xdigit:]]{4}:[[:xdigit:]]{4}\|[01]\|([[:xdigit:]]{2}:){5}[[:xdigit:]]{2}$/';
|
||||
|
||||
// ----------------------
|
||||
// Parse SR-IOV VF counts
|
||||
// ----------------------
|
||||
$sriov_devices = [];
|
||||
|
||||
if (is_file("/boot/config/sriov.cfg")) {
|
||||
$file = trim(file_get_contents("/boot/config/sriov.cfg"));
|
||||
$file = preg_replace('/^VFS=/', '', $file); // Remove prefix
|
||||
$entries = preg_split('/\s+/', $file, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if (preg_match($DBDF_SRIOV_REGEX, $entry)) {
|
||||
// Format: <DBDF>|<Vendor:Device>|<VF_count>
|
||||
[$dbdf, $ven_dev, $vf_count] = explode('|', $entry);
|
||||
$sriov_devices[$dbdf] = [
|
||||
'dbdf' => $dbdf,
|
||||
'vendor' => $ven_dev,
|
||||
'vf_count' => (int)$vf_count,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
# $sriov_devices = array_values(array_unique($sriov_devices, SORT_REGULAR));
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// Parse SR-IOV VF settings (VFIO+MAC)
|
||||
// ---------------------------------
|
||||
$sriov_devices_settings = [];
|
||||
|
||||
if (is_file("/boot/config/sriovvfs.cfg")) {
|
||||
$file = trim(file_get_contents("/boot/config/sriovvfs.cfg"));
|
||||
$file = preg_replace('/^VFSETTINGS=/', '', $file); // Remove prefix
|
||||
$entries = preg_split('/\s+/', $file, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if (preg_match($DBDF_SRIOV_SETTINGS_REGEX, $entry)) {
|
||||
// Format: <DBDF>|<Vendor:Device>|<VFIO_flag>|<MAC>
|
||||
[$dbdf, $ven_dev, $vfio_flag, $mac] = explode('|', $entry);
|
||||
$sriov_devices_settings[$dbdf] = [
|
||||
'dbdf' => $dbdf,
|
||||
'vendor' => $ven_dev,
|
||||
'vfio' => (int)$vfio_flag,
|
||||
'mac' => strtoupper($mac),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
# $sriov_devices_settings = array_values(array_unique($sriov_devices_settings, SORT_REGULAR));
|
||||
}
|
||||
|
||||
$disks = (array)parse_ini_file('state/disks.ini',true);
|
||||
$devicelist = array_column($disks, 'device');
|
||||
$lines = array ();
|
||||
@@ -430,7 +200,6 @@ if (is_file("/boot/config/sriovvfs.cfg")) {
|
||||
echo "SRIOV Available VFs:{$sriov[$pciaddress]['total_vfs']}";
|
||||
$num_vfs= $sriov[$pciaddress]['num_vfs'];
|
||||
|
||||
|
||||
if (isset($sriov_devices[$pciaddress]))
|
||||
$file_numvfs = $sriov_devices[$pciaddress]['vf_count'];
|
||||
else $file_numvfs = 0;
|
||||
@@ -452,7 +221,7 @@ if (is_file("/boot/config/sriovvfs.cfg")) {
|
||||
echo " "._("Current:").$num_vfs;
|
||||
#sprintf(" "._("Current").":%1s",$num_vfs);
|
||||
echo ' <a class="info" href="#" title="'._("Save VFs config").'" onclick="saveVFsConfig(\''.htmlentities($pciaddress).'\',\''.htmlentities($vd).'\'); return false;"><i class="fa fa-save"> </i></a>';
|
||||
echo ' <a class="info" href="#" title="'._("Action VFs update").'" onclick="applyVFsConfig(\''.htmlentities($pciaddress).'\',\''.htmlentities($vd).'\'); return false;"><i title="Apply now" class="fa fa-play"></i></a>';
|
||||
echo ' <a class="info" href="#" title="'._("Action VFs update").'" onclick="applyVFsConfig(\''.htmlentities($pciaddress).'\',\''.htmlentities($vd).'\',\''.htmlentities($num_vfs).'\'); return false;"><i title="Apply now" class="fa fa-play"></i></a>';
|
||||
|
||||
if ($file_numvfs != $num_vfs) echo " <span id='vfnotice".$pciaddress."'><i class=\"fa fa-warning fa-fw orange-text\"></i> ".sprintf(_("Pending action or reboot"));
|
||||
|
||||
@@ -479,9 +248,9 @@ if (is_file("/boot/config/sriovvfs.cfg")) {
|
||||
if (file_exists('/sys/bus/pci/devices/'.$pciaddress.'/reset')) echo "<i class=\"fa fa-retweet grey-orb middle\" title=\"",_('Function Level Reset (FLR) supported'),".\"></i>";
|
||||
echo "</td><td>";
|
||||
if (!$removed) {
|
||||
echo '<input type="checkbox" class="vfiommu'.$vrf['iommu_group'].'" value="'.$pciaddress."|".$vd.'" ';
|
||||
echo '<input type="checkbox" id="vfvfio'.$pciaddress.'" class="vfiommu'.$vrf['iommu_group'].'" 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 (array_key_exists($pciaddress,$sriov_devices_settings) && $sriov_devices_settings[$pciaddress]['vfio'] == 1) ? " checked>" : ">";
|
||||
}
|
||||
} else { echo "</td><td>"; }
|
||||
echo '</td><td title="';
|
||||
@@ -515,7 +284,7 @@ if (is_file("/boot/config/sriovvfs.cfg")) {
|
||||
echo '<label for="mac_address">MAC Address:</label>';
|
||||
echo "<input class='narrow' type=\"text\" name=\"vfmac$pciaddress\" id=\"vfmac$pciaddress\" value=\"$value_attr\" placeholder=\"$placeholder\">";
|
||||
echo '<a class="info" href="#" title="'._("Generate MAC").'" onclick="generateMAC(\''.htmlentities($pciaddress).'\'); return false;"><i class="fa fa-refresh mac_generate"> </i></a>';
|
||||
echo '<a class="info" href="#" title="'._("Save MAC config").'" onclick="saveMACConfig(\''.htmlentities($pciaddress).'\',\''.htmlentities($vd).'\'); return false;"><i class="fa fa-save"> </i></a>';
|
||||
echo '<a class="info" href="#" title="'._("Save MAC config").'" onclick="saveVFSettingsConfig(\''.htmlentities($pciaddress).'\',\''.htmlentities($vd).'\'); return false;"><i class="fa fa-save"> </i></a>';
|
||||
if ($vrf['driver'] == "vfio-pci")
|
||||
echo _("Current").": ";
|
||||
echo $vrf['driver'] == "vfio-pci" ? _("Bound to VFIO") : strtoupper($vrf['mac']);
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?PHP
|
||||
/* Copyright 2025-, Lime Technology
|
||||
* Copyright 2025-, Simon Fairweather.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
|
||||
#VFSETTINGS=0000:04:11.5|8086:1520|0|62:00:04:11:05:01 0000:04:10.5|8086:1520|1|62:00:04:10:05:01
|
||||
#VFS=0000:04:00.1|8086:1521|3 0000:04:00.0|8086:1521|2
|
||||
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
require_once "$docroot/webGui/include/Secure.php";
|
||||
require_once "$docroot/webGui/include/Wrappers.php";
|
||||
|
||||
$sriov = '/boot/config/sriov.cfg';
|
||||
$sriovvfs = '/boot/config/sriovvfs.cfg';
|
||||
|
||||
$type = _var($_POST,'type');
|
||||
$pciid = _var($_POST,'pciid');
|
||||
$vd = _var($_POST,'vd');
|
||||
|
||||
if (isset($pciid) && isset($vd)) {
|
||||
$newelement_check = $pciid.'|'.$vd.'|';
|
||||
|
||||
switch($type) {
|
||||
case "sriov":
|
||||
$old = is_file($sriov) ? rtrim(file_get_contents($sriov)) : '';
|
||||
$newexplode = explode(" ",str_replace("VFS=","",$old));
|
||||
$new = $old;
|
||||
$numvfs= _var($_POST,'numvfs');
|
||||
$currentvfs = _var($_POST,'currentvfs');
|
||||
$newelement_change = $newelement_check.$numfs;
|
||||
$found = false;
|
||||
$filepath = "/sys/bus/pci/devices/$pciid/sriov_numvfs";
|
||||
if ($numvfs == 0) {
|
||||
file_put_contents($filepath,0);
|
||||
echo 1;
|
||||
return;
|
||||
}
|
||||
if ($numvfs != $currentvfs) {
|
||||
file_put_contents($filepath,0);
|
||||
file_put_contents($filepath,$numvfs);
|
||||
# Apply VF changes.
|
||||
# foreach VF.
|
||||
echo 1;
|
||||
return;
|
||||
}
|
||||
file_put_contents($filepath,$numvfs);
|
||||
# Apply VF changes.
|
||||
# foreach VF.
|
||||
echo 1;
|
||||
return;
|
||||
|
||||
#else action numvfs > pf
|
||||
|
||||
#Apply VF settings.
|
||||
|
||||
|
||||
break;
|
||||
case "sriovsettings":
|
||||
$old = is_file($sriovvfs) ? rtrim(file_get_contents($sriovvfs)) : '';
|
||||
$newexplode = explode(" ",str_replace("VFSETTINGS=","",$old));
|
||||
$mac= _var($_POST,'mac');
|
||||
$vfio= _var($_POST,'vfio');
|
||||
if ($vfio == "true") $vfio = 1; else $vfio = 0;
|
||||
$found = false;
|
||||
foreach($newexplode as $key => $newelement) {
|
||||
if (strpos($newelement,$newelement_check) !== false) {
|
||||
$found = true;
|
||||
if($mac == "" && $vfio == 0) {
|
||||
unset($newexplode[$key]) ;
|
||||
break;
|
||||
} else {
|
||||
$newexplode[$key] = $newelement_check.$vfio."|".$mac;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$found) $newexplode[] = $newelement_check.$vfio."|".$mac;
|
||||
$new = "VFSETTINGS=".implode(" ",$newexplode);
|
||||
$file = $sriovvfs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
@@ -58,6 +58,7 @@ if (isset($pciid) && isset($vd)) {
|
||||
$newexplode = explode(" ",str_replace("VFSETTINGS=","",$old));
|
||||
$mac= _var($_POST,'mac');
|
||||
$vfio= _var($_POST,'vfio');
|
||||
if ($vfio == "true") $vfio = 1; else $vfio = 0;
|
||||
$found = false;
|
||||
foreach($newexplode as $key => $newelement) {
|
||||
if (strpos($newelement,$newelement_check) !== false) {
|
||||
|
||||
+1
-8
@@ -67,15 +67,8 @@ VFSETTINGS_LINE=$(grep "^VFSETTINGS=" "$CFG_VFS" | cut -d= -f2- | tr -d '"')
|
||||
for PARAM_VFS in $VFSETTINGS_LINE; do
|
||||
IFS='|' read -r arg1 arg2 arg3 arg4 <<< "$PARAM_VFS"
|
||||
echo "Processing $arg1 $arg2 set Mac to $arg4"
|
||||
/usr/local/sbin/sriov-setmac.sh "$arg1" "$arg2" "$arg4"
|
||||
/usr/local/sbin/sriov-vfsettings.sh "$arg1" "$arg2" "$arg3" "$arg4"
|
||||
echo "---"
|
||||
|
||||
if [[ "$arg3" == "1" ]]; then
|
||||
/usr/local/sbin/vfio-pci-bind.sh "$arg1" "$arg2" \
|
||||
1>>/var/log/vfio-pci \
|
||||
2>>/var/log/vfio-pci-errors
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
|
||||
|
||||
Executable → Regular
Reference in New Issue
Block a user