Skip to content

Commit ffa659f

Browse files
committed
virtgpu: start integrating virt-gpu code
1 parent 5049b2f commit ffa659f

File tree

2 files changed

+501
-0
lines changed

2 files changed

+501
-0
lines changed
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
#include <stdio.h>
2+
#include <cassert>
3+
#include <cerrno>
4+
5+
#include "virtgpu.h"
6+
7+
static inline void
8+
virtgpu_init_shmem_blob_mem(struct virtgpu *gpu)
9+
{
10+
/* VIRTGPU_BLOB_MEM_GUEST allocates from the guest system memory. They are
11+
* logically contiguous in the guest but are sglists (iovecs) in the host.
12+
* That makes them slower to process in the host. With host process
13+
* isolation, it also becomes impossible for the host to access sglists
14+
* directly.
15+
*
16+
* While there are ideas (and shipped code in some cases) such as creating
17+
* udmabufs from sglists, or having a dedicated guest heap, it seems the
18+
* easiest way is to reuse VIRTGPU_BLOB_MEM_HOST3D. That is, when the
19+
* renderer sees a request to export a blob where
20+
*
21+
* - blob_mem is VIRTGPU_BLOB_MEM_HOST3D
22+
* - blob_flags is VIRTGPU_BLOB_FLAG_USE_MAPPABLE
23+
* - blob_id is 0
24+
*
25+
* it allocates a host shmem.
26+
*
27+
* supports_blob_id_0 has been enforced by mandated render server config.
28+
*/
29+
assert(gpu->capset.data.supports_blob_id_0);
30+
gpu->shmem_blob_mem = VIRTGPU_BLOB_MEM_HOST3D;
31+
}
32+
33+
void
34+
create_virtgpu() {
35+
struct virtgpu *gpu = new struct virtgpu();
36+
37+
VkResult result = virtgpu_open(gpu);
38+
GGML_ASSERT(result == VK_SUCCESS);
39+
40+
result = virtgpu_init_params(gpu);
41+
GGML_ASSERT(result == VK_SUCCESS);
42+
43+
result = virtgpu_init_capset(gpu);
44+
GGML_ASSERT(result == VK_SUCCESS);
45+
46+
result = virtgpu_init_context(gpu);
47+
GGML_ASSERT(result == VK_SUCCESS);
48+
49+
virtgpu_init_shmem_blob_mem(gpu);
50+
}
51+
52+
static VkResult
53+
virtgpu_open(struct virtgpu *gpu)
54+
{
55+
drmDevicePtr devs[8];
56+
int count = drmGetDevices2(0, devs, ARRAY_SIZE(devs));
57+
if (count < 0) {
58+
INFO("failed to enumerate DRM devices");
59+
return VK_ERROR_INITIALIZATION_FAILED;
60+
}
61+
62+
VkResult result = VK_ERROR_INITIALIZATION_FAILED;
63+
for (int i = 0; i < count; i++) {
64+
result = virtgpu_open_device(gpu, devs[i]);
65+
if (result == VK_SUCCESS)
66+
break;
67+
}
68+
69+
drmFreeDevices(devs, count);
70+
71+
return result;
72+
}
73+
74+
static VkResult
75+
virtgpu_open_device(struct virtgpu *gpu, const drmDevicePtr dev)
76+
{
77+
bool supported_bus = false;
78+
79+
switch (dev->bustype) {
80+
case DRM_BUS_PCI:
81+
if (dev->deviceinfo.pci->vendor_id == VIRTGPU_PCI_VENDOR_ID &&
82+
dev->deviceinfo.pci->device_id == VIRTGPU_PCI_DEVICE_ID)
83+
supported_bus = true;
84+
break;
85+
case DRM_BUS_PLATFORM:
86+
supported_bus = true;
87+
break;
88+
default:
89+
break;
90+
}
91+
92+
if (!supported_bus || !(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
93+
if (VN_DEBUG(INIT)) {
94+
const char *name = "unknown";
95+
for (uint32_t i = 0; i < DRM_NODE_MAX; i++) {
96+
if (dev->available_nodes & (1 << i)) {
97+
name = dev->nodes[i];
98+
break;
99+
}
100+
}
101+
vn_log(gpu->instance, "skipping DRM device %s", name);
102+
}
103+
return VK_ERROR_INITIALIZATION_FAILED;
104+
}
105+
106+
const char *primary_path = dev->nodes[DRM_NODE_PRIMARY];
107+
const char *node_path = dev->nodes[DRM_NODE_RENDER];
108+
109+
int fd = open(node_path, O_RDWR | O_CLOEXEC);
110+
if (fd < 0) {
111+
if (VN_DEBUG(INIT))
112+
vn_log(gpu->instance, "failed to open %s", node_path);
113+
return VK_ERROR_INITIALIZATION_FAILED;
114+
}
115+
116+
drmVersionPtr version = drmGetVersion(fd);
117+
if (!version || strcmp(version->name, "virtio_gpu") ||
118+
version->version_major != 0) {
119+
if (VN_DEBUG(INIT)) {
120+
if (version) {
121+
vn_log(gpu->instance, "unknown DRM driver %s version %d",
122+
version->name, version->version_major);
123+
} else {
124+
vn_log(gpu->instance, "failed to get DRM driver version");
125+
}
126+
}
127+
if (version)
128+
drmFreeVersion(version);
129+
close(fd);
130+
return VK_ERROR_INITIALIZATION_FAILED;
131+
}
132+
133+
gpu->fd = fd;
134+
135+
struct stat st;
136+
if (stat(primary_path, &st) == 0) {
137+
gpu->has_primary = true;
138+
gpu->primary_major = major(st.st_rdev);
139+
gpu->primary_minor = minor(st.st_rdev);
140+
} else {
141+
gpu->has_primary = false;
142+
gpu->primary_major = 0;
143+
gpu->primary_minor = 0;
144+
}
145+
stat(node_path, &st);
146+
gpu->render_major = major(st.st_rdev);
147+
gpu->render_minor = minor(st.st_rdev);
148+
149+
gpu->bustype = dev->bustype;
150+
if (dev->bustype == DRM_BUS_PCI)
151+
gpu->pci_bus_info = *dev->businfo.pci;
152+
153+
drmFreeVersion(version);
154+
155+
if (VN_DEBUG(INIT))
156+
vn_log(gpu->instance, "using DRM device %s", node_path);
157+
158+
return VK_SUCCESS;
159+
}
160+
161+
void
162+
vn_log(struct remoting_dev_instance *instance, const char *format, ...)
163+
{
164+
if (instance) {
165+
printf("<INST>");
166+
}
167+
168+
va_list ap;
169+
170+
va_start(ap, format);
171+
vprintf(format, ap);
172+
va_end(ap);
173+
174+
/* instance may be NULL or partially initialized */
175+
}
176+
177+
178+
179+
static VkResult
180+
virtgpu_init_context(struct virtgpu *gpu)
181+
{
182+
assert(!gpu->capset.version);
183+
const int ret = virtgpu_ioctl_context_init(gpu, gpu->capset.id);
184+
if (ret) {
185+
if (VN_DEBUG(INIT)) {
186+
vn_log(gpu->instance, "failed to initialize context: %s",
187+
strerror(errno));
188+
}
189+
return VK_ERROR_INITIALIZATION_FAILED;
190+
}
191+
192+
return VK_SUCCESS;
193+
}
194+
195+
static VkResult
196+
virtgpu_init_capset(struct virtgpu *gpu)
197+
{
198+
gpu->capset.id = VIRGL_RENDERER_CAPSET_VENUS;
199+
gpu->capset.version = 0;
200+
201+
const int ret =
202+
virtgpu_ioctl_get_caps(gpu, gpu->capset.id, gpu->capset.version,
203+
&gpu->capset.data, sizeof(gpu->capset.data));
204+
if (ret) {
205+
if (VN_DEBUG(INIT)) {
206+
vn_log(gpu->instance, "failed to get venus v%d capset: %s",
207+
gpu->capset.version, strerror(errno));
208+
}
209+
return VK_ERROR_INITIALIZATION_FAILED;
210+
}
211+
212+
return VK_SUCCESS;
213+
}
214+
215+
static VkResult
216+
virtgpu_init_params(struct virtgpu *gpu)
217+
{
218+
const uint64_t required_params[] = {
219+
VIRTGPU_PARAM_3D_FEATURES, VIRTGPU_PARAM_CAPSET_QUERY_FIX,
220+
VIRTGPU_PARAM_RESOURCE_BLOB, VIRTGPU_PARAM_CONTEXT_INIT,
221+
};
222+
uint64_t val;
223+
for (uint32_t i = 0; i < ARRAY_SIZE(required_params); i++) {
224+
val = virtgpu_ioctl_getparam(gpu, required_params[i]);
225+
if (!val) {
226+
if (VN_DEBUG(INIT)) {
227+
vn_log(gpu->instance, "required kernel param %d is missing",
228+
(int)required_params[i]);
229+
}
230+
return VK_ERROR_INITIALIZATION_FAILED;
231+
}
232+
}
233+
234+
val = virtgpu_ioctl_getparam(gpu, VIRTGPU_PARAM_HOST_VISIBLE);
235+
if (val) {
236+
gpu->bo_blob_mem = VIRTGPU_BLOB_MEM_HOST3D;
237+
} else {
238+
val = virtgpu_ioctl_getparam(gpu, VIRTGPU_PARAM_GUEST_VRAM);
239+
if (val) {
240+
gpu->bo_blob_mem = VIRTGPU_BLOB_MEM_GUEST_VRAM;
241+
}
242+
}
243+
244+
if (!val) {
245+
vn_log(gpu->instance,
246+
"one of required kernel params (%d or %d) is missing",
247+
(int)VIRTGPU_PARAM_HOST_VISIBLE, (int)VIRTGPU_PARAM_GUEST_VRAM);
248+
return VK_ERROR_INITIALIZATION_FAILED;
249+
}
250+
251+
/* Cross-device feature is optional. It enables sharing dma-bufs
252+
* with other virtio devices, like virtio-wl or virtio-video used
253+
* by ChromeOS VMs. Qemu doesn't support cross-device sharing.
254+
*/
255+
val = virtgpu_ioctl_getparam(gpu, VIRTGPU_PARAM_CROSS_DEVICE);
256+
if (val)
257+
gpu->supports_cross_device = true;
258+
259+
/* implied by CONTEXT_INIT uapi */
260+
gpu->max_timeline_count = 64;
261+
262+
return VK_SUCCESS;
263+
}
264+
265+
266+
static int
267+
virtgpu_ioctl(struct virtgpu *gpu, unsigned long request, void *args)
268+
{
269+
return drmIoctl(gpu->fd, request, args);
270+
}
271+
272+
static int
273+
virtgpu_ioctl_context_init(struct virtgpu *gpu,
274+
enum virgl_renderer_capset capset_id)
275+
{
276+
struct drm_virtgpu_context_set_param ctx_set_params[3] = {
277+
{
278+
.param = VIRTGPU_CONTEXT_PARAM_CAPSET_ID,
279+
.value = capset_id,
280+
},
281+
{
282+
.param = VIRTGPU_CONTEXT_PARAM_NUM_RINGS,
283+
.value = 64,
284+
},
285+
{
286+
.param = VIRTGPU_CONTEXT_PARAM_POLL_RINGS_MASK,
287+
.value = 0, /* don't generate drm_events on fence signaling */
288+
},
289+
};
290+
291+
struct drm_virtgpu_context_init args = {
292+
.num_params = ARRAY_SIZE(ctx_set_params),
293+
.pad = 0,
294+
.ctx_set_params = (uintptr_t)&ctx_set_params,
295+
};
296+
297+
return virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_CONTEXT_INIT, &args);
298+
}
299+
300+
static int
301+
virtgpu_ioctl_get_caps(struct virtgpu *gpu,
302+
enum virgl_renderer_capset id,
303+
uint32_t version,
304+
void *capset,
305+
size_t capset_size)
306+
{
307+
struct drm_virtgpu_get_caps args = {
308+
.cap_set_id = id,
309+
.cap_set_ver = version,
310+
.addr = (uintptr_t)capset,
311+
.size = (__u32) capset_size,
312+
.pad = 0,
313+
};
314+
315+
return virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_GET_CAPS, &args);
316+
}
317+
318+
static uint64_t
319+
virtgpu_ioctl_getparam(struct virtgpu *gpu, uint64_t param)
320+
{
321+
/* val must be zeroed because kernel only writes the lower 32 bits */
322+
uint64_t val = 0;
323+
struct drm_virtgpu_getparam args = {
324+
.param = param,
325+
.value = (uintptr_t)&val,
326+
};
327+
328+
const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_GETPARAM, &args);
329+
return ret ? 0 : val;
330+
}

0 commit comments

Comments
 (0)