Skip to main content

baracuda_driver/
green.rs

1//! Green contexts (CUDA 12.4+) — partition a GPU's SMs into isolated
2//! "green" subsets that each run their own stream/kernel pipeline without
3//! contending for SMs with other green contexts on the same device.
4//!
5//! Availability: driver-reported CUDA 12.4 or newer. Falls through with
6//! `LoaderError::SymbolNotFound` on older drivers.
7
8use std::sync::Arc;
9
10use baracuda_cuda_sys::types::{CUdevResource, CUdevResourceType, CUdevSmResource};
11use baracuda_cuda_sys::{driver, CUcontext, CUdevResourceDesc, CUgreenCtx, CUstream};
12
13use crate::device::Device;
14use crate::error::{check, Result};
15
16/// Fetch the device's full SM resource — the starting point for
17/// `split_by_count`.
18pub fn device_sm_resource(device: &Device) -> Result<CUdevResource> {
19    let d = driver()?;
20    let cu = d.cu_device_get_dev_resource()?;
21    let mut r = CUdevResource::default();
22    check(unsafe { cu(device.as_raw(), &mut r, CUdevResourceType::SM) })?;
23    Ok(r)
24}
25
26/// Split an SM resource into groups of `min_count` SMs each. Returns the
27/// vector of new resources plus the leftover remainder resource.
28pub fn sm_resource_split_by_count(
29    input: &CUdevResource,
30    min_count: u32,
31) -> Result<(Vec<CUdevResource>, CUdevResource)> {
32    let d = driver()?;
33    let cu = d.cu_dev_sm_resource_split_by_count()?;
34    // Probe: pass nb_groups=0 to query how many groups would result.
35    let mut nb: core::ffi::c_uint = 0;
36    let mut remaining = CUdevResource::default();
37    check(unsafe {
38        cu(
39            core::ptr::null_mut(),
40            &mut nb,
41            input,
42            &mut remaining,
43            0,
44            min_count,
45        )
46    })?;
47    let mut result = vec![CUdevResource::default(); nb as usize];
48    if nb > 0 {
49        check(unsafe {
50            cu(
51                result.as_mut_ptr(),
52                &mut nb,
53                input,
54                &mut remaining,
55                0,
56                min_count,
57            )
58        })?;
59    }
60    Ok((result, remaining))
61}
62
63/// A green context — an SM-partitioned CUDA context. Drops destroy the
64/// green context (does not affect the parent device context).
65#[derive(Clone)]
66pub struct GreenContext {
67    inner: Arc<GreenContextInner>,
68}
69
70struct GreenContextInner {
71    handle: CUgreenCtx,
72}
73
74unsafe impl Send for GreenContextInner {}
75unsafe impl Sync for GreenContextInner {}
76
77impl core::fmt::Debug for GreenContextInner {
78    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
79        f.debug_struct("GreenContext")
80            .field("handle", &self.handle)
81            .finish_non_exhaustive()
82    }
83}
84
85impl core::fmt::Debug for GreenContext {
86    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87        self.inner.fmt(f)
88    }
89}
90
91impl GreenContext {
92    /// Create a green context on `device` from a single resource (e.g. an
93    /// SM partition produced by [`sm_resource_split_by_count`]).
94    pub fn from_resource(device: &Device, mut resource: CUdevResource) -> Result<Self> {
95        let d = driver()?;
96        // 1) Pack the resource into a CUdevResourceDesc.
97        // (`gen` is a reserved keyword in Rust 2024, so we name this `generate`.)
98        let generate = d.cu_dev_resource_generate_desc()?;
99        let mut desc: CUdevResourceDesc = core::ptr::null_mut();
100        check(unsafe { generate(&mut desc, &mut resource, 1) })?;
101        // 2) Actually create the green context.
102        let create = d.cu_green_ctx_create()?;
103        let mut handle: CUgreenCtx = core::ptr::null_mut();
104        check(unsafe { create(&mut handle, desc, device.as_raw(), 0) })?;
105        Ok(Self {
106            inner: Arc::new(GreenContextInner { handle }),
107        })
108    }
109
110    /// Convert this green context into a regular `CUcontext` handle that
111    /// can be set as current or passed to other APIs.
112    pub fn as_ctx_raw(&self) -> Result<CUcontext> {
113        let d = driver()?;
114        let cu = d.cu_ctx_from_green_ctx()?;
115        let mut out: CUcontext = core::ptr::null_mut();
116        check(unsafe { cu(&mut out, self.inner.handle) })?;
117        Ok(out)
118    }
119
120    /// Fetch the SM resource the green context was created with.
121    pub fn sm_resource(&self) -> Result<CUdevSmResource> {
122        let d = driver()?;
123        let cu = d.cu_green_ctx_get_dev_resource()?;
124        let mut r = CUdevResource::default();
125        check(unsafe { cu(self.inner.handle, &mut r, CUdevResourceType::SM) })?;
126        Ok(r.as_sm())
127    }
128
129    /// Create a stream that only runs work on this green context's SMs.
130    /// Returns the raw `CUstream` — baracuda's [`crate::Stream`] requires
131    /// a full `Context`, so we return the raw handle for now.
132    pub fn create_stream_raw(&self, flags: u32, priority: i32) -> Result<CUstream> {
133        let d = driver()?;
134        let cu = d.cu_green_ctx_stream_create()?;
135        let mut s: CUstream = core::ptr::null_mut();
136        check(unsafe { cu(&mut s, self.inner.handle, flags, priority) })?;
137        Ok(s)
138    }
139
140    /// Wrap a raw green-context handle — see [`GreenContext::from_resource`]
141    /// for the normal constructor.
142    ///
143    /// # Safety
144    ///
145    /// The caller guarantees `handle` is a valid `CUgreenCtx` that baracuda
146    /// may take ownership of (destroyed on drop).
147    pub unsafe fn from_raw(handle: CUgreenCtx) -> Self {
148        Self {
149            inner: Arc::new(GreenContextInner { handle }),
150        }
151    }
152
153    /// Consume the green context and return its raw handle without
154    /// destroying it. The caller takes over the destroy responsibility.
155    pub fn into_raw(self) -> CUgreenCtx {
156        match Arc::try_unwrap(self.inner) {
157            Ok(mut inner) => {
158                let h = inner.handle;
159                inner.handle = core::ptr::null_mut();
160                h
161            }
162            Err(arc) => arc.handle,
163        }
164    }
165
166    #[inline]
167    pub fn as_raw(&self) -> CUgreenCtx {
168        self.inner.handle
169    }
170}
171
172impl Drop for GreenContextInner {
173    fn drop(&mut self) {
174        if self.handle.is_null() {
175            return;
176        }
177        if let Ok(d) = driver() {
178            if let Ok(cu) = d.cu_green_ctx_destroy() {
179                let _ = unsafe { cu(self.handle) };
180            }
181        }
182    }
183}