1 /*
2 * Copyright 2022 Google
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include <fcntl.h>
7 #include <sys/mman.h>
8 #include <unistd.h>
9 #include <xf86drm.h>
10
11 #include <cerrno>
12 #include <cstring>
13
14 #include "LinuxVirtGpu.h"
15 #include "drm-uapi/virtgpu_drm.h"
16 #include "util/log.h"
17
LinuxVirtGpuResource(int64_t deviceHandle,uint32_t blobHandle,uint32_t resourceHandle,uint64_t size)18 LinuxVirtGpuResource::LinuxVirtGpuResource(int64_t deviceHandle, uint32_t blobHandle,
19 uint32_t resourceHandle, uint64_t size)
20 : mDeviceHandle(deviceHandle),
21 mBlobHandle(blobHandle),
22 mResourceHandle(resourceHandle),
23 mSize(size) {}
24
~LinuxVirtGpuResource()25 LinuxVirtGpuResource::~LinuxVirtGpuResource() {
26 if (mBlobHandle == INVALID_DESCRIPTOR) {
27 return;
28 }
29
30 struct drm_gem_close gem_close {
31 .handle = mBlobHandle, .pad = 0,
32 };
33
34 int ret = drmIoctl(mDeviceHandle, DRM_IOCTL_GEM_CLOSE, &gem_close);
35 if (ret) {
36 mesa_loge("DRM_IOCTL_GEM_CLOSE failed with : [%s, blobHandle %u, resourceHandle: %u]",
37 strerror(errno), mBlobHandle, mResourceHandle);
38 }
39 }
40
intoRaw()41 void LinuxVirtGpuResource::intoRaw() {
42 mBlobHandle = INVALID_DESCRIPTOR;
43 mResourceHandle = INVALID_DESCRIPTOR;
44 }
45
getBlobHandle() const46 uint32_t LinuxVirtGpuResource::getBlobHandle() const { return mBlobHandle; }
47
getResourceHandle() const48 uint32_t LinuxVirtGpuResource::getResourceHandle() const { return mResourceHandle; }
49
getSize() const50 uint64_t LinuxVirtGpuResource::getSize() const { return mSize; }
51
createMapping()52 VirtGpuResourceMappingPtr LinuxVirtGpuResource::createMapping() {
53 int ret;
54 struct drm_virtgpu_map map {
55 .handle = mBlobHandle, .pad = 0,
56 };
57
58 ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_MAP, &map);
59 if (ret) {
60 mesa_loge("DRM_IOCTL_VIRTGPU_MAP failed with %s", strerror(errno));
61 return nullptr;
62 }
63
64 uint8_t* ptr = static_cast<uint8_t*>(
65 mmap64(nullptr, mSize, PROT_WRITE | PROT_READ, MAP_SHARED, mDeviceHandle, map.offset));
66
67 if (ptr == MAP_FAILED) {
68 mesa_loge("mmap64 failed with (%s)", strerror(errno));
69 return nullptr;
70 }
71
72 return std::make_shared<LinuxVirtGpuResourceMapping>(shared_from_this(), ptr, mSize);
73 }
74
exportBlob(struct VirtGpuExternalHandle & handle)75 int LinuxVirtGpuResource::exportBlob(struct VirtGpuExternalHandle& handle) {
76 int ret, fd;
77
78 uint32_t flags = DRM_CLOEXEC;
79 ret = drmPrimeHandleToFD(mDeviceHandle, mBlobHandle, flags, &fd);
80 if (ret) {
81 mesa_loge("drmPrimeHandleToFD failed with %s", strerror(errno));
82 return ret;
83 }
84
85 handle.osHandle = static_cast<int64_t>(fd);
86 handle.type = kMemHandleDmabuf;
87 return 0;
88 }
89
wait()90 int LinuxVirtGpuResource::wait() {
91 int ret;
92 struct drm_virtgpu_3d_wait wait_3d = {0};
93
94 int retry = 0;
95 do {
96 if (retry > 0 && (retry % 10 == 0)) {
97 mesa_loge("DRM_IOCTL_VIRTGPU_WAIT failed with EBUSY for %d times.", retry);
98 }
99 wait_3d.handle = mBlobHandle;
100 ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_WAIT, &wait_3d);
101 ++retry;
102 } while (ret < 0 && errno == EBUSY);
103
104 if (ret < 0) {
105 mesa_loge("DRM_IOCTL_VIRTGPU_WAIT failed with %s", strerror(errno));
106 return ret;
107 }
108
109 return 0;
110 }
111
transferToHost(uint32_t x,uint32_t y,uint32_t w,uint32_t h)112 int LinuxVirtGpuResource::transferToHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
113 int ret;
114 struct drm_virtgpu_3d_transfer_to_host xfer = {0};
115
116 xfer.box.x = x;
117 xfer.box.y = y;
118 xfer.box.w = w;
119 xfer.box.h = h;
120 xfer.box.d = 1;
121 xfer.bo_handle = mBlobHandle;
122
123 ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST, &xfer);
124 if (ret < 0) {
125 mesa_loge("DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST failed with %s", strerror(errno));
126 return ret;
127 }
128
129 return 0;
130 }
131
transferFromHost(uint32_t x,uint32_t y,uint32_t w,uint32_t h)132 int LinuxVirtGpuResource::transferFromHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
133 int ret;
134 struct drm_virtgpu_3d_transfer_from_host xfer = {0};
135
136 xfer.box.x = x;
137 xfer.box.y = y;
138 xfer.box.w = w;
139 xfer.box.h = h;
140 xfer.box.d = 1;
141 xfer.bo_handle = mBlobHandle;
142
143 ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST, &xfer);
144 if (ret < 0) {
145 mesa_loge("DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST failed with %s", strerror(errno));
146 return ret;
147 }
148
149 return 0;
150 }
151