// SPDX-License-Identifier: GPL-2.0 /* * Support for using dma-bufs. * * Copyright (C) 2022 Google LLC */ #include #include #include #include #include "gxp-dma.h" #include "gxp-dmabuf.h" #include "gxp-vd.h" struct gxp_dmabuf_mapping { struct gxp_mapping mapping; struct dma_buf *dmabuf; struct dma_buf_attachment *attachment; /* * For normal mappings, the `sg_table` is embedded directly in the * `gxp_mapping` and populated by `sg_alloc_table_from_pages()`. * For dma-bufs however, a pointer to the `sg_table` is returned by * `dma_buf_map_attachment()`. * * Rather than manage the memory of `gxp_mapping`'s `sg_table` * independently so it can contain a pointer, dma-bufs store their * `sg_table` pointer here and ignore `mapping->sgt`. */ struct sg_table *sgt; }; /* Mapping destructor for gxp_mapping_put() to call */ static void destroy_dmabuf_mapping(struct gxp_mapping *mapping) { struct gxp_dmabuf_mapping *dmabuf_mapping; struct gxp_dev *gxp = mapping->gxp; /* Unmap and detach the dma-buf */ dmabuf_mapping = container_of(mapping, struct gxp_dmabuf_mapping, mapping); gxp_dma_unmap_dmabuf_attachment(gxp, mapping->domain, dmabuf_mapping->attachment, dmabuf_mapping->sgt, mapping->dir); dma_buf_detach(dmabuf_mapping->dmabuf, dmabuf_mapping->attachment); dma_buf_put(dmabuf_mapping->dmabuf); kfree(dmabuf_mapping); } struct gxp_mapping *gxp_dmabuf_map(struct gxp_dev *gxp, struct gcip_iommu_domain *domain, int fd, u32 flags, enum dma_data_direction dir) { struct dma_buf *dmabuf; struct dma_buf_attachment *attachment; struct sg_table *sgt; struct gxp_dmabuf_mapping *dmabuf_mapping; int ret = 0; if (!valid_dma_direction(dir)) return ERR_PTR(-EINVAL); dmabuf = dma_buf_get(fd); if (IS_ERR(dmabuf)) { dev_err(gxp->dev, "Failed to get dma-buf to map (ret=%ld)\n", PTR_ERR(dmabuf)); return ERR_CAST(dmabuf); } attachment = dma_buf_attach(dmabuf, gxp->dev); if (IS_ERR(attachment)) { dev_err(gxp->dev, "Failed to attach dma-buf to map (ret=%ld)\n", PTR_ERR(attachment)); ret = PTR_ERR(attachment); goto err_attach; } sgt = gxp_dma_map_dmabuf_attachment(gxp, domain, attachment, flags, dir); if (IS_ERR(sgt)) { dev_err(gxp->dev, "Failed to map dma-buf attachment (ret=%ld)\n", PTR_ERR(sgt)); ret = PTR_ERR(sgt); goto err_map_attachment; } dmabuf_mapping = kzalloc(sizeof(*dmabuf_mapping), GFP_KERNEL); if (!dmabuf_mapping) { ret = -ENOMEM; goto err_alloc_mapping; } /* dma-buf mappings are indicated by a host_address of 0 */ refcount_set(&dmabuf_mapping->mapping.refcount, 1); dmabuf_mapping->mapping.destructor = destroy_dmabuf_mapping; dmabuf_mapping->mapping.host_address = 0; dmabuf_mapping->mapping.gxp = gxp; dmabuf_mapping->mapping.domain = domain; dmabuf_mapping->mapping.device_address = sg_dma_address(sgt->sgl); dmabuf_mapping->mapping.dir = dir; dmabuf_mapping->mapping.size = dmabuf->size; dmabuf_mapping->dmabuf = dmabuf; dmabuf_mapping->attachment = attachment; dmabuf_mapping->sgt = sgt; return &dmabuf_mapping->mapping; err_alloc_mapping: gxp_dma_unmap_dmabuf_attachment(gxp, domain, attachment, sgt, dir); err_map_attachment: dma_buf_detach(dmabuf, attachment); err_attach: dma_buf_put(dmabuf); return ERR_PTR(ret); } struct sg_table *gxp_dmabuf_get_sgt(struct gxp_mapping *mapping) { struct gxp_dmabuf_mapping *dmabuf_mapping; if (mapping->host_address) /* Not a dmabuf */ return NULL; dmabuf_mapping = container_of(mapping, struct gxp_dmabuf_mapping, mapping); return dmabuf_mapping->sgt; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) MODULE_IMPORT_NS(DMA_BUF); #endif