Merge pull request #465 from mahkoh/jorth/cap-sys-nice
all: add support for CAP_SYS_NICE
This commit is contained in:
commit
7beaad9041
21 changed files with 585 additions and 51 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -611,6 +611,7 @@ dependencies = [
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"opera",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"png",
|
"png",
|
||||||
|
|
@ -859,6 +860,12 @@ version = "1.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opera"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5444847837ad2026bf6addabcf0c3c25bf3e9f92e435dedb555f4a6a0180ded1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option-ext"
|
name = "option-ext"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ kbvm = "0.1.4"
|
||||||
tiny-skia = { version = "0.11.4", default-features = false, features = ["std"] }
|
tiny-skia = { version = "0.11.4", default-features = false, features = ["std"] }
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
|
opera = "1.0.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
repc = "0.1.1"
|
repc = "0.1.1"
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,33 @@ cargo build --release
|
||||||
|
|
||||||
The binary is then available under `./target/release/jay`.
|
The binary is then available under `./target/release/jay`.
|
||||||
|
|
||||||
|
## Running with CAP_SYS_NICE
|
||||||
|
|
||||||
|
Jay supports being started with CAP_SYS_NICE capabilities. For example, such
|
||||||
|
capabilities can be added to the binary via
|
||||||
|
|
||||||
|
```shell
|
||||||
|
~# setcap cap_sys_nice=p jay
|
||||||
|
```
|
||||||
|
|
||||||
|
If CAP_SYS_NICE is available, Jay will, by default, elevate its scheduler to
|
||||||
|
SCHED_RR and create Vulkan queues with the highest available priority. This can
|
||||||
|
improve responsiveness if the CPU or GPU are under high load.
|
||||||
|
|
||||||
|
If Jay is started with the environment variable `JAY_NO_REALTIME=1` or a
|
||||||
|
`config.so` exists, then Jay will not elevate its scheduler but will still
|
||||||
|
create elevated Vulkan queues.
|
||||||
|
|
||||||
|
Jay will drop all capabilities almost immediately after being started. Before
|
||||||
|
that, it will spawn a dedicated thread that retains the CAP_SYS_NICE capability
|
||||||
|
to create elevated Vulkan queues later.
|
||||||
|
|
||||||
|
If Jay has elevated its scheduler to SCHED_RR, then it will refuse to load
|
||||||
|
`config.so` configurations. Otherwise unprivileged applications would be able
|
||||||
|
to run arbitrary code with SCHED_RR by crafting a dedicated `config.so`. This
|
||||||
|
behavior can be overridden by compiling Jay with
|
||||||
|
`JAY_ALLOW_REALTIME_CONFIG_SO=1`.
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@
|
||||||
[window-and-client-rules.md](./docs/window-and-client-rules.md).
|
[window-and-client-rules.md](./docs/window-and-client-rules.md).
|
||||||
- Add client and tree CLI subcommands to inspect clients and windows, primarily
|
- Add client and tree CLI subcommands to inspect clients and windows, primarily
|
||||||
to facilitate the writing of window and client rules.
|
to facilitate the writing of window and client rules.
|
||||||
|
- Jay now supports being started with CAP_SYS_NICE capabilities to improve
|
||||||
|
responsiveness under high system load. This is described in detail in
|
||||||
|
[setup.md](docs/setup.md).
|
||||||
|
|
||||||
# 1.10.0 (2025-04-22)
|
# 1.10.0 (2025-04-22)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ use {
|
||||||
compositor::start_compositor,
|
compositor::start_compositor,
|
||||||
format::{Format, ref_formats},
|
format::{Format, ref_formats},
|
||||||
portal,
|
portal,
|
||||||
|
pr_caps::drop_all_pr_caps,
|
||||||
},
|
},
|
||||||
::log::Level,
|
::log::Level,
|
||||||
clap::{Args, Parser, Subcommand, ValueEnum, ValueHint, builder::PossibleValue},
|
clap::{Args, Parser, Subcommand, ValueEnum, ValueHint, builder::PossibleValue},
|
||||||
|
|
@ -233,6 +234,9 @@ impl ValueEnum for &'static Format {
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let cli = Jay::parse();
|
let cli = Jay::parse();
|
||||||
|
if not_matches!(cli.command, Cmd::Run(_)) {
|
||||||
|
drop_all_pr_caps();
|
||||||
|
}
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Cmd::Run(a) => start_compositor(cli.global, a),
|
Cmd::Run(a) => start_compositor(cli.global, a),
|
||||||
Cmd::GenerateCompletion(g) => generate::main(g),
|
Cmd::GenerateCompletion(g) => generate::main(g),
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ use {
|
||||||
logger::Logger,
|
logger::Logger,
|
||||||
output_schedule::OutputSchedule,
|
output_schedule::OutputSchedule,
|
||||||
portal::{self, PortalStartup},
|
portal::{self, PortalStartup},
|
||||||
|
pr_caps::{PrCapsThread, pr_caps},
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
sighand::{self, SighandError},
|
sighand::{self, SighandError},
|
||||||
state::{ConnectorData, IdleState, ScreenlockState, State, XWaylandState},
|
state::{ConnectorData, IdleState, ScreenlockState, State, XWaylandState},
|
||||||
|
|
@ -51,9 +52,17 @@ use {
|
||||||
},
|
},
|
||||||
user_session::import_environment,
|
user_session::import_environment,
|
||||||
utils::{
|
utils::{
|
||||||
clone3::ensure_reaper, clonecell::CloneCell, errorfmt::ErrorFmt, fdcloser::FdCloser,
|
clone3::ensure_reaper,
|
||||||
numcell::NumCell, oserror::OsError, queue::AsyncQueue, refcounted::RefCounted,
|
clonecell::CloneCell,
|
||||||
run_toplevel::RunToplevel, tri::Try,
|
errorfmt::ErrorFmt,
|
||||||
|
fdcloser::FdCloser,
|
||||||
|
nice::{did_elevate_scheduler, elevate_scheduler},
|
||||||
|
numcell::NumCell,
|
||||||
|
oserror::OsError,
|
||||||
|
queue::AsyncQueue,
|
||||||
|
refcounted::RefCounted,
|
||||||
|
run_toplevel::RunToplevel,
|
||||||
|
tri::Try,
|
||||||
},
|
},
|
||||||
version::VERSION,
|
version::VERSION,
|
||||||
video::drm::wait_for_sync_obj::WaitForSyncObj,
|
video::drm::wait_for_sync_obj::WaitForSyncObj,
|
||||||
|
|
@ -72,6 +81,14 @@ pub const MAX_EXTENTS: i32 = (1 << 22) - 1;
|
||||||
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
||||||
sighand::reset_all();
|
sighand::reset_all();
|
||||||
let reaper_pid = ensure_reaper();
|
let reaper_pid = ensure_reaper();
|
||||||
|
let caps = pr_caps().into_comp();
|
||||||
|
let caps_thread = if caps.has_nice() {
|
||||||
|
elevate_scheduler();
|
||||||
|
Some(caps.into_thread())
|
||||||
|
} else {
|
||||||
|
drop(caps);
|
||||||
|
None
|
||||||
|
};
|
||||||
let forker = create_forker(reaper_pid);
|
let forker = create_forker(reaper_pid);
|
||||||
let portal = portal::run_from_compositor(global.log_level.into());
|
let portal = portal::run_from_compositor(global.log_level.into());
|
||||||
enable_profiler();
|
enable_profiler();
|
||||||
|
|
@ -83,7 +100,14 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let res = start_compositor2(Some(forker), portal, Some(logger.clone()), args, None);
|
let res = start_compositor2(
|
||||||
|
Some(forker),
|
||||||
|
portal,
|
||||||
|
Some(logger.clone()),
|
||||||
|
args,
|
||||||
|
None,
|
||||||
|
caps_thread,
|
||||||
|
);
|
||||||
leaks::log_leaked();
|
leaks::log_leaked();
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
let e = ErrorFmt(e);
|
let e = ErrorFmt(e);
|
||||||
|
|
@ -97,7 +121,7 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
||||||
|
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
pub fn start_compositor_for_test(future: TestFuture) -> Result<(), CompositorError> {
|
pub fn start_compositor_for_test(future: TestFuture) -> Result<(), CompositorError> {
|
||||||
let res = start_compositor2(None, None, None, RunArgs::default(), Some(future));
|
let res = start_compositor2(None, None, None, RunArgs::default(), Some(future), None);
|
||||||
leaks::log_leaked();
|
leaks::log_leaked();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
@ -143,9 +167,13 @@ fn start_compositor2(
|
||||||
logger: Option<Arc<Logger>>,
|
logger: Option<Arc<Logger>>,
|
||||||
run_args: RunArgs,
|
run_args: RunArgs,
|
||||||
test_future: Option<TestFuture>,
|
test_future: Option<TestFuture>,
|
||||||
|
caps_thread: Option<PrCapsThread>,
|
||||||
) -> Result<(), CompositorError> {
|
) -> Result<(), CompositorError> {
|
||||||
log::info!("pid = {}", uapi::getpid());
|
log::info!("pid = {}", uapi::getpid());
|
||||||
log::info!("version = {VERSION}");
|
log::info!("version = {VERSION}");
|
||||||
|
if did_elevate_scheduler() {
|
||||||
|
log::info!("Running with elevated scheduler: SCHED_RR");
|
||||||
|
}
|
||||||
init_fd_limit();
|
init_fd_limit();
|
||||||
leaks::init();
|
leaks::init();
|
||||||
clientmem::init()?;
|
clientmem::init()?;
|
||||||
|
|
@ -304,6 +332,7 @@ fn start_compositor2(
|
||||||
show_pin_icon: Cell::new(false),
|
show_pin_icon: Cell::new(false),
|
||||||
cl_matcher_manager: ClMatcherManager::new(&crit_ids),
|
cl_matcher_manager: ClMatcherManager::new(&crit_ids),
|
||||||
tl_matcher_manager: TlMatcherManager::new(&crit_ids),
|
tl_matcher_manager: TlMatcherManager::new(&crit_ids),
|
||||||
|
caps_thread,
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
@ -680,7 +709,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
state.dummy_output.set(Some(dummy_output));
|
state.dummy_output.set(Some(dummy_output));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn config_dir() -> Option<String> {
|
pub fn config_dir() -> Option<String> {
|
||||||
if let Ok(xdg) = env::var("XDG_CONFIG_HOME") {
|
if let Ok(xdg) = env::var("XDG_CONFIG_HOME") {
|
||||||
Some(format!("{}/jay", xdg))
|
Some(format!("{}/jay", xdg))
|
||||||
} else if let Ok(home) = env::var("HOME") {
|
} else if let Ok(home) = env::var("HOME") {
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,13 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
tree::ToplevelData,
|
tree::ToplevelData,
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, numcell::NumCell, ptr_ext::PtrExt,
|
clonecell::CloneCell,
|
||||||
toplevel_identifier::ToplevelIdentifier, unlink_on_drop::UnlinkOnDrop, xrd::xrd,
|
nice::{JAY_NO_REALTIME, dont_allow_config_so},
|
||||||
|
numcell::NumCell,
|
||||||
|
ptr_ext::PtrExt,
|
||||||
|
toplevel_identifier::ToplevelIdentifier,
|
||||||
|
unlink_on_drop::UnlinkOnDrop,
|
||||||
|
xrd::xrd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
bincode::Options,
|
bincode::Options,
|
||||||
|
|
@ -26,7 +31,7 @@ use {
|
||||||
window::{self, TileState},
|
window::{self, TileState},
|
||||||
},
|
},
|
||||||
libloading::Library,
|
libloading::Library,
|
||||||
std::{cell::Cell, io, mem, ptr, rc::Rc},
|
std::{cell::Cell, io, mem, path::Path, ptr, rc::Rc},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -42,6 +47,8 @@ pub enum ConfigError {
|
||||||
CopyConfigFile(#[source] io::Error),
|
CopyConfigFile(#[source] io::Error),
|
||||||
#[error("XDG_RUNTIME_DIR is not set")]
|
#[error("XDG_RUNTIME_DIR is not set")]
|
||||||
XrdNotSet,
|
XrdNotSet,
|
||||||
|
#[error("Custom config.so is not permitted")]
|
||||||
|
NotPermitted,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConfigProxy {
|
pub struct ConfigProxy {
|
||||||
|
|
@ -280,11 +287,24 @@ impl ConfigProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_config_dir(state: &Rc<State>) -> Result<Self, ConfigError> {
|
pub fn from_config_dir(state: &Rc<State>) -> Result<Self, ConfigError> {
|
||||||
|
if dont_allow_config_so() {
|
||||||
|
if have_config_so(state.config_dir.as_deref()) {
|
||||||
|
log::warn!("Not loading config.so because");
|
||||||
|
log::warn!(" 1. Jay was started with CAP_SYS_NICE");
|
||||||
|
log::warn!(" 2. Jay was not started with {}=1", JAY_NO_REALTIME);
|
||||||
|
log::warn!(" 3. The scheduler was elevated to SCHED_RR");
|
||||||
|
log::warn!(
|
||||||
|
" 4. Jay was not compiled with {}=1",
|
||||||
|
jay_allow_realtime_config_so!(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Err(ConfigError::NotPermitted);
|
||||||
|
}
|
||||||
let dir = match state.config_dir.as_deref() {
|
let dir = match state.config_dir.as_deref() {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
_ => return Err(ConfigError::ConfigDirNotSet),
|
_ => return Err(ConfigError::ConfigDirNotSet),
|
||||||
};
|
};
|
||||||
let file = format!("{}/config.so", dir);
|
let file = format!("{}/{CONFIG_SO}", dir);
|
||||||
unsafe { Self::from_file(&file, state) }
|
unsafe { Self::from_file(&file, state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -355,3 +375,15 @@ pub struct InvokedShortcut {
|
||||||
pub effective_mods: Modifiers,
|
pub effective_mods: Modifiers,
|
||||||
pub sym: KeySym,
|
pub sym: KeySym,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CONFIG_SO: &str = "config.so";
|
||||||
|
|
||||||
|
pub fn have_config_so(config_dir: Option<&str>) -> bool {
|
||||||
|
let Some(dir) = config_dir else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let mut dir = dir.to_owned();
|
||||||
|
dir.push_str("/");
|
||||||
|
dir.push_str(CONFIG_SO);
|
||||||
|
Path::new(&dir).exists()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use {
|
||||||
async_engine::AsyncEngine,
|
async_engine::AsyncEngine,
|
||||||
gfx_api::{GfxContext, GfxError},
|
gfx_api::{GfxContext, GfxError},
|
||||||
io_uring::IoUring,
|
io_uring::IoUring,
|
||||||
|
pr_caps::PrCapsThread,
|
||||||
utils::errorfmt::ErrorFmt,
|
utils::errorfmt::ErrorFmt,
|
||||||
video::drm::Drm,
|
video::drm::Drm,
|
||||||
},
|
},
|
||||||
|
|
@ -19,12 +20,13 @@ pub fn create_gfx_context(
|
||||||
ring: &Rc<IoUring>,
|
ring: &Rc<IoUring>,
|
||||||
drm: &Drm,
|
drm: &Drm,
|
||||||
api: GfxApi,
|
api: GfxApi,
|
||||||
|
caps_thread: Option<&PrCapsThread>,
|
||||||
) -> Result<Rc<dyn GfxContext>, GfxError> {
|
) -> Result<Rc<dyn GfxContext>, GfxError> {
|
||||||
let mut apis = [GfxApi::OpenGl, GfxApi::Vulkan];
|
let mut apis = [GfxApi::OpenGl, GfxApi::Vulkan];
|
||||||
apis.sort_by_key(|&a| if a == api { -1 } else { a as i32 });
|
apis.sort_by_key(|&a| if a == api { -1 } else { a as i32 });
|
||||||
let mut last_err = None;
|
let mut last_err = None;
|
||||||
for api in apis {
|
for api in apis {
|
||||||
let res = create_gfx_context_(eng, ring, drm, api);
|
let res = create_gfx_context_(eng, ring, drm, api, caps_thread);
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => return res,
|
Ok(_) => return res,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -41,10 +43,11 @@ fn create_gfx_context_(
|
||||||
ring: &Rc<IoUring>,
|
ring: &Rc<IoUring>,
|
||||||
drm: &Drm,
|
drm: &Drm,
|
||||||
api: GfxApi,
|
api: GfxApi,
|
||||||
|
caps_thread: Option<&PrCapsThread>,
|
||||||
) -> Result<Rc<dyn GfxContext>, GfxError> {
|
) -> Result<Rc<dyn GfxContext>, GfxError> {
|
||||||
match api {
|
match api {
|
||||||
GfxApi::OpenGl => gl::create_gfx_context(drm),
|
GfxApi::OpenGl => gl::create_gfx_context(drm),
|
||||||
GfxApi::Vulkan => vulkan::create_graphics_context(eng, ring, drm),
|
GfxApi::Vulkan => vulkan::create_graphics_context(eng, ring, drm, caps_thread),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,9 @@ use {
|
||||||
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
|
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
|
||||||
},
|
},
|
||||||
io_uring::IoUring,
|
io_uring::IoUring,
|
||||||
|
pr_caps::PrCapsThread,
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
utils::oserror::OsError,
|
utils::{errorfmt::ErrorFmt, oserror::OsError},
|
||||||
video::{
|
video::{
|
||||||
dmabuf::DmaBuf,
|
dmabuf::DmaBuf,
|
||||||
drm::{Drm, DrmError, sync_obj::SyncObjCtx},
|
drm::{Drm, DrmError, sync_obj::SyncObjCtx},
|
||||||
|
|
@ -224,16 +225,27 @@ pub fn create_graphics_context(
|
||||||
eng: &Rc<AsyncEngine>,
|
eng: &Rc<AsyncEngine>,
|
||||||
ring: &Rc<IoUring>,
|
ring: &Rc<IoUring>,
|
||||||
drm: &Drm,
|
drm: &Drm,
|
||||||
|
caps_thread: Option<&PrCapsThread>,
|
||||||
) -> Result<Rc<dyn GfxContext>, GfxError> {
|
) -> Result<Rc<dyn GfxContext>, GfxError> {
|
||||||
let instance = VulkanInstance::new(Level::Info, *VULKAN_VALIDATION)?;
|
let instance = VulkanInstance::new(Level::Info, *VULKAN_VALIDATION)?;
|
||||||
let device = instance.create_device(drm)?;
|
let device = 'device: {
|
||||||
|
if let Some(t) = caps_thread {
|
||||||
|
match unsafe { t.run(|| instance.create_device(drm, true)) } {
|
||||||
|
Ok(d) => break 'device d,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not create high-priority device: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance.create_device(drm, false)?
|
||||||
|
};
|
||||||
let renderer = device.create_renderer(eng, ring)?;
|
let renderer = device.create_renderer(eng, ring)?;
|
||||||
Ok(Rc::new(Context(renderer)))
|
Ok(Rc::new(Context(renderer)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_vulkan_allocator(drm: &Drm) -> Result<Rc<dyn Allocator>, AllocatorError> {
|
pub fn create_vulkan_allocator(drm: &Drm) -> Result<Rc<dyn Allocator>, AllocatorError> {
|
||||||
let instance = VulkanInstance::new(Level::Debug, *VULKAN_VALIDATION)?;
|
let instance = VulkanInstance::new(Level::Debug, *VULKAN_VALIDATION)?;
|
||||||
let device = instance.create_device(drm)?;
|
let device = instance.create_device(drm, false)?;
|
||||||
let allocator = device.create_bo_allocator(drm)?;
|
let allocator = device.create_bo_allocator(drm)?;
|
||||||
Ok(Rc::new(allocator))
|
Ok(Rc::new(allocator))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,12 @@ use {
|
||||||
physical_device_drm, queue_family_foreign,
|
physical_device_drm, queue_family_foreign,
|
||||||
},
|
},
|
||||||
khr::{
|
khr::{
|
||||||
driver_properties, external_fence_fd, external_memory_fd, external_semaphore_fd,
|
self, driver_properties, external_fence_fd, external_memory_fd, external_semaphore_fd,
|
||||||
push_descriptor,
|
push_descriptor,
|
||||||
},
|
},
|
||||||
vk::{
|
vk::{
|
||||||
self, DeviceCreateInfo, DeviceQueueCreateInfo, DeviceSize, DriverId,
|
self, DeviceCreateInfo, DeviceQueueCreateInfo, DeviceQueueGlobalPriorityCreateInfoKHR,
|
||||||
ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags,
|
DeviceSize, DriverId, ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags,
|
||||||
ExternalSemaphoreProperties, MAX_MEMORY_TYPES, MemoryPropertyFlags, MemoryType,
|
ExternalSemaphoreProperties, MAX_MEMORY_TYPES, MemoryPropertyFlags, MemoryType,
|
||||||
PhysicalDevice, PhysicalDeviceBufferDeviceAddressFeatures,
|
PhysicalDevice, PhysicalDeviceBufferDeviceAddressFeatures,
|
||||||
PhysicalDeviceDescriptorBufferFeaturesEXT, PhysicalDeviceDescriptorBufferPropertiesEXT,
|
PhysicalDeviceDescriptorBufferFeaturesEXT, PhysicalDeviceDescriptorBufferPropertiesEXT,
|
||||||
|
|
@ -39,7 +39,7 @@ use {
|
||||||
PhysicalDeviceProperties2, PhysicalDeviceSynchronization2Features,
|
PhysicalDeviceProperties2, PhysicalDeviceSynchronization2Features,
|
||||||
PhysicalDeviceTimelineSemaphoreFeatures,
|
PhysicalDeviceTimelineSemaphoreFeatures,
|
||||||
PhysicalDeviceUniformBufferStandardLayoutFeatures, PhysicalDeviceVulkan12Properties,
|
PhysicalDeviceUniformBufferStandardLayoutFeatures, PhysicalDeviceVulkan12Properties,
|
||||||
Queue, QueueFlags,
|
Queue, QueueFamilyProperties2, QueueFlags, QueueGlobalPriorityKHR,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isnt::std_1::collections::IsntHashMap2Ext,
|
isnt::std_1::collections::IsntHashMap2Ext,
|
||||||
|
|
@ -50,6 +50,7 @@ use {
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
},
|
},
|
||||||
uapi::Ustr,
|
uapi::Ustr,
|
||||||
|
vk::QueueFamilyGlobalPriorityPropertiesKHR,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct VulkanDevice {
|
pub struct VulkanDevice {
|
||||||
|
|
@ -210,20 +211,42 @@ impl VulkanInstance {
|
||||||
fn find_queues(
|
fn find_queues(
|
||||||
&self,
|
&self,
|
||||||
phy_dev: PhysicalDevice,
|
phy_dev: PhysicalDevice,
|
||||||
) -> Result<(u32, Option<(u32, u32, u32)>), VulkanError> {
|
) -> Result<
|
||||||
let props = unsafe {
|
(
|
||||||
|
u32,
|
||||||
|
Option<(u32, u32, u32)>,
|
||||||
|
QueueGlobalPriorityKHR,
|
||||||
|
QueueGlobalPriorityKHR,
|
||||||
|
),
|
||||||
|
VulkanError,
|
||||||
|
> {
|
||||||
|
let len = unsafe {
|
||||||
self.instance
|
self.instance
|
||||||
.get_physical_device_queue_family_properties(phy_dev)
|
.get_physical_device_queue_family_properties2_len(phy_dev)
|
||||||
};
|
};
|
||||||
|
let mut priority_props = vec![QueueFamilyGlobalPriorityPropertiesKHR::default(); len];
|
||||||
|
let mut props: Vec<_> = priority_props
|
||||||
|
.iter_mut()
|
||||||
|
.map(|p| QueueFamilyProperties2::default().push_next(p))
|
||||||
|
.collect();
|
||||||
|
unsafe {
|
||||||
|
self.instance
|
||||||
|
.get_physical_device_queue_family_properties2(phy_dev, &mut props[..])
|
||||||
|
}
|
||||||
let gfx_queue = props
|
let gfx_queue = props
|
||||||
.iter()
|
.iter()
|
||||||
.position(|p| p.queue_flags.contains(QueueFlags::GRAPHICS))
|
.position(|p| {
|
||||||
|
p.queue_family_properties
|
||||||
|
.queue_flags
|
||||||
|
.contains(QueueFlags::GRAPHICS)
|
||||||
|
})
|
||||||
.ok_or(VulkanError::NoGraphicsQueue)?;
|
.ok_or(VulkanError::NoGraphicsQueue)?;
|
||||||
let transfer_queue = 'transfer: {
|
let transfer_queue = 'transfer: {
|
||||||
let mut transfer_only = None;
|
let mut transfer_only = None;
|
||||||
let mut compute_only = None;
|
let mut compute_only = None;
|
||||||
let mut separate_gfx = None;
|
let mut separate_gfx = None;
|
||||||
for (idx, props) in props.iter().enumerate() {
|
for (idx, props) in props.iter().enumerate() {
|
||||||
|
let props = &props.queue_family_properties;
|
||||||
if idx == gfx_queue {
|
if idx == gfx_queue {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -244,7 +267,7 @@ impl VulkanInstance {
|
||||||
if let Some(idx) = transfer_only.or(compute_only).or(separate_gfx) {
|
if let Some(idx) = transfer_only.or(compute_only).or(separate_gfx) {
|
||||||
break 'transfer Some(idx);
|
break 'transfer Some(idx);
|
||||||
}
|
}
|
||||||
if props[gfx_queue].queue_count > 1 {
|
if props[gfx_queue].queue_family_properties.queue_count > 1 {
|
||||||
break 'transfer Some(gfx_queue);
|
break 'transfer Some(gfx_queue);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
@ -252,13 +275,27 @@ impl VulkanInstance {
|
||||||
let mut width_mask = 0;
|
let mut width_mask = 0;
|
||||||
let mut height_mask = 0;
|
let mut height_mask = 0;
|
||||||
if let Some(idx) = transfer_queue {
|
if let Some(idx) = transfer_queue {
|
||||||
let g = &props[idx].min_image_transfer_granularity;
|
let g = &props[idx]
|
||||||
|
.queue_family_properties
|
||||||
|
.min_image_transfer_granularity;
|
||||||
width_mask = g.width.wrapping_sub(1);
|
width_mask = g.width.wrapping_sub(1);
|
||||||
height_mask = g.height.wrapping_sub(1);
|
height_mask = g.height.wrapping_sub(1);
|
||||||
}
|
}
|
||||||
|
let get_priority = |idx: usize| {
|
||||||
|
let props = &priority_props[idx];
|
||||||
|
if props.priority_count > 0 {
|
||||||
|
props.priorities[props.priority_count as usize - 1]
|
||||||
|
} else {
|
||||||
|
QueueGlobalPriorityKHR::MEDIUM
|
||||||
|
}
|
||||||
|
};
|
||||||
Ok((
|
Ok((
|
||||||
gfx_queue as _,
|
gfx_queue as _,
|
||||||
transfer_queue.map(|v| (v as _, width_mask, height_mask)),
|
transfer_queue.map(|v| (v as _, width_mask, height_mask)),
|
||||||
|
get_priority(gfx_queue),
|
||||||
|
transfer_queue
|
||||||
|
.map(get_priority)
|
||||||
|
.unwrap_or(QueueGlobalPriorityKHR::MEDIUM),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,7 +312,11 @@ impl VulkanInstance {
|
||||||
.contains(ExternalSemaphoreFeatureFlags::IMPORTABLE)
|
.contains(ExternalSemaphoreFeatureFlags::IMPORTABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_device(self: &Rc<Self>, drm: &Drm) -> Result<Rc<VulkanDevice>, VulkanError> {
|
pub fn create_device(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
drm: &Drm,
|
||||||
|
mut high_priority: bool,
|
||||||
|
) -> Result<Rc<VulkanDevice>, VulkanError> {
|
||||||
let render_node = drm
|
let render_node = drm
|
||||||
.get_render_node()
|
.get_render_node()
|
||||||
.map_err(VulkanError::FetchRenderNode)?
|
.map_err(VulkanError::FetchRenderNode)?
|
||||||
|
|
@ -293,7 +334,17 @@ impl VulkanInstance {
|
||||||
if !supports_descriptor_buffer {
|
if !supports_descriptor_buffer {
|
||||||
log::warn!("Vulkan device does not support descriptor buffers");
|
log::warn!("Vulkan device does not support descriptor buffers");
|
||||||
}
|
}
|
||||||
let (graphics_queue_family_idx, transfer_queue_family) = self.find_queues(phy_dev)?;
|
let supports_queue_priority = extensions.contains_key(khr::global_priority::NAME);
|
||||||
|
if !supports_queue_priority && high_priority {
|
||||||
|
high_priority = false;
|
||||||
|
log::warn!("Vulkan device does not support queue priorities");
|
||||||
|
}
|
||||||
|
let (
|
||||||
|
graphics_queue_family_idx,
|
||||||
|
transfer_queue_family,
|
||||||
|
max_graphics_priority,
|
||||||
|
max_transfer_priority,
|
||||||
|
) = self.find_queues(phy_dev)?;
|
||||||
let mut distinct_transfer_queue_family_idx = None;
|
let mut distinct_transfer_queue_family_idx = None;
|
||||||
let mut transfer_granularity_mask = (0, 0);
|
let mut transfer_granularity_mask = (0, 0);
|
||||||
if let Some((idx, width_mask, height_mask)) = transfer_queue_family {
|
if let Some((idx, width_mask, height_mask)) = transfer_queue_family {
|
||||||
|
|
@ -325,18 +376,31 @@ impl VulkanInstance {
|
||||||
let mut uniform_buffer_standard_layout_features =
|
let mut uniform_buffer_standard_layout_features =
|
||||||
PhysicalDeviceUniformBufferStandardLayoutFeatures::default()
|
PhysicalDeviceUniformBufferStandardLayoutFeatures::default()
|
||||||
.uniform_buffer_standard_layout(true);
|
.uniform_buffer_standard_layout(true);
|
||||||
|
let mut gfx_queue_device_queue_global_priority_create_info =
|
||||||
|
DeviceQueueGlobalPriorityCreateInfoKHR::default()
|
||||||
|
.global_priority(max_graphics_priority);
|
||||||
|
let mut trn_queue_device_queue_global_priority_create_info =
|
||||||
|
DeviceQueueGlobalPriorityCreateInfoKHR::default()
|
||||||
|
.global_priority(max_transfer_priority);
|
||||||
let mut queue_create_infos = ArrayVec::<_, 2>::new();
|
let mut queue_create_infos = ArrayVec::<_, 2>::new();
|
||||||
queue_create_infos.push(
|
let queue_create_info = |idx, priority_info| {
|
||||||
DeviceQueueCreateInfo::default()
|
let mut info = DeviceQueueCreateInfo::default()
|
||||||
.queue_family_index(graphics_queue_family_idx)
|
.queue_family_index(idx)
|
||||||
.queue_priorities(&[1.0]),
|
.queue_priorities(&[1.0]);
|
||||||
);
|
if high_priority {
|
||||||
|
info = info.push_next(priority_info);
|
||||||
|
}
|
||||||
|
info
|
||||||
|
};
|
||||||
|
queue_create_infos.push(queue_create_info(
|
||||||
|
graphics_queue_family_idx,
|
||||||
|
&mut gfx_queue_device_queue_global_priority_create_info,
|
||||||
|
));
|
||||||
if let Some((tq, _, _)) = transfer_queue_family {
|
if let Some((tq, _, _)) = transfer_queue_family {
|
||||||
queue_create_infos.push(
|
queue_create_infos.push(queue_create_info(
|
||||||
DeviceQueueCreateInfo::default()
|
tq,
|
||||||
.queue_family_index(tq)
|
&mut trn_queue_device_queue_global_priority_create_info,
|
||||||
.queue_priorities(&[1.0]),
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
let mut device_create_info = DeviceCreateInfo::default()
|
let mut device_create_info = DeviceCreateInfo::default()
|
||||||
.push_next(&mut semaphore_features)
|
.push_next(&mut semaphore_features)
|
||||||
|
|
@ -433,6 +497,11 @@ impl VulkanInstance {
|
||||||
};
|
};
|
||||||
unsafe { device.get_device_queue(family_idx, queue_idx) }
|
unsafe { device.get_device_queue(family_idx, queue_idx) }
|
||||||
});
|
});
|
||||||
|
if high_priority {
|
||||||
|
log::info!(
|
||||||
|
"Created queues with priorities {max_graphics_priority:?}/{max_transfer_priority:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
Ok(Rc::new(VulkanDevice {
|
Ok(Rc::new(VulkanDevice {
|
||||||
physical_device: phy_dev,
|
physical_device: phy_dev,
|
||||||
render_node,
|
render_node,
|
||||||
|
|
|
||||||
|
|
@ -760,3 +760,15 @@ macro_rules! client_wire_scale_to_logical {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! not_matches {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
!matches!($($tt)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! jay_allow_realtime_config_so {
|
||||||
|
() => {
|
||||||
|
"JAY_ALLOW_REALTIME_CONFIG_SO"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ mod output_schedule;
|
||||||
mod pango;
|
mod pango;
|
||||||
mod pipewire;
|
mod pipewire;
|
||||||
mod portal;
|
mod portal;
|
||||||
|
mod pr_caps;
|
||||||
mod rect;
|
mod rect;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
mod scale;
|
mod scale;
|
||||||
|
|
|
||||||
|
|
@ -185,17 +185,22 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if render_ctx.is_none() {
|
if render_ctx.is_none() {
|
||||||
let ctx =
|
let ctx = match create_gfx_context(
|
||||||
match create_gfx_context(&self.state.eng, &self.state.ring, &drm, GfxApi::OpenGl) {
|
&self.state.eng,
|
||||||
Ok(c) => c,
|
&self.state.ring,
|
||||||
Err(e) => {
|
&drm,
|
||||||
log::error!(
|
GfxApi::OpenGl,
|
||||||
"Could not create render context from drm device: {}",
|
None,
|
||||||
ErrorFmt(e)
|
) {
|
||||||
);
|
Ok(c) => c,
|
||||||
return;
|
Err(e) => {
|
||||||
}
|
log::error!(
|
||||||
};
|
"Could not create render context from drm device: {}",
|
||||||
|
ErrorFmt(e)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
let ctx = Rc::new(PortalRenderCtx {
|
let ctx = Rc::new(PortalRenderCtx {
|
||||||
_dev_id: dev_id,
|
_dev_id: dev_id,
|
||||||
ctx,
|
ctx,
|
||||||
|
|
|
||||||
275
src/pr_caps.rs
Normal file
275
src/pr_caps.rs
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
pr_caps::sys::{
|
||||||
|
_LINUX_CAPABILITY_U32S_3, _LINUX_CAPABILITY_VERSION_3, CAP_SYS_NICE, cap_user_data_t,
|
||||||
|
cap_user_header_t,
|
||||||
|
},
|
||||||
|
utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt, oserror::OsError},
|
||||||
|
},
|
||||||
|
opera::PhantomNotSend,
|
||||||
|
parking_lot::{Condvar, Mutex},
|
||||||
|
std::{
|
||||||
|
mem,
|
||||||
|
sync::Arc,
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
},
|
||||||
|
uapi::{
|
||||||
|
c::{SYS_capget, SYS_capset, syscall},
|
||||||
|
map_err,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct PrCaps {
|
||||||
|
effective: u64,
|
||||||
|
permitted: u64,
|
||||||
|
inheritable: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PrCompCaps {
|
||||||
|
caps: PrCaps,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PrCapsThread {
|
||||||
|
thread: Option<JoinHandle<()>>,
|
||||||
|
data: Arc<ThreadData>,
|
||||||
|
_no_send: PhantomNotSend,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ThreadData {
|
||||||
|
cond: Condvar,
|
||||||
|
mutex: Mutex<MutData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct MutData {
|
||||||
|
exit: bool,
|
||||||
|
fun: Option<Box<dyn FnOnce() + Send>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pr_caps() -> PrCaps {
|
||||||
|
let mut hdr = cap_user_header_t {
|
||||||
|
version: _LINUX_CAPABILITY_VERSION_3,
|
||||||
|
pid: 0,
|
||||||
|
};
|
||||||
|
let mut caps = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3];
|
||||||
|
let ret = unsafe { syscall(SYS_capget, &mut hdr, &mut caps) };
|
||||||
|
if let Err(e) = map_err!(ret) {
|
||||||
|
eprintln!(
|
||||||
|
"Could not get process capabilities: {}",
|
||||||
|
ErrorFmt(OsError(e.0))
|
||||||
|
);
|
||||||
|
return PrCaps {
|
||||||
|
effective: 0,
|
||||||
|
permitted: 0,
|
||||||
|
inheritable: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
PrCaps {
|
||||||
|
effective: caps[0].effective as u64 | ((caps[1].effective as u64) << 32),
|
||||||
|
permitted: caps[0].permitted as u64 | ((caps[1].permitted as u64) << 32),
|
||||||
|
inheritable: caps[0].inheritable as u64 | ((caps[1].inheritable as u64) << 32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drop_all_pr_caps() {
|
||||||
|
let mut hdr = cap_user_header_t {
|
||||||
|
version: _LINUX_CAPABILITY_VERSION_3,
|
||||||
|
pid: 0,
|
||||||
|
};
|
||||||
|
let caps = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3];
|
||||||
|
let ret = unsafe { syscall(SYS_capset, &mut hdr, &caps) };
|
||||||
|
if let Err(e) = map_err!(ret) {
|
||||||
|
eprintln!(
|
||||||
|
"Could not get drop capabilities: {}",
|
||||||
|
ErrorFmt(OsError(e.0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrCaps {
|
||||||
|
pub fn into_comp(mut self) -> PrCompCaps {
|
||||||
|
let mut caps = 0;
|
||||||
|
macro_rules! add_cap {
|
||||||
|
($name:ident) => {
|
||||||
|
if self.permitted.contains(1 << $name) {
|
||||||
|
caps |= 1 << $name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
add_cap!(CAP_SYS_NICE);
|
||||||
|
let mut hdr = cap_user_header_t {
|
||||||
|
version: _LINUX_CAPABILITY_VERSION_3,
|
||||||
|
pid: 0,
|
||||||
|
};
|
||||||
|
let caps_hi = (caps >> 32) as u32;
|
||||||
|
let caps_lo = caps as u32;
|
||||||
|
let mut data = [cap_user_data_t::default(); _LINUX_CAPABILITY_U32S_3];
|
||||||
|
data[0].effective = caps_lo;
|
||||||
|
data[1].effective = caps_hi;
|
||||||
|
data[0].permitted = caps_lo;
|
||||||
|
data[1].permitted = caps_hi;
|
||||||
|
let ret = unsafe { syscall(SYS_capset, &mut hdr, &data) };
|
||||||
|
if let Err(e) = map_err!(ret) {
|
||||||
|
eprintln!(
|
||||||
|
"Could not get set compositor capabilities: {}",
|
||||||
|
ErrorFmt(OsError(e.0))
|
||||||
|
);
|
||||||
|
return PrCompCaps { caps: self };
|
||||||
|
}
|
||||||
|
self.effective = caps;
|
||||||
|
self.permitted = caps;
|
||||||
|
self.inheritable = 0;
|
||||||
|
PrCompCaps { caps: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrCompCaps {
|
||||||
|
pub fn has_nice(&self) -> bool {
|
||||||
|
self.caps.effective.contains(1 << CAP_SYS_NICE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_thread(self) -> PrCapsThread {
|
||||||
|
let data = Arc::new(ThreadData::default());
|
||||||
|
let data2 = data.clone();
|
||||||
|
let jh = thread::Builder::new()
|
||||||
|
.name("SYS_nice thread".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
let data2 = data2;
|
||||||
|
let mut lock = data2.mutex.lock();
|
||||||
|
loop {
|
||||||
|
if lock.exit {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(f) = lock.fun.take() {
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
data2.cond.wait(&mut lock);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.expect("Could not spawn SYS_nice thread");
|
||||||
|
PrCapsThread {
|
||||||
|
thread: Some(jh),
|
||||||
|
data,
|
||||||
|
_no_send: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrCapsThread {
|
||||||
|
pub unsafe fn run<T, F>(&self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce() -> T,
|
||||||
|
{
|
||||||
|
struct AssertSend<T>(T);
|
||||||
|
unsafe impl<T> Send for AssertSend<T> {}
|
||||||
|
struct Data<T> {
|
||||||
|
cond: Condvar,
|
||||||
|
mutex: Mutex<Option<AssertSend<T>>>,
|
||||||
|
}
|
||||||
|
let data = Arc::new(Data {
|
||||||
|
cond: Default::default(),
|
||||||
|
mutex: Default::default(),
|
||||||
|
});
|
||||||
|
let data2 = data.clone();
|
||||||
|
let f = AssertSend(f);
|
||||||
|
let fun = Box::new(move || {
|
||||||
|
let f = f;
|
||||||
|
let t = f.0();
|
||||||
|
*data2.mutex.lock() = Some(AssertSend(t));
|
||||||
|
data2.cond.notify_all();
|
||||||
|
});
|
||||||
|
let fun = unsafe {
|
||||||
|
mem::transmute::<Box<dyn FnOnce() + Send + '_>, Box<dyn FnOnce() + Send>>(fun)
|
||||||
|
};
|
||||||
|
self.data.mutex.lock().fun = Some(fun);
|
||||||
|
self.data.cond.notify_all();
|
||||||
|
let mut lock = data.mutex.lock();
|
||||||
|
loop {
|
||||||
|
if let Some(t) = lock.take() {
|
||||||
|
return t.0;
|
||||||
|
}
|
||||||
|
data.cond.wait(&mut lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for PrCaps {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
drop_all_pr_caps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for PrCapsThread {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.data.mutex.lock().exit = true;
|
||||||
|
self.data.cond.notify_all();
|
||||||
|
let _ = self.thread.take().unwrap().join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod sys {
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use uapi::c::pid_t;
|
||||||
|
|
||||||
|
pub const _LINUX_CAPABILITY_VERSION_3: u32 = 0x20080522;
|
||||||
|
pub const _LINUX_CAPABILITY_U32S_3: usize = 2;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct cap_user_header_t {
|
||||||
|
pub version: u32,
|
||||||
|
pub pid: pid_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub struct cap_user_data_t {
|
||||||
|
pub effective: u32,
|
||||||
|
pub permitted: u32,
|
||||||
|
pub inheritable: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CAP_CHOWN: u32 = 0;
|
||||||
|
pub const CAP_DAC_OVERRIDE: u32 = 1;
|
||||||
|
pub const CAP_DAC_READ_SEARCH: u32 = 2;
|
||||||
|
pub const CAP_FOWNER: u32 = 3;
|
||||||
|
pub const CAP_FSETID: u32 = 4;
|
||||||
|
pub const CAP_KILL: u32 = 5;
|
||||||
|
pub const CAP_SETGID: u32 = 6;
|
||||||
|
pub const CAP_SETUID: u32 = 7;
|
||||||
|
pub const CAP_SETPCAP: u32 = 8;
|
||||||
|
pub const CAP_LINUX_IMMUTABLE: u32 = 9;
|
||||||
|
pub const CAP_NET_BIND_SERVICE: u32 = 10;
|
||||||
|
pub const CAP_NET_BROADCAST: u32 = 11;
|
||||||
|
pub const CAP_NET_ADMIN: u32 = 12;
|
||||||
|
pub const CAP_NET_RAW: u32 = 13;
|
||||||
|
pub const CAP_IPC_LOCK: u32 = 14;
|
||||||
|
pub const CAP_IPC_OWNER: u32 = 15;
|
||||||
|
pub const CAP_SYS_MODULE: u32 = 16;
|
||||||
|
pub const CAP_SYS_RAWIO: u32 = 17;
|
||||||
|
pub const CAP_SYS_CHROOT: u32 = 18;
|
||||||
|
pub const CAP_SYS_PTRACE: u32 = 19;
|
||||||
|
pub const CAP_SYS_PACCT: u32 = 20;
|
||||||
|
pub const CAP_SYS_ADMIN: u32 = 21;
|
||||||
|
pub const CAP_SYS_BOOT: u32 = 22;
|
||||||
|
pub const CAP_SYS_NICE: u32 = 23;
|
||||||
|
pub const CAP_SYS_RESOURCE: u32 = 24;
|
||||||
|
pub const CAP_SYS_TIME: u32 = 25;
|
||||||
|
pub const CAP_SYS_TTY_CONFIG: u32 = 26;
|
||||||
|
pub const CAP_MKNOD: u32 = 27;
|
||||||
|
pub const CAP_LEASE: u32 = 28;
|
||||||
|
pub const CAP_AUDIT_WRITE: u32 = 29;
|
||||||
|
pub const CAP_AUDIT_CONTROL: u32 = 30;
|
||||||
|
pub const CAP_SETFCAP: u32 = 31;
|
||||||
|
pub const CAP_MAC_OVERRIDE: u32 = 32;
|
||||||
|
pub const CAP_MAC_ADMIN: u32 = 33;
|
||||||
|
pub const CAP_SYSLOG: u32 = 34;
|
||||||
|
pub const CAP_WAKE_ALARM: u32 = 35;
|
||||||
|
pub const CAP_BLOCK_SUSPEND: u32 = 36;
|
||||||
|
pub const CAP_AUDIT_READ: u32 = 37;
|
||||||
|
pub const CAP_PERFMON: u32 = 38;
|
||||||
|
pub const CAP_BPF: u32 = 39;
|
||||||
|
pub const CAP_CHECKPOINT_RESTORE: u32 = 40;
|
||||||
|
}
|
||||||
|
|
@ -74,6 +74,7 @@ use {
|
||||||
keyboard::KeyboardStateIds,
|
keyboard::KeyboardStateIds,
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
logger::Logger,
|
logger::Logger,
|
||||||
|
pr_caps::PrCapsThread,
|
||||||
rect::{Rect, Region},
|
rect::{Rect, Region},
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
|
|
@ -246,6 +247,7 @@ pub struct State {
|
||||||
pub show_pin_icon: Cell<bool>,
|
pub show_pin_icon: Cell<bool>,
|
||||||
pub cl_matcher_manager: ClMatcherManager,
|
pub cl_matcher_manager: ClMatcherManager,
|
||||||
pub tl_matcher_manager: TlMatcherManager,
|
pub tl_matcher_manager: TlMatcherManager,
|
||||||
|
pub caps_thread: Option<PrCapsThread>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -446,6 +448,7 @@ impl State {
|
||||||
&self.ring,
|
&self.ring,
|
||||||
drm,
|
drm,
|
||||||
api.unwrap_or(self.default_gfx_api.get()),
|
api.unwrap_or(self.default_gfx_api.get()),
|
||||||
|
self.caps_thread.as_ref(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ pub mod line_logger;
|
||||||
pub mod linkedlist;
|
pub mod linkedlist;
|
||||||
pub mod log_on_drop;
|
pub mod log_on_drop;
|
||||||
pub mod mmap;
|
pub mod mmap;
|
||||||
|
pub mod nice;
|
||||||
pub mod nonblock;
|
pub mod nonblock;
|
||||||
pub mod num_cpus;
|
pub mod num_cpus;
|
||||||
pub mod numcell;
|
pub mod numcell;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
forker::ForkerError,
|
forker::ForkerError,
|
||||||
|
pr_caps::drop_all_pr_caps,
|
||||||
utils::{errorfmt::ErrorFmt, on_drop::OnDrop, process_name::set_process_name},
|
utils::{errorfmt::ErrorFmt, on_drop::OnDrop, process_name::set_process_name},
|
||||||
},
|
},
|
||||||
std::{env, mem::MaybeUninit, process, slice, str::FromStr},
|
std::{env, mem::MaybeUninit, process, slice, str::FromStr},
|
||||||
|
|
@ -135,6 +136,7 @@ pub fn ensure_reaper() -> c::pid_t {
|
||||||
if let Ok(id) = env::var(REAPER_VAR) {
|
if let Ok(id) = env::var(REAPER_VAR) {
|
||||||
if let Ok(id) = c::pid_t::from_str(&id) {
|
if let Ok(id) = c::pid_t::from_str(&id) {
|
||||||
if uapi::getppid() == id {
|
if uapi::getppid() == id {
|
||||||
|
set_deathsig();
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,8 +159,10 @@ pub fn ensure_reaper() -> c::pid_t {
|
||||||
unsafe {
|
unsafe {
|
||||||
env::set_var(REAPER_VAR, reaper_pid.to_string());
|
env::set_var(REAPER_VAR, reaper_pid.to_string());
|
||||||
}
|
}
|
||||||
|
set_deathsig();
|
||||||
return reaper_pid;
|
return reaper_pid;
|
||||||
};
|
};
|
||||||
|
drop_all_pr_caps();
|
||||||
set_process_name("jay reaper");
|
set_process_name("jay reaper");
|
||||||
while let Ok((pid, status)) = uapi::wait() {
|
while let Ok((pid, status)) = uapi::wait() {
|
||||||
if pid == main_process_id {
|
if pid == main_process_id {
|
||||||
|
|
@ -167,3 +171,9 @@ pub fn ensure_reaper() -> c::pid_t {
|
||||||
}
|
}
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_deathsig() {
|
||||||
|
unsafe {
|
||||||
|
c::prctl(c::PR_SET_PDEATHSIG, c::SIGKILL as c::c_ulong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
40
src/utils/nice.rs
Normal file
40
src/utils/nice.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
use {
|
||||||
|
crate::{compositor::config_dir, config::have_config_so},
|
||||||
|
c::sched_setscheduler,
|
||||||
|
std::{
|
||||||
|
env, mem,
|
||||||
|
sync::atomic::{AtomicBool, Ordering::Relaxed},
|
||||||
|
},
|
||||||
|
uapi::c::{self, SCHED_RESET_ON_FORK, SCHED_RR, sched_param},
|
||||||
|
};
|
||||||
|
|
||||||
|
static DID_ELEVATE_SCHEDULER: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
pub const JAY_NO_REALTIME: &str = "JAY_NO_REALTIME";
|
||||||
|
|
||||||
|
pub fn elevate_scheduler() {
|
||||||
|
if env::var(JAY_NO_REALTIME).as_deref().unwrap_or_default() == "1" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if have_config_so(config_dir().as_deref()) && dont_allow_realtime_config_so() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut param = unsafe { mem::zeroed::<sched_param>() };
|
||||||
|
param.sched_priority = 1;
|
||||||
|
let res = unsafe { sched_setscheduler(0, SCHED_RR | SCHED_RESET_ON_FORK, ¶m) };
|
||||||
|
if res == 0 {
|
||||||
|
DID_ELEVATE_SCHEDULER.store(true, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn did_elevate_scheduler() -> bool {
|
||||||
|
DID_ELEVATE_SCHEDULER.load(Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dont_allow_realtime_config_so() -> bool {
|
||||||
|
option_env!(jay_allow_realtime_config_so!()).unwrap_or_default() != "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dont_allow_config_so() -> bool {
|
||||||
|
did_elevate_scheduler() && dont_allow_realtime_config_so()
|
||||||
|
}
|
||||||
|
|
@ -1529,7 +1529,7 @@
|
||||||
"tile",
|
"tile",
|
||||||
"quit",
|
"quit",
|
||||||
"reload-config-toml",
|
"reload-config-toml",
|
||||||
"reload-config-to",
|
"reload-config-so",
|
||||||
"consume",
|
"consume",
|
||||||
"forward",
|
"forward",
|
||||||
"none",
|
"none",
|
||||||
|
|
|
||||||
|
|
@ -3428,7 +3428,7 @@ The string should have one of the following values:
|
||||||
|
|
||||||
Reload the `config.toml`.
|
Reload the `config.toml`.
|
||||||
|
|
||||||
- `reload-config-to`:
|
- `reload-config-so`:
|
||||||
|
|
||||||
Reload the `config.so`.
|
Reload the `config.so`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -790,7 +790,7 @@ SimpleActionName:
|
||||||
description: Terminate the compositor.
|
description: Terminate the compositor.
|
||||||
- value: reload-config-toml
|
- value: reload-config-toml
|
||||||
description: Reload the `config.toml`.
|
description: Reload the `config.toml`.
|
||||||
- value: reload-config-to
|
- value: reload-config-so
|
||||||
description: Reload the `config.so`.
|
description: Reload the `config.so`.
|
||||||
- value: consume
|
- value: consume
|
||||||
description: |
|
description: |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue