libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_accel.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * @stability experimental
4 * Use of this source code is governed by the ISC License,
5 * a copy of which is in the file LICENSE in the top-level directory
6 * of this distribution.
7 *
8 * src/inc/xtc_accel.h
9 * Attached-compute (GPU / NPU) access -- the FIBER-PARKING BRIDGE
10 * over an async accelerator completion.
11 *
12 * SCOPE, stated up front because it is deliberately narrow. An
13 * accelerator (a discrete/integrated GPU, or an NPU / VPU) is, at
14 * the layer a concurrency runtime cares about, an ASYNC I/O DEVICE:
15 * you submit an opaque command buffer / compiled graph, it runs on
16 * a coprocessor, and it signals completion via a FENCE -- on Linux,
17 * a pollable sync_file / dma-fence fd, exactly the object epoll
18 * already waits on. That is the ONLY thing this module does: it
19 * parks a fiber on such a completion and wakes it when the device
20 * is done, deterministically-testable under the sim backend.
21 *
22 * WHAT THIS MODULE IS NOT, and never becomes:
23 * - NOT a tensor / compute library. It marshals no weights, runs
24 * no kernels, owns no model format.
25 * - NOT a device-memory allocator. Host<->device transfer and
26 * buffer lifetime are the consumer's (or the vendor runtime's).
27 * - NOT a vendor-SDK portability shim. libxtc does NOT link
28 * Level Zero / CUDA / Vulkan / OpenVINO / ONNX. The CONSUMER
29 * links whichever runtime drives the device, does the submit,
30 * and hands us the resulting fence fd (or a blocking closure).
31 * The abstraction stops at the fence. Submission and math live
32 * above us; that boundary is what keeps this a concurrency
33 * primitive and not a decades-deep accelerator-runtime tarpit.
34 *
35 * GPU AND NPU ARE ONE ABSTRACTION HERE. On Linux both are DRM
36 * devices (/dev/dri/renderD* for a render GPU, /dev/accel/accel*
37 * for an NPU via the accel subsystem) whose completions are the
38 * SAME kernel object: a dma-fence exported to a sync_file fd. A
39 * GPU fence fd and an NPU fence fd are indistinguishable to poll(2),
40 * so they are indistinguishable to us. The device KIND
41 * (xtc_accel_kind_t) is a stats/observability tag only, never a
42 * behavioral branch.
43 *
44 * TWO WAYS TO WAIT (pick per submission):
45 * 1. FENCE-FD (preferred, no OS thread held): the consumer's
46 * runtime submits work and returns a completion fence exported
47 * as an fd (Level Zero event/fence -> sync_file,
48 * drmSyncobjExportSyncFile, VK_KHR_external_fence_fd, a CUDA
49 * event bridged to an eventfd, ...). xtc_accel_wait_fence
50 * parks the calling fiber on that fd via the normal loop
51 * readiness path and resumes it when the fence signals.
52 * 2. BLOCKING FALLBACK (a whole OS thread parked for the call):
53 * when a runtime exposes only a synchronous "run and block"
54 * call and no pollable fence, xtc_accel_run_blocking routes it
55 * through the xtc_blocking thread pool -- correct, but holds a
56 * pool thread for the duration, so it suits coarse-grained
57 * inference, not thousands of tiny ops/sec.
58 *
59 * AVAILABILITY. When the build was configured without accelerator
60 * support (XTC_HAVE_ACCEL undefined -- no DRM/accel present, or
61 * --without-accel), the probe reports zero devices and the fence
62 * wait returns XTC_E_NOSYS, following the same "always linkable,
63 * NOSYS when unsupported" convention as the TLS and crypto modules.
64 * xtc_accel_run_blocking works regardless (it needs no device
65 * support -- it just runs the consumer's closure on the pool).
66 */
67
68#ifndef XTC_ACCEL_H
69#define XTC_ACCEL_H
70
71#include "xtc_export.h"
72
73#include <stddef.h>
74#include <stdint.h>
75
76#include "xtc.h"
77
78/* Device kind -- an OBSERVABILITY TAG only (see the header comment); it
79 * never changes how a fence is waited on. */
80typedef enum xtc_accel_kind {
81 XTC_ACCEL_KIND_UNKNOWN = 0,
82 XTC_ACCEL_KIND_GPU, /* render node (/dev/dri/renderD*) */
83 XTC_ACCEL_KIND_NPU /* accel subsystem (/dev/accel/accel*) */
84} xtc_accel_kind_t;
85
86/* One discovered device. `name` is a short human/stat label (e.g.
87 * "renderD128", "accel0"); `node` is the device node path. `driver`
88 * is the kernel driver name if cheaply known (e.g. "xe", "intel_vpu"),
89 * else "". These are for selection/observability by the consumer and
90 * its runtime -- libxtc itself opens none of them. */
91typedef struct xtc_accel_dev {
92 xtc_accel_kind_t kind;
93 char name[32];
94 char node[64];
95 char driver[32];
97
98/*
99 * PUBLIC: int xtc_accel_probe __P((xtc_accel_dev_t *, int, int *));
100 *
101 * Enumerate accelerator devices present on the host into out[0..max),
102 * writing the total count found to *out_n (which may exceed max if the
103 * buffer was too small -- only the first max are written). Returns
104 * XTC_OK on success (including zero devices), XTC_E_INVAL on a bad
105 * argument. When the build lacks accelerator support this reports
106 * zero devices and returns XTC_OK (there is nothing to enumerate;
107 * absence is not an error). Pure discovery -- opens/submits nothing.
108 */
109XTC_API int xtc_accel_probe(xtc_accel_dev_t *out, int max, int *out_n);
110
111/*
112 * PUBLIC: int xtc_accel_wait_fence __P((int, int64_t));
113 *
114 * Park the CALLING FIBER until the completion fence `fence_fd` (a
115 * pollable sync_file / dma-fence / eventfd the consumer's runtime
116 * produced when it submitted work) signals, or `timeout_ns` elapses
117 * (< 0 = wait forever). On the fence signalling, resumes the fiber
118 * and returns XTC_OK. Returns XTC_E_AGAIN on timeout (the fence never
119 * fired), XTC_E_INVAL on a bad fd, XTC_E_NOSYS when the build lacks
120 * accelerator support.
121 *
122 * libxtc does NOT own, dup, or close `fence_fd` -- the caller retains
123 * ownership and closes it after this returns. This must be called
124 * from within a fiber (it parks); calling it off a loop is an error.
125 * It holds NO OS thread while waiting (that is the point vs
126 * run_blocking).
127 */
128XTC_API int xtc_accel_wait_fence(int fence_fd, int64_t timeout_ns);
129
130/*
131 * PUBLIC: int xtc_accel_run_blocking __P((int (*)(void *), void *, int *));
132 *
133 * Run a synchronous accelerator call `fn(arg)` on the xtc_blocking
134 * thread pool, parking the calling fiber until it returns; the fiber
135 * resumes with fn's return value in *out_result (may be NULL). For
136 * runtimes that expose only a blocking submit-and-wait with no
137 * pollable fence. Returns XTC_OK (fn ran; see *out_result), or the
138 * xtc_blocking error if the work could not be dispatched. Holds one
139 * pool thread for the duration of fn -- prefer xtc_accel_wait_fence
140 * when a fence fd is available. Works regardless of XTC_HAVE_ACCEL
141 * (it needs no device support -- fn is the consumer's own closure).
142 */
143XTC_API int xtc_accel_run_blocking(int (*fn)(void *), void *arg,
144 int *out_result);
145
146#endif /* XTC_ACCEL_H */