| 1 | |
| 2 | * Copyright 2014 Canonical
|
| 3 | *
|
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a
|
| 5 | * copy of this software and associated documentation files (the "Software"),
|
| 6 | * to deal in the Software without restriction, including without limitation
|
| 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
| 8 | * and/or sell copies of the Software, and to permit persons to whom the
|
| 9 | * Software is furnished to do so, subject to the following conditions:
|
| 10 | *
|
| 11 | * The above copyright notice and this permission notice shall be included in
|
| 12 | * all copies or substantial portions of the Software.
|
| 13 | *
|
| 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
| 17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
| 18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
| 19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
| 20 | * OTHER DEALINGS IN THE SOFTWARE.
|
| 21 | *
|
| 22 | * Authors: Andreas Pokorny
|
| 23 |
|
| 24 |
|
| 25 | #include <drm/drm_prime.h>
|
| 26 | #include <drm/drm_print.h>
|
| 27 | #include <linux/virtio_dma_buf.h>
|
| 28 |
|
| 29 | #include "virtgpu_drv.h"
|
| 30 |
|
| 31 | MODULE_IMPORT_NS("DMA_BUF");
|
| 32 |
|
| 33 | static int virtgpu_virtio_get_uuid(struct dma_buf *buf,
|
| 34 | uuid_t *uuid)
|
| 35 | {
|
| 36 | struct drm_gem_object *obj = buf->priv;
|
| 37 | struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
|
| 38 | struct virtio_gpu_device *vgdev = obj->dev->dev_private;
|
| 39 |
|
| 40 | wait_event(vgdev->resp_wq, bo->uuid_state != STATE_INITIALIZING);
|
| 41 | if (bo->uuid_state != STATE_OK)
|
| 42 | return -ENODEV;
|
| 43 |
|
| 44 | uuid_copy(uuid, &bo->uuid);
|
| 45 |
|
| 46 | return 0;
|
| 47 | }
|
| 48 |
|
| 49 | static struct sg_table *
|
| 50 | virtgpu_gem_map_dma_buf(struct dma_buf_attachment *attach,
|
| 51 | enum dma_data_direction dir)
|
| 52 | {
|
| 53 | struct drm_gem_object *obj = attach->dmabuf->priv;
|
| 54 | struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
|
| 55 |
|
| 56 | if (virtio_gpu_is_vram(bo))
|
| 57 | return virtio_gpu_vram_map_dma_buf(bo, attach->dev, dir);
|
| 58 |
|
| 59 | return drm_gem_map_dma_buf(attach, dir);
|
| 60 | }
|
| 61 |
|
| 62 | static void virtgpu_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
|
| 63 | struct sg_table *sgt,
|
| 64 | enum dma_data_direction dir)
|
| 65 | {
|
| 66 | struct drm_gem_object *obj = attach->dmabuf->priv;
|
| 67 | struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
|
| 68 |
|
| 69 | if (virtio_gpu_is_vram(bo)) {
|
| 70 | virtio_gpu_vram_unmap_dma_buf(attach->dev, sgt, dir);
|
| 71 | return;
|
| 72 | }
|
| 73 |
|
| 74 | drm_gem_unmap_dma_buf(attach, sgt, dir);
|
| 75 | }
|
| 76 |
|
| 77 | static const struct virtio_dma_buf_ops virtgpu_dmabuf_ops = {
|
| 78 | .ops = {
|
| 79 | .attach = virtio_dma_buf_attach,
|
| 80 | .detach = drm_gem_map_detach,
|
| 81 | .map_dma_buf = virtgpu_gem_map_dma_buf,
|
| 82 | .unmap_dma_buf = virtgpu_gem_unmap_dma_buf,
|
| 83 | .release = drm_gem_dmabuf_release,
|
| 84 | .mmap = drm_gem_dmabuf_mmap,
|
| 85 | .vmap = drm_gem_dmabuf_vmap,
|
| 86 | .vunmap = drm_gem_dmabuf_vunmap,
|
| 87 | },
|
| 88 | .device_attach = drm_gem_map_attach,
|
| 89 | .get_uuid = virtgpu_virtio_get_uuid,
|
| 90 | };
|
| 91 |
|
| 92 | int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev,
|
| 93 | struct virtio_gpu_object *bo)
|
| 94 | {
|
| 95 | struct virtio_gpu_object_array *objs;
|
| 96 |
|
| 97 | objs = virtio_gpu_array_alloc(1);
|
| 98 | if (!objs)
|
| 99 | return -ENOMEM;
|
| 100 |
|
| 101 | virtio_gpu_array_add_obj(objs, &bo->base.base);
|
| 102 |
|
| 103 | return virtio_gpu_cmd_resource_assign_uuid(vgdev, objs);
|
| 104 | }
|
| 105 |
|
| 106 | struct dma_buf *virtgpu_gem_prime_export(struct drm_gem_object *obj,
|
| 107 | int flags)
|
| 108 | {
|
| 109 | struct dma_buf *buf;
|
| 110 | struct drm_device *dev = obj->dev;
|
| 111 | struct virtio_gpu_device *vgdev = dev->dev_private;
|
| 112 | struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
|
| 113 | int ret = 0;
|
| 114 | bool blob = bo->host3d_blob || bo->guest_blob;
|
| 115 | DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
| 116 |
|
| 117 | if (!blob) {
|
| 118 | if (vgdev->has_resource_assign_uuid) {
|
| 119 | ret = virtio_gpu_resource_assign_uuid(vgdev, bo);
|
| 120 | if (ret)
|
| 121 | return ERR_PTR(ret);
|
| 122 |
|
| 123 | virtio_gpu_notify(vgdev);
|
| 124 | } else {
|
| 125 | bo->uuid_state = STATE_ERR;
|
| 126 | }
|
| 127 | } else if (!(bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE)) {
|
| 128 | bo->uuid_state = STATE_ERR;
|
| 129 | }
|
| 130 |
|
| 131 | exp_info.ops = &virtgpu_dmabuf_ops.ops;
|
| 132 | exp_info.size = obj->size;
|
| 133 | exp_info.flags = flags;
|
| 134 | exp_info.priv = obj;
|
| 135 | exp_info.resv = obj->resv;
|
| 136 |
|
| 137 | buf = virtio_dma_buf_export(&exp_info);
|
| 138 | if (IS_ERR(buf))
|
| 139 | return buf;
|
| 140 |
|
| 141 | drm_dev_get(dev);
|
| 142 | drm_gem_object_get(obj);
|
| 143 |
|
| 144 | return buf;
|
| 145 | }
|
| 146 |
|
| 147 | int virtgpu_dma_buf_import_sgt(struct virtio_gpu_mem_entry **ents,
|
| 148 | unsigned int *nents,
|
| 149 | struct virtio_gpu_object *bo,
|
| 150 | struct dma_buf_attachment *attach)
|
| 151 | {
|
| 152 | struct scatterlist *sl;
|
| 153 | struct sg_table *sgt;
|
| 154 | long i, ret;
|
| 155 |
|
| 156 | dma_resv_assert_held(attach->dmabuf->resv);
|
| 157 |
|
| 158 | ret = dma_resv_wait_timeout(attach->dmabuf->resv,
|
| 159 | DMA_RESV_USAGE_KERNEL,
|
| 160 | false, MAX_SCHEDULE_TIMEOUT);
|
| 161 | if (ret <= 0)
|
| 162 | return ret < 0 ? ret : -ETIMEDOUT;
|
| 163 |
|
| 164 | sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
|
| 165 | if (IS_ERR(sgt))
|
| 166 | return PTR_ERR(sgt);
|
| 167 |
|
| 168 | *ents = kvmalloc_objs(struct virtio_gpu_mem_entry, sgt->nents);
|
| 169 | if (!(*ents)) {
|
| 170 | dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
|
| 171 | return -ENOMEM;
|
| 172 | }
|
| 173 |
|
| 174 | *nents = sgt->nents;
|
| 175 | for_each_sgtable_dma_sg(sgt, sl, i) {
|
| 176 | (*ents)[i].addr = cpu_to_le64(sg_dma_address(sl));
|
| 177 | (*ents)[i].length = cpu_to_le32(sg_dma_len(sl));
|
| 178 | (*ents)[i].padding = 0;
|
| 179 | }
|
| 180 |
|
| 181 | bo->sgt = sgt;
|
| 182 | return 0;
|
| 183 | }
|
| 184 |
|
| 185 | static void virtgpu_dma_buf_unmap(struct virtio_gpu_object *bo)
|
| 186 | {
|
| 187 | struct dma_buf_attachment *attach = bo->base.base.import_attach;
|
| 188 |
|
| 189 | dma_resv_assert_held(attach->dmabuf->resv);
|
| 190 |
|
| 191 | if (bo->created) {
|
| 192 | virtio_gpu_detach_object_fenced(bo);
|
| 193 |
|
| 194 | if (bo->sgt)
|
| 195 | dma_buf_unmap_attachment(attach, bo->sgt,
|
| 196 | DMA_BIDIRECTIONAL);
|
| 197 |
|
| 198 | bo->sgt = NULL;
|
| 199 | }
|
| 200 | }
|
| 201 |
|
| 202 | static void virtgpu_dma_buf_free_obj(struct drm_gem_object *obj)
|
| 203 | {
|
| 204 | struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
|
| 205 | struct virtio_gpu_device *vgdev = obj->dev->dev_private;
|
| 206 | struct dma_buf_attachment *attach = obj->import_attach;
|
| 207 |
|
| 208 | if (drm_gem_is_imported(obj)) {
|
| 209 | struct dma_buf *dmabuf = attach->dmabuf;
|
| 210 |
|
| 211 | dma_resv_lock(dmabuf->resv, NULL);
|
| 212 | virtgpu_dma_buf_unmap(bo);
|
| 213 | dma_resv_unlock(dmabuf->resv);
|
| 214 |
|
| 215 | dma_buf_detach(dmabuf, attach);
|
| 216 | dma_buf_put(dmabuf);
|
| 217 | }
|
| 218 |
|
| 219 | if (bo->created) {
|
| 220 | virtio_gpu_remove_from_restore_list(bo);
|
| 221 | virtio_gpu_cmd_unref_resource(vgdev, bo, false);
|
| 222 | virtio_gpu_notify(vgdev);
|
| 223 | return;
|
| 224 | }
|
| 225 | virtio_gpu_cleanup_object(bo);
|
| 226 | }
|
| 227 |
|
| 228 | static int virtgpu_dma_buf_init_obj(struct drm_device *dev,
|
| 229 | struct virtio_gpu_object *bo,
|
| 230 | struct dma_buf_attachment *attach)
|
| 231 | {
|
| 232 | struct virtio_gpu_device *vgdev = dev->dev_private;
|
| 233 | struct virtio_gpu_object_params params = { 0 };
|
| 234 | struct dma_resv *resv = attach->dmabuf->resv;
|
| 235 | struct virtio_gpu_mem_entry *ents = NULL;
|
| 236 | unsigned int nents;
|
| 237 | int ret;
|
| 238 |
|
| 239 | ret = virtio_gpu_resource_id_get(vgdev, &bo->hw_res_handle);
|
| 240 | if (ret) {
|
| 241 | virtgpu_dma_buf_free_obj(&bo->base.base);
|
| 242 | return ret;
|
| 243 | }
|
| 244 |
|
| 245 | dma_resv_lock(resv, NULL);
|
| 246 |
|
| 247 | ret = dma_buf_pin(attach);
|
| 248 | if (ret)
|
| 249 | goto err_pin;
|
| 250 |
|
| 251 | ret = virtgpu_dma_buf_import_sgt(&ents, &nents, bo, attach);
|
| 252 | if (ret)
|
| 253 | goto err_import;
|
| 254 |
|
| 255 | params.blob = true;
|
| 256 | params.blob_mem = VIRTGPU_BLOB_MEM_GUEST;
|
| 257 | params.blob_flags = VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
|
| 258 | params.size = attach->dmabuf->size;
|
| 259 |
|
| 260 | virtio_gpu_cmd_resource_create_blob(vgdev, bo, ¶ms,
|
| 261 | ents, nents);
|
| 262 | bo->guest_blob = true;
|
| 263 |
|
| 264 | dma_buf_unpin(attach);
|
| 265 | dma_resv_unlock(resv);
|
| 266 |
|
| 267 | |
| 268 | * Store the dmabuf imported object with its params to the
|
| 269 | * restore list.
|
| 270 |
|
| 271 | bo->params = params;
|
| 272 | virtio_gpu_add_object_to_restore_list(vgdev, bo);
|
| 273 |
|
| 274 | return 0;
|
| 275 |
|
| 276 | err_import:
|
| 277 | dma_buf_unpin(attach);
|
| 278 | err_pin:
|
| 279 | dma_resv_unlock(resv);
|
| 280 | virtgpu_dma_buf_free_obj(&bo->base.base);
|
| 281 | return ret;
|
| 282 | }
|
| 283 |
|
| 284 | int virtgpu_dma_buf_obj_resubmit(struct virtio_gpu_device *vgdev,
|
| 285 | struct virtio_gpu_object *bo)
|
| 286 | {
|
| 287 | struct virtio_gpu_mem_entry *ents;
|
| 288 | struct scatterlist *sl;
|
| 289 | int i;
|
| 290 |
|
| 291 | if (!bo->sgt) {
|
| 292 | DRM_ERROR("no sgt bound to virtio_gpu_object\n");
|
| 293 | return -ENOMEM;
|
| 294 | }
|
| 295 |
|
| 296 | ents = kvmalloc_objs(struct virtio_gpu_mem_entry, bo->sgt->nents);
|
| 297 | if (!ents) {
|
| 298 | DRM_ERROR("failed to allocate ent list\n");
|
| 299 | return -ENOMEM;
|
| 300 | }
|
| 301 |
|
| 302 | for_each_sgtable_dma_sg(bo->sgt, sl, i) {
|
| 303 | ents[i].addr = cpu_to_le64(sg_dma_address(sl));
|
| 304 | ents[i].length = cpu_to_le32(sg_dma_len(sl));
|
| 305 | ents[i].padding = 0;
|
| 306 | }
|
| 307 |
|
| 308 | virtio_gpu_cmd_resource_create_blob(vgdev, bo, &bo->params,
|
| 309 | ents, bo->sgt->nents);
|
| 310 |
|
| 311 | return 0;
|
| 312 | }
|
| 313 |
|
| 314 | static const struct drm_gem_object_funcs virtgpu_gem_dma_buf_funcs = {
|
| 315 | .free = virtgpu_dma_buf_free_obj,
|
| 316 | };
|
| 317 |
|
| 318 | static void virtgpu_dma_buf_move_notify(struct dma_buf_attachment *attach)
|
| 319 | {
|
| 320 | struct drm_gem_object *obj = attach->importer_priv;
|
| 321 | struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
|
| 322 |
|
| 323 | virtgpu_dma_buf_unmap(bo);
|
| 324 | }
|
| 325 |
|
| 326 | static const struct dma_buf_attach_ops virtgpu_dma_buf_attach_ops = {
|
| 327 | .allow_peer2peer = true,
|
| 328 | .invalidate_mappings = virtgpu_dma_buf_move_notify
|
| 329 | };
|
| 330 |
|
| 331 | struct drm_gem_object *virtgpu_gem_prime_import(struct drm_device *dev,
|
| 332 | struct dma_buf *buf)
|
| 333 | {
|
| 334 | struct virtio_gpu_device *vgdev = dev->dev_private;
|
| 335 | struct dma_buf_attachment *attach;
|
| 336 | struct virtio_gpu_object *bo;
|
| 337 | struct drm_gem_object *obj;
|
| 338 | int ret;
|
| 339 |
|
| 340 | if (buf->ops == &virtgpu_dmabuf_ops.ops) {
|
| 341 | obj = buf->priv;
|
| 342 | if (obj->dev == dev) {
|
| 343 | |
| 344 | * Importing dmabuf exported from our own gem increases
|
| 345 | * refcount on gem itself instead of f_count of dmabuf.
|
| 346 |
|
| 347 | drm_gem_object_get(obj);
|
| 348 | return obj;
|
| 349 | }
|
| 350 | }
|
| 351 |
|
| 352 | if (!vgdev->has_resource_blob || vgdev->has_virgl_3d)
|
| 353 | return drm_gem_prime_import(dev, buf);
|
| 354 |
|
| 355 | bo = kzalloc_obj(*bo);
|
| 356 | if (!bo)
|
| 357 | return ERR_PTR(-ENOMEM);
|
| 358 |
|
| 359 | INIT_LIST_HEAD(&bo->restore_node);
|
| 360 |
|
| 361 | obj = &bo->base.base;
|
| 362 | obj->resv = buf->resv;
|
| 363 | obj->funcs = &virtgpu_gem_dma_buf_funcs;
|
| 364 | drm_gem_private_object_init(dev, obj, buf->size);
|
| 365 |
|
| 366 | attach = dma_buf_dynamic_attach(buf, dev->dev,
|
| 367 | &virtgpu_dma_buf_attach_ops, obj);
|
| 368 | if (IS_ERR(attach)) {
|
| 369 | kfree(bo);
|
| 370 | return ERR_CAST(attach);
|
| 371 | }
|
| 372 |
|
| 373 | obj->import_attach = attach;
|
| 374 | get_dma_buf(buf);
|
| 375 |
|
| 376 | ret = virtgpu_dma_buf_init_obj(dev, bo, attach);
|
| 377 | if (ret < 0)
|
| 378 | return ERR_PTR(ret);
|
| 379 |
|
| 380 | return obj;
|
| 381 | }
|
| 382 |
|
| 383 | struct drm_gem_object *virtgpu_gem_prime_import_sg_table(
|
| 384 | struct drm_device *dev, struct dma_buf_attachment *attach,
|
| 385 | struct sg_table *table)
|
| 386 | {
|
| 387 | return ERR_PTR(-ENODEV);
|
| 388 | }
|