Update WIP

This commit is contained in:
SimonFair
2025-11-12 19:59:07 +00:00
parent 1ae804c1a1
commit 5d5282bca7
8 changed files with 763 additions and 190 deletions
+308 -101
View File
@@ -57,7 +57,14 @@ function saveVFSettingsConfig(pciId,vd,interactive=1) {
if (d==1) {
addRebootNotice(message);
document.getElementById("warning").innerHTML = "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
if (interactive == 1) swal("MACs Saved","", "success");
if (interactive == 1)
swal({
title: "VF Settings",
text: "Configuration saved.",
type: "success",
timer: 3000,
showConfirmButton: false
});
} else {
removeRebootNotice(message);
document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
@@ -73,7 +80,14 @@ function saveVFsConfig(pciId,vd,interactive=1) {
if (d==1) {
addRebootNotice(message);
document.getElementById("warning").innerHTML = "<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
if (interactive == 1) swal("VFs Saved ","","success");
if (interactive == 1)
swal({
title: "VFs",
text: "Configuration saved.",
type: "success",
timer: 3000,
showConfirmButton: false
});
} else {
removeRebootNotice(message);
document.getElementById("warning").innerHTML = "<b>_(No changes)_.</b>";
@@ -138,90 +152,264 @@ function generateMAC(pciId) {
$field.val(mac);
}
swal("MAC Generated", mac, "success");
swal({
title: "MAC",
text: "Generated successfully.",
type: "success",
timer: 3000,
showConfirmButton: false
});
});
}
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 = parseInt(document.getElementById("vf" + pciId).value, 10);
const message = "_(System Devices)_: _(A reboot is required to apply changes)_";
const numvfs = parseInt(document.getElementById("vf" + pciId).value, 10);
vfs = parseInt(vfs, 10);
// Case 1: VFs will be removed
if (vfs !== 0 && numvfs === 0) {
swal({
title: "VFs will be removed",
text: "Card will reset.",
type: "warning",
showCancelButton: true,
confirmButtonText: "OK",
closeOnConfirm: true
}, function(isConfirm) {
if (isConfirm) {
// User clicked OK
sleep(2000);
doApply(pciId, vd, numvfs, message);
} else {
// User clicked Cancel — optionally show feedback
swal("Cancelled", "No changes were made.", "info");
// --- Step 1: Check for active VMs (always returns JSON) ---
$.post("/plugins/dynamix/include/apply.sriov-cfg.php", {
type: "inuse",
pciid: pciId,
pcitype: "PF"
}, null, "json") // automatically parse JSON
.done(function (data) {
console.log("Active check result:", data);
// If device is in use, show VM list and stop here
if (data.inuse === true) {
let vmListText = "";
let vmCount = Array.isArray(data.vms) ? data.vms.length : 0;
if (vmCount > 0) {
vmListText = data.vms.join("\n");
} else {
vmListText = "No VM names returned.";
}
const plural = vmCount === 1 ? "VM is" : "VMs are";
const messageText =
`The following ${plural} currently using this device:\n\n` +
vmListText +
"\n\nPlease stop " +
(vmCount === 1 ? "it" : "them") +
" before applying changes.";
swal({
title: "Active VMs Detected",
text: messageText,
type: "warning",
confirmButtonText: "OK",
closeOnConfirm: true
});
return;
}
});
return;
}
// --- Step 2: Continue only if NOT in use ---
// Case 2: Number of VFs changed
if (vfs !== numvfs && vfs !== 0) {
swal({
title: "Number of VFs changed",
text: "Will need to remove and re-add VFs.",
type: "warning",
showCancelButton: true,
confirmButtonText: "OK",
closeOnConfirm: true
}, function(isConfirm) {
if (isConfirm) {
// User clicked OK
sleep(2000);
doApply(pciId, vd, numvfs, message);
} else {
// User clicked Cancel — optionally show feedback
swal("Cancelled", "No changes were made.", "info");
// Case 1: VFs will be removed
if (vfs !== 0 && numvfs === 0) {
swal({
title: "VFs will be removed",
text: "Card will reset.",
type: "warning",
showCancelButton: true,
confirmButtonText: "OK",
cancelButtonText: "Cancel",
closeOnConfirm: true
}, function(isConfirm) {
if (isConfirm) {
setTimeout(function() {
doVFApply(pciId, vd, numvfs, message);
}, 300);
} else {
swal("Cancelled", "No changes were made.", "info");
}
});
return;
}
return;
// Case 2: Number of VFs changed
if (vfs !== numvfs && vfs !== 0) {
swal({
title: "Number of VFs changed",
text: "Will need to remove and re-add VFs.",
type: "warning",
showCancelButton: true,
confirmButtonText: "OK",
cancelButtonText: "Cancel",
closeOnConfirm: true
}, function(isConfirm) {
if (isConfirm) {
setTimeout(function() {
doVFApply(pciId, vd, numvfs, message);
}, 300);
} else {
swal("Cancelled", "No changes were made.", "info");
}
});
return;
}
// Case 3: All VFs removed
if (vfs !== numvfs && vfs === 0) {
doVFApply(pciId, vd, numvfs, message);
}
})
.fail(function (xhr, status, error) {
console.error("Active check failed:", status, error, xhr.responseText);
swal({
title: "Error",
text: "Could not verify active device state.",
type: "error",
showConfirmButton: true
});
}
if (vfs !== numvfs && vfs === 0) doApply(pciId, vd, numvfs, message);
});
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
function applyVFSettings(pciId, vd, currentvfio, currentmac) {
const message = "_(System Devices)_: _(A reboot is required to apply changes)_";
const mac = document.getElementById("vfmac" + pciId).value;
const vfio = document.getElementById("vfvfio" + pciId).checked;
//vfs = parseInt(vfs, 10);
// --- Step 1: Check for active VMs (always returns JSON) ---
$.post("/plugins/dynamix/include/apply.sriov-cfg.php", {
type: "inuse",
pciid: pciId,
pcitype: "VF"
}, null, "json") // automatically parse JSON
.done(function (data) {
console.log("Active check result:", data);
// If device is in use, show VM list and stop here
if (data.inuse === true) {
let vmListText = "";
let vmCount = Array.isArray(data.vms) ? data.vms.length : 0;
if (vmCount > 0) {
vmListText = data.vms.join("\n");
} else {
vmListText = "No VM names returned.";
}
const plural = vmCount === 1 ? "VM is" : "VMs are";
const messageText =
`The following ${plural} currently using this device:\n\n` +
vmListText +
"\n\nPlease stop " +
(vmCount === 1 ? "it" : "them") +
" before applying changes.";
swal({
title: "Active VMs Detected",
text: messageText,
type: "warning",
confirmButtonText: "OK",
closeOnConfirm: true
});
return;
}
saveVFSettingsConfig(pciId, vd, 0);
doVFSettingApply(pciId, vd, vfio, mac, currentvfio,currentmac,message);
})
.fail(function (xhr, status, error) {
console.error("Active check failed:", status, error, xhr.responseText);
swal({
title: "Error",
text: "Could not verify active device state.",
type: "error",
showConfirmButton: true
});
});
}
function doApply(pciId, vd, numvfs, message) {
// Show "updating" alert immediately
function doVFSettingApply(pciId, vd, vfio, mac, currentvfio, currentmac, message) {
// Show "updating" alert
swal({
title: "Updating...",
text: "Please wait while configuration is applied.",
type: "info",
showConfirmButton: false,
allowOutsideClick: false,
closeOnConfirm: false
});
// Perform the POST
$.post("/plugins/dynamix/include/apply.sriov-cfg.php", {
type: "sriovsettings",
pciid: pciId,
vd: vd,
vfio: vfio,
mac: mac,
currentvfio: currentvfio,
currentmac: currentmac
})
.done(function (response) {
let data;
// Try to parse JSON safely
try {
data = (typeof response === "string") ? JSON.parse(response) : response;
} catch (e) {
swal({
title: "Error",
text: "Invalid JSON response from server.",
type: "error",
showConfirmButton: true
});
return;
}
// Handle structured PHP result
if (data.success) {
addRebootNotice(message);
document.getElementById("warning").innerHTML =
"<b>_(ALERT)_: _(Changes saved)_. _(Reboot to take effect)_.</b>";
saveVFSettingsConfig(pciId, vd, 0);
swal({
title: "Update Complete",
text: data.details ? data.details.join("\n") : "Configuration successfully applied.",
type: "success",
timer: 3000,
showConfirmButton: false
});
} else {
removeRebootNotice(message);
const errorMsg = data.error || "No changes detected or operation failed.";
document.getElementById("warning").innerHTML = "<b>" + errorMsg + "</b>";
swal({
title: "Operation Failed",
text: errorMsg,
type: "error",
showConfirmButton: true
});
}
// Always reload the table after handling
$('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
})
.fail(function (xhr, status, error) {
swal({
title: "Network Error",
text: "Failed to communicate with the server.\n" + (error || status),
type: "error",
showConfirmButton: true
});
});
}
function doVFApply(pciId, vd, numvfs, message) {
// Show "updating" alert
swal({
title: "Updating...",
text: "Please wait while configuration is applied.",
@@ -238,38 +426,57 @@ function doApply(pciId, vd, numvfs, message) {
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>";
.done(function (data) {
// Normalize to JSON object
if (typeof data === "string") {
try { data = JSON.parse(data); }
catch (e) {
data = { success: false, error: "Invalid JSON: " + e.message };
}
}
// Extract a clean message
let msg = "Configuration successfully applied.";
if (data.error) {
msg = "Error: " + data.error;
} else if (data.details) {
if (Array.isArray(data.details)) {
msg = data.details.join("\n");
} else if (typeof data.details === "object") {
msg = Object.entries(data.details)
.map(([k, v]) => `${k}: ${JSON.stringify(v, null, 2)}`)
.join("\n\n");
} else if (typeof data.details === "string") {
msg = data.details;
}
}
// Pick alert type
const ok = data.success === true || data.success === 1;
saveVFsConfig(pciId, vd, 0);
// Show success or failure alert
swal({
title: data.success ? "Update Complete" : "Update Failed",
text: msg,
type: data.success ? "success" : "error",
timer: data.success ? 3000 : null,
showConfirmButton: !data.success
});
// Always reload the table after handling
$('#t1').load('/webGui/include/SysDevs.php', { table: 't1' });
})
.fail(function (xhr, status, error) {
swal({
title: "Network Error",
text: "Failed to communicate with the server.\n" + (error || status),
type: "error",
showConfirmButton: true
});
});
}
// 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
+267 -1
View File
@@ -13,8 +13,28 @@
?>
<?
#$allowedPCIClass = ['0x02','0x03'];
$allowedPCIClass = ['0x02'];
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
/**
* Get available kernel modules for this PCI device based on its modalias
*/
function getModulesFromModalias(string $pci): array {
$modaliasFile = "/sys/bus/pci/devices/{$pci}/modalias";
if (!is_readable($modaliasFile)) return [];
$alias = trim(file_get_contents($modaliasFile));
$cmd = sprintf('modprobe -R %s 2>/dev/null', escapeshellarg($alias));
$out = trim(shell_exec($cmd));
return $out ? preg_split('/\s+/', $out) : [];
}
/**
* Enumerate SR-IOV capable PCI devices (keyed by PCI address).
@@ -102,8 +122,10 @@ function getSriovInfoJson(bool $includeVfDetails = true): string {
} else {
$vf_entry['driver'] = null; // no driver bound
}
// Kernel modules (from modalias)
$vf_entry['modules'] = getModulesFromModalias($vf_pci);
}
$vfs[] = $vf_entry;
$vfs[$vf_pci] = $vf_entry;
}
$results[$pci] = [
@@ -123,6 +145,58 @@ function getSriovInfoJson(bool $includeVfDetails = true): string {
return json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
function rebindVfDriver($vf, $sriov, $target = 'original')
{
$res = ['pci'=>$vf,'success'=>false,'error'=>null,'details'=>[]];
$vf_path = "/sys/bus/pci/devices/$vf";
$physfn = "$vf_path/physfn";
if (!is_link($physfn)) return $res + ['error'=>"Missing physfn link"];
$pf = basename(readlink($physfn));
$vf_info = $sriov[$pf]['vfs'][$vf] ?? null;
if (!$vf_info) return $res + ['error'=>"VF not found in \$sriov for PF $pf"];
$orig_mod = $vf_info['modules'][0] ?? $sriov[$pf]['module'] ?? null;
$curr_drv = $vf_info['driver'] ?? null;
if (!$orig_mod) return $res + ['error'=>"No module info for $vf"];
$drv_override = "$vf_path/driver_override";
// Determine target driver
$new_drv = ($target === 'vfio-pci') ? 'vfio-pci' : $orig_mod;
// Step 1: Unbind current driver
$curr_unbind = "/sys/bus/pci/drivers/$curr_drv/unbind";
if (is_writable($curr_unbind))
@file_put_contents($curr_unbind, $vf);
// Step 2: Load target driver if needed
$target_bind = "/sys/bus/pci/drivers/$new_drv/bind";
if (!file_exists($target_bind))
exec("modprobe $new_drv 2>/dev/null");
// Step 3: Override driver binding
if (is_writable($drv_override))
@file_put_contents($drv_override, "$new_drv");
@file_put_contents("/sys/bus/pci/drivers_probe", $vf);
if (is_writable($drv_override))
@file_put_contents($drv_override, "\n");
// Step 4: Verify binding
$drv_link = "$vf_path/driver";
if (is_link($drv_link)) {
$bound = basename(readlink($drv_link));
if ($bound === $new_drv) {
$res['success'] = true;
$res['details'][] = "Bound to $new_drv";
return $res;
}
return $res + ['error'=>"Bound to $bound instead of $new_drv"];
}
return $res + ['error'=>"No driver link after reprobe"];
}
function detectVfParam(string $driver): ?string {
if (!function_exists('shell_exec')) return null;
$out = @shell_exec('modinfo ' . escapeshellarg($driver) . ' 2>/dev/null');
@@ -245,6 +319,7 @@ function parseVFSettings() {
if (preg_match($DBDF_SRIOV_SETTINGS_REGEX, $entry)) {
// Format: <DBDF>|<Vendor:Device>|<VFIO_flag>|<MAC>
[$dbdf, $ven_dev, $vfio_flag, $mac] = explode('|', $entry);
if ($mac == "00:00:00:00:00:00") $mac = "";
$sriov_devices_settings[$dbdf] = [
'dbdf' => $dbdf,
'vendor' => $ven_dev,
@@ -258,4 +333,195 @@ function parseVFSettings() {
return $sriov_devices_settings;
}
}
/**
* Safely set a MAC address for a VF.
* Automatically detects PF, interface, and VF index.
*
* @param string $vf_pci PCI ID of VF (e.g. 0000:02:00.2)
* @param string $mac MAC address to assign
* @param string|null $rebindDriver Driver to bind after change:
* - null → rebind to original driver
* - 'none' → leave unbound
* - 'vfio-pci' → bind to vfio-pci
*
* @return array Result info (for JSON or logs)
*/
function setVfMacAddress(string $vf_pci, string $mac, ?string $rebindDriver = null): array {
$vf_path = "/sys/bus/pci/devices/{$vf_pci}";
$result = [
'vf_pci' => $vf_pci,
'mac' => $mac,
'pf_pci' => null,
'pf_iface' => null,
'vf_index' => null,
'driver_before' => null,
'driver_after' => null,
'unbind' => false,
'mac_set' => false,
'rebind' => false,
'error' => null
];
if (!is_dir($vf_path)) {
$result['error'] = "VF path not found: $vf_path";
return $result;
}
// --- Find parent PF (Physical Function) ---
$pf_link = "$vf_path/physfn";
if (!is_link($pf_link)) {
$result['error'] = "No PF link for $vf_pci (not an SR-IOV VF?)";
return $result;
}
$pf_pci = basename(readlink($pf_link));
$result['pf_pci'] = $pf_pci;
// --- Detect PF network interface name ---
$pf_net = glob("/sys/bus/pci/devices/{$pf_pci}/net/*");
$pf_iface = ($pf_net && isset($pf_net[0])) ? basename($pf_net[0]) : null;
if (!$pf_iface) {
$result['error'] = "Could not detect PF interface for $pf_pci";
return $result;
}
$result['pf_iface'] = $pf_iface;
// --- Detect VF index ---
$vf_index = getVfIndex($pf_pci, $vf_pci);
if ($vf_index === null) {
$result['error'] = "Could not determine VF index for $vf_pci under $pf_pci";
return $result;
}
$result['vf_index'] = $vf_index;
// --- Detect current driver ---
$driver_link = "$vf_path/driver";
$vf_driver = is_link($driver_link) ? basename(readlink($driver_link)) : null;
$result['driver_before'] = $vf_driver;
// --- Unbind from current driver ---
if ($vf_driver) {
$unbind_path = "/sys/bus/pci/drivers/{$vf_driver}/unbind";
if (is_writable($unbind_path)) {
file_put_contents($unbind_path, $vf_pci);
$result['unbind'] = true;
} else {
$result['error'] = "Cannot unbind VF $vf_pci from $vf_driver (permissions)";
return $result;
}
}
// --- Set MAC ---
if ($mac=="") $mac="00:00:00:00:00:00";
$cmd = sprintf(
'ip link set %s vf %d mac %s 2>&1',
escapeshellarg($pf_iface),
$vf_index,
escapeshellarg($mac)
);
exec($cmd, $output, $ret);
if ($ret === 0) {
$result['mac_set'] = true;
} else {
$result['error'] = "Failed to set MAC: " . implode("; ", $output);
}
// --- Rebind logic ---
if ($rebindDriver !== "none") {
$target_driver = $rebindDriver ?? $vf_driver;
if ($target_driver) {
$bind_path = "/sys/bus/pci/drivers/{$target_driver}/bind";
if (is_writable($bind_path)) {
file_put_contents($bind_path, $vf_pci);
$result['rebind'] = true;
$result['driver_after'] = $target_driver;
} else {
$result['error'] = "Cannot rebind VF $vf_pci to $target_driver (permissions)";
}
}
}
if (!isset($result['error'])) $result['success'] = true;
return $result;
}
/**
* Helper: Determine VF index from PF/VF relationship
*/
function getVfIndex(string $pf_pci, string $vf_pci): ?int {
$pf_path = "/sys/bus/pci/devices/{$pf_pci}";
foreach (glob("$pf_path/virtfn*") as $vf_link) {
if (is_link($vf_link)) {
$target = basename(readlink($vf_link));
if ($target === $vf_pci) {
return (int)preg_replace('/[^0-9]/', '', basename($vf_link));
}
}
}
return null;
}
function build_pci_active_vm_map() {
global $lv;
$pcitovm = [];
$vms = $lv->get_domains();
foreach ($vms as $vm) {
$vmpciids = $lv->domain_get_vm_pciids($vm);
$res = $lv->get_domain_by_name($vm);
$dom = $lv->domain_get_info($res);
$state = $lv->domain_state_translate($dom['state']);
if ($state === 'shutoff') continue;
foreach ($vmpciids as $pciid => $pcidetail) {
$pcitovm["0000:" . $pciid][$vm] = $state;
}
}
return $pcitovm;
}
function is_pci_inuse($pciid, $type) {
$actives = build_pci_active_vm_map();
$sriov = json_decode(getSriovInfoJson(true), true);
$inuse = false;
$vms = [];
switch ($type) {
case "VF":
if (isset($actives[$pciid])) {
$inuse = true;
$vms = array_keys($actives[$pciid]);
}
break;
case "PF":
if (isset($sriov[$pciid])) {
$vfs = $sriov[$pciid]['vfs'] ?? [];
foreach ($vfs as $vf) {
$vf_pci = $vf['pci'];
if (isset($actives[$vf_pci])) {
$inuse = true;
$vms = array_merge($vms, array_keys($actives[$vf_pci]));
}
}
}
break;
}
// Remove duplicate VM names (in case multiple VFs from same VM)
$vms = array_values(array_unique($vms));
// Output consistent JSON structure
$result = [
"inuse" => $inuse,
"vms" => $vms
];
header('Content-Type: application/json');
echo json_encode($result, JSON_PRETTY_PRINT);
exit;
}
?>
+10 -8
View File
@@ -15,13 +15,14 @@
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/SriovHelpers.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
// add translations
$_SERVER['REQUEST_URI'] = 'tools';
require_once "$docroot/webGui/include/Translations.php";
$pci_device_diffs = comparePCIData();
$allowedPCIClass = ['0x02','0x03'];
function usb_physical_port($usbbusdev) {
if (preg_match('/^Bus (?P<bus>\S+) Device (?P<dev>\S+): ID (?P<id>\S+)(?P<name>.*)$/', $usbbusdev, $usbMatch)) {
@@ -137,7 +138,7 @@ case 't1':
$groups[] = "\tR[{$removeddata['device']['vendor_id']}:{$removeddata['device']['device_id']}] ".str_replace("0000:","",$removedpci)." ".trim($removeddata['device']['description'],"\n");
}
$ackparm = "";
# echo "<tr><td>";var_dump($sriov, $sriov_devices_settings);echo"</td></tr>";
#echo "<tr><td>";var_dump( $sriov_devices_settings);echo"</td></tr>";
foreach ($groups as $line) {
if (!$line) continue;
if (in_array($line,$sriovvfs)) continue;
@@ -218,7 +219,7 @@ case 't1':
}
echo '</select>';
echo " "._("Current:").$num_vfs;
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).'\',\''.htmlentities($num_vfs).'\'); return false;"><i title="Apply now" class="fa fa-play"></i></a>';
@@ -278,16 +279,17 @@ case 't1':
} else {
$mac = null;
}
$placeholder = empty($mac) ? 'Undefined dynamic allocation' : '';
$placeholder = empty($mac) ? 'Dynamic allocation' : '';
$value_attr = empty($mac) ? '' : htmlspecialchars($mac, ENT_QUOTES);
echo "<tr><td></td><td></td><td></td><td></td><td>";
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="saveVFSettingsConfig(\''.htmlentities($pciaddress).'\',\''.htmlentities($vd).'\'); return false;"><i class="fa fa-save"> </i></a>';
if ($vrf['driver'] == "vfio-pci")
echo _("Current").": ";
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="saveVFSettingsConfig(\''.htmlentities($pciaddress).'\',\''.htmlentities($vd).'\'); return false;"><i class="fa fa-save"> </i></a>';
echo ' <a class="info" href="#" title="'._("Action VFs update").'" onclick="applyVFSettings(\''.htmlentities($pciaddress).'\',\''.htmlentities($vd).'\',\''.htmlentities($sriov_devices_settings[$pciaddress]['vfio'] == 1 ? 'true':'false').'\',\''.$value_attr.'\'); return false;"><i title="Apply now VFIO and MAC Address" class="fa fa-play"></i></a> ';
if ($vrf['driver'] != "vfio-pci") echo _("Current").": ";
echo $vrf['driver'] == "vfio-pci" ? _("Bound to VFIO") : strtoupper($vrf['mac']);
echo " <span id=vfstatus$pciaddress></span>";
echo "</td></tr>";
}
}
@@ -1,4 +1,4 @@
#!/usr/bin/php
#!/usr/bin/php
<?PHP
function SysDriverslog($m, $type='NOTICE') {
if ($type == 'DEBUG') return;
@@ -18,89 +18,186 @@
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Secure.php";
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
require_once "$docroot/webGui/include/SriovHelpers.php";
function action_settings($pciid) {
$sriov = json_decode(getSriovInfoJson(),true);
$sriov_devices_settings=parseVFSettings();
$vfs = $sriov[$pciid]['vfs'];
file_put_contents('/tmp/vfaction',"");
foreach($vfs as $vf) {
if (array_key_exists($vf['pci'],$sriov_devices_settings)) {
$vfpci = $vf['pci'];
$vfio = $sriov_devices_settings[$vfpci]['vfio'];
$mac = $sriov_devices_settings[$vfpci]['mac'];
$action_cmd = "/usr/local/sbin/sriov-vfsettings.sh ".escapeshellarg($vf['pci'])." ".escapeshellarg($vf['vd'])." ".escapeshellarg($vfio)." ".escapeshellarg($mac);
file_put_contents('/tmp/vfaction',$action_cmd,FILE_APPEND);
$action_result = shell_exec($action_cmd);
function json_response($success, $error = null, $details = [])
{
header('Content-Type: application/json');
#if ($action_result == "error") return $action_result;
// Normalize details
if (is_array($details)) {
if (count($details) === 1) {
// Collapse single-item arrays to a single value
$details = reset($details);
} elseif (empty($details)) {
$details = null;
}
}
}
$response = [
'success' => (bool)$success,
'error' => $error,
'details' => $details
];
echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
exit;
}
$sriov = '/boot/config/sriov.cfg';
$sriovvfs = '/boot/config/sriovvfs.cfg';
$type = _var($_POST,'type');
$pciid = _var($_POST,'pciid');
$vd = _var($_POST,'vd');
function action_settings($pciid)
{
$sriov = json_decode(getSriovInfoJson(), true);
$sriov_devices_settings = parseVFSettings();
$vfs = $sriov[$pciid]['vfs'] ?? [];
if (isset($pciid) && isset($vd)) {
$newelement_check = $pciid.'|'.$vd.'|';
$results = [];
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;
foreach ($vfs as $vf) {
$vfpci = $vf['pci'];
if (!isset($sriov_devices_settings[$vfpci])) continue;
$vfio = $sriov_devices_settings[$vfpci]['vfio'];
$mac = $sriov_devices_settings[$vfpci]['mac'];
// Skip if no action needed
if ($vfio == 0 && $mac == "") continue;
$cmd = "/usr/local/sbin/sriov-vfsettings.sh " .
escapeshellarg($vfpci) . " " .
escapeshellarg($vf['vd']) . " " .
escapeshellarg($vfio) . " " .
escapeshellarg($mac) . " 2>&1"; // capture stderr too
$output = [];
$ret = 0;
exec($cmd, $output, $ret);
// Clean output: remove blank lines and trim whitespace
$output = array_filter(array_map('trim', $output));
file_put_contents("/tmp/vfaction", "$ret\n$cmd\n" . json_encode($output) . "\n");
if ($ret !== 0) {
// Only include relevant lines for error reporting
$results[] = [
'success' => false,
'error' => implode("\n", $output) ?: "Unknown error (exit code $ret)"
];
} else {
// Success: include minimal details or last few lines
$results[] = [
'success' => true,
'details' => 'Applied VF settings'
];
}
}
file_put_contents("/tmp/vfactionres",json_encode($results) . "\n");
return $results;
}
$type = _var($_POST, 'type');
$pciid = _var($_POST, 'pciid');
$vd = _var($_POST, 'vd');
if (!isset($pciid) || !isset($vd)) {
echo json_response(false, "Missing PCI ID or virtual device");
exit;
}
switch ($type) {
// --------------------------------------------------------
// SR-IOV enable/disable & VF count changes
// --------------------------------------------------------
case "sriov":
$numvfs = _var($_POST, 'numvfs');
$currentvfs = _var($_POST, 'currentvfs');
$filepath = "/sys/bus/pci/devices/$pciid/sriov_numvfs";
if ($numvfs == 0) {
file_put_contents($filepath,0);
echo 1;
return;
if (!is_writable($filepath)) {
echo json_response(false, "Cannot modify $filepath");
break;
}
if ($numvfs != $currentvfs) {
file_put_contents($filepath,0);
file_put_contents($filepath,$numvfs);
action_settings($pciid);
echo 1;
return;
}
file_put_contents($filepath,$numvfs);
action_settings($pciid);
echo 1;
return;
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;
try {
if ($numvfs == 0) {
file_put_contents($filepath, 0);
echo json_response(true, null, ["Disabled all VFs"]);
break;
}
}
if ($numvfs != $currentvfs) {
file_put_contents($filepath, 0);
file_put_contents($filepath, $numvfs);
$results = action_settings($pciid);
echo json_response(true, null, ["Changed VF count to $numvfs", $results]);
break;
}
file_put_contents($filepath, $numvfs);
$results = action_settings($pciid);
echo json_response(true, null, ["Reapplied VF settings", $results]);
} catch (Throwable $e) {
echo json_response(false, "Failed to change VF count: " . $e->getMessage());
}
if (!$found) $newexplode[] = $newelement_check.$vfio."|".$mac;
$new = "VFSETTINGS=".implode(" ",$newexplode);
$file = $sriovvfs;
break;
}
break;
// --------------------------------------------------------
// VF driver binding, MAC changes
// --------------------------------------------------------
case "sriovsettings":
$mac = _var($_POST, 'mac');
$vfio = _var($_POST, 'vfio');
$currentvfio = _var($_POST, 'currentvfio');
$currentmac = _var($_POST, 'currentmac');
$sriov = json_decode(getSriovInfoJson(), true);
if ($vfio == "true") $vfio = 1; else $vfio = 0;
if ($currentvfio == "true") $currentvfio = 1; else $currentvfio = 0;
try {
// Case 1: MAC changed and currently under vfio
if ($currentmac != $mac && $currentvfio == 1) {
$rtn = setVfMacAddress($pciid, $mac, "vfio-pci");
echo json_encode($rtn);
break;
}
// Case 2: VFIO binding changed
if ($currentvfio != $vfio && $currentmac == $mac) {
$driver = ($vfio == 1) ? "vfio-pci" : "original";
$rtn = rebindVfDriver($pciid, $sriov, $driver);
echo json_encode($rtn);
break;
}
// Case 3: MAC changed but still under host driver
if ($currentmac != $mac && $vfio == 0) {
$rtn = setVfMacAddress($pciid, $mac, null);
echo json_encode($rtn);
break;
}
// Nothing changed
echo json_response(true, null, ["No changes detected"]);
} catch (Throwable $e) {
echo json_response(false, "Error applying VF settings: " . $e->getMessage());
}
break;
// --------------------------------------------------------
// Check PCI device in use
// --------------------------------------------------------
case "inuse":
$pcitype = _var($_POST, 'pcitype');
$result = is_pci_inuse($pciid, $pcitype);
echo json_encode($result);
break;
default:
echo json_response(false, "Unknown request type: $type");
break;
}
?>
@@ -57,13 +57,14 @@ if (isset($pciid) && isset($vd)) {
$old = is_file($sriovvfs) ? rtrim(file_get_contents($sriovvfs)) : '';
$newexplode = explode(" ",str_replace("VFSETTINGS=","",$old));
$mac= _var($_POST,'mac');
if ($mac == "") $mac = "00:00:00:00:00:00";
$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) {
if($mac == "00:00:00:00:00:00" && $vfio == 0) {
unset($newexplode[$key]) ;
break;
} else {
+1 -1
View File
@@ -15,7 +15,7 @@
# example:VFSETTINGS=0000:04:11.5|8086:1520|62:00:04:11:05:01
SRIOV_ENABLED_FILE=/boot/config/sriov_enabled
if [[ ! -f $SRIOV_ENABLED_FILE ]] ; then
if [[ -f $SRIOV_DISABLED_FILE ]] ; then
echo 'SRIOV Processing disabled.'
exit
fi
+6 -6
View File
@@ -184,11 +184,11 @@ fi
echo "MAC Address set"
if [[ "$VFIO" == "1" ]]; then
echo "Binding VF to vfio"
/usr/local/sbin/vfio-pci-bind.sh "$BDF" "$VD" \
1>>/var/log/vfio-pci \
2>>/var/log/vfio-pci-errors
fi
if [[ "$VFIO" == "1" ]]; then
echo "Binding VF to vfio"
/usr/local/sbin/vfio-pci-bind.sh "$BDF" "$VD" \
1>>/var/log/vfio-pci \
2>>/var/log/vfio-pci-errors
fi