cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dmabuf-cache.c (4750B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * NVIDIA Tegra Video decoder driver
      4 *
      5 * Copyright (C) 2016-2019 GRATE-DRIVER project
      6 */
      7
      8#include <linux/dma-buf.h>
      9#include <linux/iova.h>
     10#include <linux/kernel.h>
     11#include <linux/list.h>
     12#include <linux/sched.h>
     13#include <linux/slab.h>
     14#include <linux/workqueue.h>
     15#include <linux/module.h>
     16
     17#include "vde.h"
     18
     19MODULE_IMPORT_NS(DMA_BUF);
     20
     21struct tegra_vde_cache_entry {
     22	enum dma_data_direction dma_dir;
     23	struct dma_buf_attachment *a;
     24	struct delayed_work dwork;
     25	struct tegra_vde *vde;
     26	struct list_head list;
     27	struct sg_table *sgt;
     28	struct iova *iova;
     29	unsigned int refcnt;
     30};
     31
     32static void tegra_vde_release_entry(struct tegra_vde_cache_entry *entry)
     33{
     34	struct dma_buf *dmabuf = entry->a->dmabuf;
     35
     36	WARN_ON_ONCE(entry->refcnt);
     37
     38	if (entry->vde->domain)
     39		tegra_vde_iommu_unmap(entry->vde, entry->iova);
     40
     41	dma_buf_unmap_attachment(entry->a, entry->sgt, entry->dma_dir);
     42	dma_buf_detach(dmabuf, entry->a);
     43	dma_buf_put(dmabuf);
     44
     45	list_del(&entry->list);
     46	kfree(entry);
     47}
     48
     49static void tegra_vde_delayed_unmap(struct work_struct *work)
     50{
     51	struct tegra_vde_cache_entry *entry;
     52	struct tegra_vde *vde;
     53
     54	entry = container_of(work, struct tegra_vde_cache_entry,
     55			     dwork.work);
     56	vde = entry->vde;
     57
     58	mutex_lock(&vde->map_lock);
     59	tegra_vde_release_entry(entry);
     60	mutex_unlock(&vde->map_lock);
     61}
     62
     63int tegra_vde_dmabuf_cache_map(struct tegra_vde *vde,
     64			       struct dma_buf *dmabuf,
     65			       enum dma_data_direction dma_dir,
     66			       struct dma_buf_attachment **ap,
     67			       dma_addr_t *addrp)
     68{
     69	struct dma_buf_attachment *attachment;
     70	struct tegra_vde_cache_entry *entry;
     71	struct device *dev = vde->dev;
     72	struct sg_table *sgt;
     73	struct iova *iova;
     74	int err;
     75
     76	mutex_lock(&vde->map_lock);
     77
     78	list_for_each_entry(entry, &vde->map_list, list) {
     79		if (entry->a->dmabuf != dmabuf)
     80			continue;
     81
     82		if (!cancel_delayed_work(&entry->dwork))
     83			continue;
     84
     85		if (entry->dma_dir != dma_dir)
     86			entry->dma_dir = DMA_BIDIRECTIONAL;
     87
     88		dma_buf_put(dmabuf);
     89
     90		if (vde->domain)
     91			*addrp = iova_dma_addr(&vde->iova, entry->iova);
     92		else
     93			*addrp = sg_dma_address(entry->sgt->sgl);
     94
     95		goto ref;
     96	}
     97
     98	attachment = dma_buf_attach(dmabuf, dev);
     99	if (IS_ERR(attachment)) {
    100		dev_err(dev, "Failed to attach dmabuf\n");
    101		err = PTR_ERR(attachment);
    102		goto err_unlock;
    103	}
    104
    105	sgt = dma_buf_map_attachment(attachment, dma_dir);
    106	if (IS_ERR(sgt)) {
    107		dev_err(dev, "Failed to get dmabufs sg_table\n");
    108		err = PTR_ERR(sgt);
    109		goto err_detach;
    110	}
    111
    112	if (!vde->domain && sgt->nents > 1) {
    113		dev_err(dev, "Sparse DMA region is unsupported, please enable IOMMU\n");
    114		err = -EINVAL;
    115		goto err_unmap;
    116	}
    117
    118	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
    119	if (!entry) {
    120		err = -ENOMEM;
    121		goto err_unmap;
    122	}
    123
    124	if (vde->domain) {
    125		err = tegra_vde_iommu_map(vde, sgt, &iova, dmabuf->size);
    126		if (err)
    127			goto err_free;
    128
    129		*addrp = iova_dma_addr(&vde->iova, iova);
    130	} else {
    131		*addrp = sg_dma_address(sgt->sgl);
    132		iova = NULL;
    133	}
    134
    135	INIT_DELAYED_WORK(&entry->dwork, tegra_vde_delayed_unmap);
    136	list_add(&entry->list, &vde->map_list);
    137
    138	entry->dma_dir = dma_dir;
    139	entry->iova = iova;
    140	entry->vde = vde;
    141	entry->sgt = sgt;
    142	entry->a = attachment;
    143ref:
    144	entry->refcnt++;
    145
    146	*ap = entry->a;
    147
    148	mutex_unlock(&vde->map_lock);
    149
    150	return 0;
    151
    152err_free:
    153	kfree(entry);
    154err_unmap:
    155	dma_buf_unmap_attachment(attachment, sgt, dma_dir);
    156err_detach:
    157	dma_buf_detach(dmabuf, attachment);
    158err_unlock:
    159	mutex_unlock(&vde->map_lock);
    160
    161	return err;
    162}
    163
    164void tegra_vde_dmabuf_cache_unmap(struct tegra_vde *vde,
    165				  struct dma_buf_attachment *a,
    166				  bool release)
    167{
    168	struct tegra_vde_cache_entry *entry;
    169
    170	mutex_lock(&vde->map_lock);
    171
    172	list_for_each_entry(entry, &vde->map_list, list) {
    173		if (entry->a != a)
    174			continue;
    175
    176		WARN_ON_ONCE(!entry->refcnt);
    177
    178		if (--entry->refcnt == 0) {
    179			if (release)
    180				tegra_vde_release_entry(entry);
    181			else
    182				schedule_delayed_work(&entry->dwork, 5 * HZ);
    183		}
    184		break;
    185	}
    186
    187	mutex_unlock(&vde->map_lock);
    188}
    189
    190void tegra_vde_dmabuf_cache_unmap_sync(struct tegra_vde *vde)
    191{
    192	struct tegra_vde_cache_entry *entry, *tmp;
    193
    194	mutex_lock(&vde->map_lock);
    195
    196	list_for_each_entry_safe(entry, tmp, &vde->map_list, list) {
    197		if (entry->refcnt)
    198			continue;
    199
    200		if (!cancel_delayed_work(&entry->dwork))
    201			continue;
    202
    203		tegra_vde_release_entry(entry);
    204	}
    205
    206	mutex_unlock(&vde->map_lock);
    207}
    208
    209void tegra_vde_dmabuf_cache_unmap_all(struct tegra_vde *vde)
    210{
    211	struct tegra_vde_cache_entry *entry, *tmp;
    212
    213	mutex_lock(&vde->map_lock);
    214
    215	while (!list_empty(&vde->map_list)) {
    216		list_for_each_entry_safe(entry, tmp, &vde->map_list, list) {
    217			if (!cancel_delayed_work(&entry->dwork))
    218				continue;
    219
    220			tegra_vde_release_entry(entry);
    221		}
    222
    223		mutex_unlock(&vde->map_lock);
    224		schedule();
    225		mutex_lock(&vde->map_lock);
    226	}
    227
    228	mutex_unlock(&vde->map_lock);
    229}