mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
feat: listen to netlink kernel events
This commit is contained in:
parent
599adf63cc
commit
e2257a83d2
@ -12,11 +12,14 @@ mod tests;
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use futures::future::select_all;
|
use futures::future::select_all;
|
||||||
|
use server::system;
|
||||||
use server::{handle_stream, handler::Handler, Server};
|
use server::{handle_stream, handler::Handler, Server};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{os::unix::net::UnixStream as StdUnixStream, time::Duration};
|
use std::{os::unix::net::UnixStream as StdUnixStream, time::Duration};
|
||||||
use tokio::net::UnixStream;
|
use tokio::net::UnixStream;
|
||||||
|
use tokio::sync::Notify;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
runtime,
|
runtime,
|
||||||
signal::unix::{signal, SignalKind},
|
signal::unix::{signal, SignalKind},
|
||||||
@ -72,6 +75,7 @@ pub fn run() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
tokio::task::spawn_local(listen_config_changes(handler.clone()));
|
tokio::task::spawn_local(listen_config_changes(handler.clone()));
|
||||||
tokio::task::spawn_local(listen_exit_signals(handler.clone()));
|
tokio::task::spawn_local(listen_exit_signals(handler.clone()));
|
||||||
|
tokio::task::spawn_local(listen_device_events(handler.clone()));
|
||||||
tokio::task::spawn_local(suspend::listen_events(handler));
|
tokio::task::spawn_local(suspend::listen_events(handler));
|
||||||
|
|
||||||
server.run().await;
|
server.run().await;
|
||||||
@ -136,6 +140,22 @@ async fn listen_config_changes(handler: Handler) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn listen_device_events(handler: Handler) {
|
||||||
|
let notify = Arc::new(Notify::new());
|
||||||
|
let task_notify = notify.clone();
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
if let Err(err) = system::listen_netlink_kernel_event(&task_notify) {
|
||||||
|
error!("kernel event listener error: {err:#}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loop {
|
||||||
|
notify.notified().await;
|
||||||
|
info!("got kernel drm subsystem event, reloading GPUs");
|
||||||
|
handler.reload_gpus().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn ensure_sufficient_uptime() {
|
async fn ensure_sufficient_uptime() {
|
||||||
match get_uptime() {
|
match get_uptime() {
|
||||||
Ok(current_uptime) => {
|
Ok(current_uptime) => {
|
||||||
|
@ -96,10 +96,7 @@ pub struct Handler {
|
|||||||
|
|
||||||
impl<'a> Handler {
|
impl<'a> Handler {
|
||||||
pub async fn new(config: Config) -> anyhow::Result<Self> {
|
pub async fn new(config: Config) -> anyhow::Result<Self> {
|
||||||
let base_path = match env::var("_LACT_DRM_SYSFS_PATH") {
|
let base_path = drm_base_path();
|
||||||
Ok(custom_path) => PathBuf::from(custom_path),
|
|
||||||
Err(_) => PathBuf::from("/sys/class/drm"),
|
|
||||||
};
|
|
||||||
Self::with_base_path(&base_path, config).await
|
Self::with_base_path(&base_path, config).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,6 +196,23 @@ impl<'a> Handler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn reload_gpus(&self) {
|
||||||
|
let base_path = drm_base_path();
|
||||||
|
match load_controllers(&base_path) {
|
||||||
|
Ok(new_controllers) => {
|
||||||
|
info!("GPU list reloaded with {} devices", new_controllers.len());
|
||||||
|
*self.gpu_controllers.write().await = new_controllers;
|
||||||
|
|
||||||
|
if let Err(err) = self.apply_current_config().await {
|
||||||
|
error!("could not reapply config: {err:#}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("could not load GPU controllers: {err:#}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn stop_profile_watcher(&self) {
|
async fn stop_profile_watcher(&self) {
|
||||||
let tx = self.profile_watcher_tx.borrow_mut().take();
|
let tx = self.profile_watcher_tx.borrow_mut().take();
|
||||||
if let Some(existing_stop_notify) = tx {
|
if let Some(existing_stop_notify) = tx {
|
||||||
@ -1074,3 +1088,10 @@ fn add_path_to_archive(
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drm_base_path() -> PathBuf {
|
||||||
|
match env::var("_LACT_DRM_SYSFS_PATH") {
|
||||||
|
Ok(custom_path) => PathBuf::from(custom_path),
|
||||||
|
Err(_) => PathBuf::from("/sys/class/drm"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
use anyhow::{anyhow, ensure, Context};
|
use anyhow::{anyhow, ensure, Context};
|
||||||
use lact_schema::{InitramfsType, SystemInfo, GIT_COMMIT};
|
use lact_schema::{InitramfsType, SystemInfo, GIT_COMMIT};
|
||||||
|
use nix::sys::socket::{
|
||||||
|
bind, recv, socket, AddressFamily, MsgFlags, NetlinkAddr, SockFlag, SockProtocol, SockType,
|
||||||
|
};
|
||||||
use os_release::{OsRelease, OS_RELEASE};
|
use os_release::{OsRelease, OS_RELEASE};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File, Permissions},
|
fs::{self, File, Permissions},
|
||||||
io::Write,
|
io::Write,
|
||||||
os::unix::prelude::PermissionsExt,
|
os::{fd::AsRawFd, unix::prelude::PermissionsExt},
|
||||||
path::Path,
|
path::Path,
|
||||||
|
process,
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
use tokio::process::Command;
|
use tokio::{process::Command, sync::Notify};
|
||||||
use tracing::{info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
static OC_TOGGLED: AtomicBool = AtomicBool::new(false);
|
static OC_TOGGLED: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
@ -191,6 +195,54 @@ async fn run_command(exec: &str, args: &[&str]) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn listen_netlink_kernel_event(notify: &Notify) -> anyhow::Result<()> {
|
||||||
|
let socket = socket(
|
||||||
|
AddressFamily::Netlink,
|
||||||
|
SockType::Raw,
|
||||||
|
SockFlag::empty(),
|
||||||
|
SockProtocol::NetlinkKObjectUEvent,
|
||||||
|
)
|
||||||
|
.context("Could not setup netlink socket")?;
|
||||||
|
|
||||||
|
let sa = NetlinkAddr::new(process::id(), 1);
|
||||||
|
bind(socket.as_raw_fd(), &sa).context("Could not bind netlink socket")?;
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
loop {
|
||||||
|
// Read only the size using the peek and truncate flags first
|
||||||
|
let msg_size = recv(
|
||||||
|
socket.as_raw_fd(),
|
||||||
|
&mut [],
|
||||||
|
MsgFlags::MSG_PEEK | MsgFlags::MSG_TRUNC,
|
||||||
|
)
|
||||||
|
.context("Could not read netlink message")?;
|
||||||
|
buf.clear();
|
||||||
|
buf.resize(msg_size, 0);
|
||||||
|
|
||||||
|
// Read the actual message into the buffer
|
||||||
|
recv(socket.as_raw_fd(), &mut buf, MsgFlags::empty())
|
||||||
|
.context("Could not read netlink message")?;
|
||||||
|
|
||||||
|
for raw_line in buf.split(|c| *c == b'\0') {
|
||||||
|
match std::str::from_utf8(raw_line) {
|
||||||
|
Ok(line) => {
|
||||||
|
if let Some(subsystem) = line.strip_prefix("SUBSYSTEM=") {
|
||||||
|
if subsystem == "drm" {
|
||||||
|
notify.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
error!(
|
||||||
|
"Got invalid unicode in uevent line {}",
|
||||||
|
String::from_utf8_lossy(raw_line)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::server::system::detect_initramfs_type;
|
use crate::server::system::detect_initramfs_type;
|
||||||
|
Loading…
Reference in New Issue
Block a user