mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-01 21:13:09 +00:00
hardware acceleration and support for NVIDIA cards on nonfree images (#3089)
* add nvidia packages
* add nvidia deps to nonfree
* gpu_acceleration flag & nvidia hacking
* fix gpu_config & /tmp/lxc.log
* implement hardware acceleration more dynamically
* refactor OpenUI
* use mknod
* registry updates for multi-hardware-requirements
* pluralize
* handle new registry types
* remove log
* migrations and driver fixes
* wip
* misc patches
* handle nvidia-container differently
* chore: comments (#3093)
* chore: comments
* revert some sizing
---------
Co-authored-by: Matt Hill <mattnine@protonmail.com>
* Revert "handle nvidia-container differently"
This reverts commit d708ae53df.
* fix debian containers
* cleanup
* feat: add empty array placeholder in forms (#3095)
* fixes from testing, client side device filtering for better fingerprinting resistance
* fix mac builds
---------
Co-authored-by: Sam Sartor <me@samsartor.com>
Co-authored-by: Matt Hill <mattnine@protonmail.com>
Co-authored-by: Alex Inkin <alexander@inkin.ru>
This commit is contained in:
@@ -7,7 +7,7 @@ use ts_rs::TS;
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, TS, PartialEq, Eq)]
|
||||
#[ts(type = "string")]
|
||||
pub struct GitHash(String);
|
||||
|
||||
|
||||
@@ -242,18 +242,23 @@ impl TryFrom<ManifestV1> for Manifest {
|
||||
.device
|
||||
.into_iter()
|
||||
.map(|(class, product)| DeviceFilter {
|
||||
pattern_description: format!(
|
||||
description: format!(
|
||||
"a {class} device matching the expression {}",
|
||||
product.as_ref()
|
||||
),
|
||||
class,
|
||||
pattern: product,
|
||||
product: Some(product),
|
||||
..Default::default()
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
git_hash: value.git_hash,
|
||||
os_version: value.eos_version,
|
||||
sdk_version: None,
|
||||
hardware_acceleration: match value.main {
|
||||
PackageProcedure::Docker(d) => d.gpu_acceleration,
|
||||
PackageProcedure::Script(_) => false,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use crate::s9pk::git_hash::GitHash;
|
||||
use crate::s9pk::merkle_archive::directory_contents::DirectoryContents;
|
||||
use crate::s9pk::merkle_archive::expected::{Expected, Filter};
|
||||
use crate::s9pk::v2::pack::ImageConfig;
|
||||
use crate::util::lshw::{LshwDevice, LshwDisplay, LshwProcessor};
|
||||
use crate::util::serde::Regex;
|
||||
use crate::util::{VersionString, mime};
|
||||
use crate::version::{Current, VersionT};
|
||||
@@ -62,6 +63,8 @@ pub struct Manifest {
|
||||
pub dependencies: Dependencies,
|
||||
#[serde(default)]
|
||||
pub hardware_requirements: HardwareRequirements,
|
||||
#[serde(default)]
|
||||
pub hardware_acceleration: bool,
|
||||
pub git_hash: Option<GitHash>,
|
||||
#[serde(default = "current_version")]
|
||||
#[ts(type = "string")]
|
||||
@@ -165,7 +168,7 @@ impl Manifest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct HardwareRequirements {
|
||||
@@ -176,19 +179,122 @@ pub struct HardwareRequirements {
|
||||
#[ts(type = "string[] | null")]
|
||||
pub arch: Option<BTreeSet<InternedString>>,
|
||||
}
|
||||
impl HardwareRequirements {
|
||||
/// returns a value that can be used as a sort key to get most specific requirements first
|
||||
pub fn specificity_desc(&self) -> (u32, u32, u64) {
|
||||
(
|
||||
u32::MAX - self.device.len() as u32, // more device requirements = more specific
|
||||
self.arch.as_ref().map_or(u32::MAX, |a| a.len() as u32), // more arches = less specific
|
||||
self.ram.map_or(0, |r| r), // more ram = more specific
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct DeviceFilter {
|
||||
pub description: String,
|
||||
#[ts(type = "\"processor\" | \"display\"")]
|
||||
pub class: InternedString,
|
||||
#[ts(type = "string")]
|
||||
pub pattern: Regex,
|
||||
pub pattern_description: String,
|
||||
#[ts(type = "string | null")]
|
||||
pub product: Option<Regex>,
|
||||
#[ts(type = "string | null")]
|
||||
pub vendor: Option<Regex>,
|
||||
#[ts(optional)]
|
||||
pub capabilities: Option<BTreeSet<InternedString>>,
|
||||
#[ts(optional)]
|
||||
pub driver: Option<InternedString>,
|
||||
}
|
||||
// Omit description
|
||||
impl PartialEq for DeviceFilter {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.class == other.class
|
||||
&& self.product == other.product
|
||||
&& self.vendor == other.vendor
|
||||
&& self.capabilities == other.capabilities
|
||||
&& self.driver == other.driver
|
||||
}
|
||||
}
|
||||
impl DeviceFilter {
|
||||
pub fn matches(&self, device: &LshwDevice) -> bool {
|
||||
if &*self.class != device.class() {
|
||||
return false;
|
||||
}
|
||||
match device {
|
||||
LshwDevice::Processor(LshwProcessor {
|
||||
product,
|
||||
vendor,
|
||||
capabilities,
|
||||
}) => {
|
||||
if let Some(match_product) = &self.product {
|
||||
if !product
|
||||
.as_deref()
|
||||
.map_or(false, |p| match_product.as_ref().is_match(p))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if let Some(match_vendor) = &self.vendor {
|
||||
if !vendor
|
||||
.as_deref()
|
||||
.map_or(false, |v| match_vendor.as_ref().is_match(v))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if !self
|
||||
.capabilities
|
||||
.as_ref()
|
||||
.map_or(true, |c| c.is_subset(capabilities))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
LshwDevice::Display(LshwDisplay {
|
||||
product,
|
||||
vendor,
|
||||
capabilities,
|
||||
driver,
|
||||
}) => {
|
||||
if let Some(match_product) = &self.product {
|
||||
if !product
|
||||
.as_deref()
|
||||
.map_or(false, |p| match_product.as_ref().is_match(p))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if let Some(match_vendor) = &self.vendor {
|
||||
if !vendor
|
||||
.as_deref()
|
||||
.map_or(false, |v| match_vendor.as_ref().is_match(v))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if !self
|
||||
.capabilities
|
||||
.as_ref()
|
||||
.map_or(true, |c| c.is_subset(capabilities))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if !self
|
||||
.driver
|
||||
.as_ref()
|
||||
.map_or(true, |d| Some(d) == driver.as_ref())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS, PartialEq, Eq)]
|
||||
#[ts(export)]
|
||||
pub struct Description {
|
||||
pub short: String,
|
||||
@@ -212,7 +318,7 @@ impl Description {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct Alerts {
|
||||
|
||||
@@ -265,7 +265,7 @@ impl PackParams {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS)]
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct ImageConfig {
|
||||
@@ -274,15 +274,8 @@ pub struct ImageConfig {
|
||||
pub arch: BTreeSet<InternedString>,
|
||||
#[ts(type = "string | null")]
|
||||
pub emulate_missing_as: Option<InternedString>,
|
||||
}
|
||||
impl Default for ImageConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
source: ImageSource::Packed,
|
||||
arch: BTreeSet::new(),
|
||||
emulate_missing_as: None,
|
||||
}
|
||||
}
|
||||
#[serde(default)]
|
||||
pub nvidia_container: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -299,6 +292,8 @@ struct CliImageConfig {
|
||||
arch: Vec<InternedString>,
|
||||
#[arg(long)]
|
||||
emulate_missing_as: Option<InternedString>,
|
||||
#[arg(long)]
|
||||
nvidia_container: bool,
|
||||
}
|
||||
impl TryFrom<CliImageConfig> for ImageConfig {
|
||||
type Error = clap::Error;
|
||||
@@ -317,6 +312,7 @@ impl TryFrom<CliImageConfig> for ImageConfig {
|
||||
},
|
||||
arch: value.arch.into_iter().collect(),
|
||||
emulate_missing_as: value.emulate_missing_as,
|
||||
nvidia_container: value.nvidia_container,
|
||||
};
|
||||
res.emulate_missing_as
|
||||
.as_ref()
|
||||
@@ -379,20 +375,21 @@ pub enum ImageSource {
|
||||
DockerTag(String),
|
||||
// Recipe(DirRecipe),
|
||||
}
|
||||
impl Default for ImageSource {
|
||||
fn default() -> Self {
|
||||
ImageSource::Packed
|
||||
}
|
||||
}
|
||||
impl ImageSource {
|
||||
pub fn ingredients(&self) -> Vec<PathBuf> {
|
||||
match self {
|
||||
Self::Packed => Vec::new(),
|
||||
Self::DockerBuild {
|
||||
dockerfile,
|
||||
workdir,
|
||||
..
|
||||
} => {
|
||||
Self::DockerBuild { dockerfile, .. } => {
|
||||
vec![
|
||||
workdir
|
||||
dockerfile
|
||||
.as_deref()
|
||||
.unwrap_or(Path::new("."))
|
||||
.join(dockerfile.as_deref().unwrap_or(Path::new("Dockerfile"))),
|
||||
.unwrap_or(Path::new("Dockerfile"))
|
||||
.to_owned(),
|
||||
]
|
||||
}
|
||||
Self::DockerTag(_) => Vec::new(),
|
||||
|
||||
Reference in New Issue
Block a user