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