Home | History | Annotate | Line # | Download | only in linux
linux_dma_buf.c revision 1.15.4.1
      1  1.15.4.1    martin /*	$NetBSD: linux_dma_buf.c,v 1.15.4.1 2023/02/24 14:14:16 martin Exp $	*/
      2       1.1  riastrad 
      3       1.1  riastrad /*-
      4       1.1  riastrad  * Copyright (c) 2018 The NetBSD Foundation, Inc.
      5       1.1  riastrad  * All rights reserved.
      6       1.1  riastrad  *
      7       1.1  riastrad  * This code is derived from software contributed to The NetBSD Foundation
      8       1.1  riastrad  * by Taylor R. Campbell.
      9       1.1  riastrad  *
     10       1.1  riastrad  * Redistribution and use in source and binary forms, with or without
     11       1.1  riastrad  * modification, are permitted provided that the following conditions
     12       1.1  riastrad  * are met:
     13       1.1  riastrad  * 1. Redistributions of source code must retain the above copyright
     14       1.1  riastrad  *    notice, this list of conditions and the following disclaimer.
     15       1.1  riastrad  * 2. Redistributions in binary form must reproduce the above copyright
     16       1.1  riastrad  *    notice, this list of conditions and the following disclaimer in the
     17       1.1  riastrad  *    documentation and/or other materials provided with the distribution.
     18       1.1  riastrad  *
     19       1.1  riastrad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20       1.1  riastrad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21       1.1  riastrad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22       1.1  riastrad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23       1.1  riastrad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24       1.1  riastrad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25       1.1  riastrad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26       1.1  riastrad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27       1.1  riastrad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28       1.1  riastrad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29       1.1  riastrad  * POSSIBILITY OF SUCH DAMAGE.
     30       1.1  riastrad  */
     31       1.1  riastrad 
     32       1.1  riastrad #include <sys/cdefs.h>
     33  1.15.4.1    martin __KERNEL_RCSID(0, "$NetBSD: linux_dma_buf.c,v 1.15.4.1 2023/02/24 14:14:16 martin Exp $");
     34       1.1  riastrad 
     35       1.1  riastrad #include <sys/types.h>
     36       1.1  riastrad #include <sys/atomic.h>
     37       1.1  riastrad #include <sys/file.h>
     38       1.1  riastrad #include <sys/filedesc.h>
     39       1.1  riastrad #include <sys/kmem.h>
     40       1.1  riastrad #include <sys/mutex.h>
     41       1.1  riastrad 
     42       1.1  riastrad #include <linux/dma-buf.h>
     43       1.1  riastrad #include <linux/err.h>
     44       1.9  riastrad #include <linux/dma-resv.h>
     45       1.1  riastrad 
     46       1.1  riastrad static int	dmabuf_fop_poll(struct file *, int);
     47       1.1  riastrad static int	dmabuf_fop_close(struct file *);
     48       1.1  riastrad static int	dmabuf_fop_kqfilter(struct file *, struct knote *);
     49       1.1  riastrad static int	dmabuf_fop_mmap(struct file *, off_t *, size_t, int, int *,
     50       1.1  riastrad 		    int *, struct uvm_object **, int *);
     51  1.15.4.1    martin static int	dmabuf_fop_seek(struct file *fp, off_t delta, int whence,
     52  1.15.4.1    martin 		    off_t *newoffp, int flags);
     53       1.1  riastrad 
     54       1.1  riastrad static const struct fileops dmabuf_fileops = {
     55       1.1  riastrad 	.fo_name = "dmabuf",
     56       1.1  riastrad 	.fo_read = fbadop_read,
     57       1.1  riastrad 	.fo_write = fbadop_write,
     58       1.1  riastrad 	.fo_ioctl = fbadop_ioctl,
     59       1.1  riastrad 	.fo_fcntl = fnullop_fcntl,
     60       1.1  riastrad 	.fo_poll = dmabuf_fop_poll,
     61       1.1  riastrad 	.fo_stat = fbadop_stat,
     62       1.1  riastrad 	.fo_close = dmabuf_fop_close,
     63       1.1  riastrad 	.fo_kqfilter = dmabuf_fop_kqfilter,
     64       1.1  riastrad 	.fo_restart = fnullop_restart,
     65       1.1  riastrad 	.fo_mmap = dmabuf_fop_mmap,
     66  1.15.4.1    martin 	.fo_seek = dmabuf_fop_seek,
     67       1.1  riastrad };
     68       1.1  riastrad 
     69       1.1  riastrad struct dma_buf *
     70       1.1  riastrad dma_buf_export(struct dma_buf_export_info *info)
     71       1.1  riastrad {
     72       1.1  riastrad 	struct dma_buf *dmabuf;
     73       1.1  riastrad 
     74       1.1  riastrad 	if (info->resv == NULL) {
     75       1.1  riastrad 		dmabuf = kmem_zalloc(offsetof(struct dma_buf, db_resv_int[1]),
     76       1.1  riastrad 		    KM_SLEEP);
     77       1.1  riastrad 	} else {
     78       1.1  riastrad 		dmabuf = kmem_zalloc(sizeof(*dmabuf), KM_SLEEP);
     79       1.1  riastrad 	}
     80       1.1  riastrad 
     81       1.1  riastrad 	dmabuf->priv = info->priv;
     82       1.1  riastrad 	dmabuf->ops = info->ops;
     83       1.1  riastrad 	dmabuf->size = info->size;
     84       1.1  riastrad 	dmabuf->resv = info->resv;
     85       1.1  riastrad 
     86       1.1  riastrad 	mutex_init(&dmabuf->db_lock, MUTEX_DEFAULT, IPL_NONE);
     87       1.1  riastrad 	dmabuf->db_refcnt = 1;
     88       1.9  riastrad 	dma_resv_poll_init(&dmabuf->db_resv_poll);
     89       1.1  riastrad 
     90       1.1  riastrad 	if (dmabuf->resv == NULL) {
     91       1.1  riastrad 		dmabuf->resv = &dmabuf->db_resv_int[0];
     92       1.9  riastrad 		dma_resv_init(dmabuf->resv);
     93       1.1  riastrad 	}
     94       1.1  riastrad 
     95       1.1  riastrad 	return dmabuf;
     96       1.1  riastrad }
     97       1.1  riastrad 
     98       1.1  riastrad int
     99       1.1  riastrad dma_buf_fd(struct dma_buf *dmabuf, int flags)
    100       1.1  riastrad {
    101       1.1  riastrad 	struct file *file;
    102       1.1  riastrad 	int fd;
    103       1.1  riastrad 	unsigned refcnt __diagused;
    104       1.1  riastrad 	int ret;
    105       1.1  riastrad 
    106      1.11  riastrad 	/* XXX errno NetBSD->Linux */
    107       1.1  riastrad 	ret = -fd_allocfile(&file, &fd);
    108       1.1  riastrad 	if (ret)
    109       1.1  riastrad 		goto out0;
    110       1.1  riastrad 
    111       1.1  riastrad 	refcnt = atomic_inc_uint_nv(&dmabuf->db_refcnt);
    112       1.1  riastrad 	KASSERT(refcnt > 1);
    113       1.1  riastrad 
    114       1.1  riastrad 	file->f_type = DTYPE_MISC;
    115       1.1  riastrad 	file->f_flag = 0;	/* XXX DRM code allows only O_CLOEXEC.  */
    116       1.1  riastrad 	file->f_ops = &dmabuf_fileops;
    117       1.1  riastrad 	file->f_data = dmabuf;
    118       1.1  riastrad 	fd_set_exclose(curlwp, fd, (flags & O_CLOEXEC) != 0);
    119       1.1  riastrad 	fd_affix(curproc, file, fd);
    120       1.1  riastrad 
    121       1.2  riastrad 	ret = fd;
    122       1.1  riastrad out0:	return ret;
    123       1.1  riastrad }
    124       1.1  riastrad 
    125       1.1  riastrad struct dma_buf *
    126       1.1  riastrad dma_buf_get(int fd)
    127       1.1  riastrad {
    128       1.1  riastrad 	struct file *file;
    129       1.1  riastrad 	struct dma_buf *dmabuf;
    130       1.1  riastrad 	unsigned refcnt __diagused;
    131       1.1  riastrad 	int error;
    132       1.1  riastrad 
    133       1.1  riastrad 	if ((file = fd_getfile(fd)) == NULL) {
    134       1.1  riastrad 		error = EBADF;
    135       1.6      maya 		goto fail0;
    136       1.1  riastrad 	}
    137       1.1  riastrad 	if (file->f_type != DTYPE_MISC || file->f_ops != &dmabuf_fileops) {
    138       1.1  riastrad 		error = EINVAL;
    139       1.6      maya 		goto fail1;
    140       1.1  riastrad 	}
    141       1.1  riastrad 
    142       1.1  riastrad 	dmabuf = file->f_data;
    143       1.1  riastrad 	refcnt = atomic_inc_uint_nv(&dmabuf->db_refcnt);
    144       1.1  riastrad 	KASSERT(refcnt > 1);
    145       1.1  riastrad 	fd_putfile(fd);
    146       1.1  riastrad 	return dmabuf;
    147       1.1  riastrad 
    148       1.1  riastrad fail1:	fd_putfile(fd);
    149       1.1  riastrad fail0:	KASSERT(error);
    150       1.1  riastrad 	return ERR_PTR(-error);
    151       1.1  riastrad }
    152       1.1  riastrad 
    153       1.1  riastrad void
    154       1.1  riastrad get_dma_buf(struct dma_buf *dmabuf)
    155       1.1  riastrad {
    156       1.1  riastrad 	unsigned refcnt __diagused;
    157       1.1  riastrad 
    158       1.1  riastrad 	refcnt = atomic_inc_uint_nv(&dmabuf->db_refcnt);
    159       1.1  riastrad 	KASSERT(refcnt > 1);
    160       1.1  riastrad }
    161       1.1  riastrad 
    162       1.1  riastrad void
    163       1.1  riastrad dma_buf_put(struct dma_buf *dmabuf)
    164       1.1  riastrad {
    165       1.1  riastrad 
    166      1.15  riastrad 	membar_release();
    167       1.1  riastrad 	if (atomic_dec_uint_nv(&dmabuf->db_refcnt) != 0)
    168       1.1  riastrad 		return;
    169      1.15  riastrad 	membar_acquire();
    170       1.1  riastrad 
    171       1.9  riastrad 	dma_resv_poll_fini(&dmabuf->db_resv_poll);
    172       1.3  riastrad 	mutex_destroy(&dmabuf->db_lock);
    173       1.3  riastrad 	if (dmabuf->resv == &dmabuf->db_resv_int[0]) {
    174       1.9  riastrad 		dma_resv_fini(dmabuf->resv);
    175       1.1  riastrad 		kmem_free(dmabuf, offsetof(struct dma_buf, db_resv_int[1]));
    176       1.1  riastrad 	} else {
    177       1.1  riastrad 		kmem_free(dmabuf, sizeof(*dmabuf));
    178       1.1  riastrad 	}
    179       1.1  riastrad }
    180       1.1  riastrad 
    181       1.1  riastrad struct dma_buf_attachment *
    182      1.13  riastrad dma_buf_dynamic_attach(struct dma_buf *dmabuf, bus_dma_tag_t dmat,
    183      1.13  riastrad     bool dynamic_mapping)
    184       1.1  riastrad {
    185       1.1  riastrad 	struct dma_buf_attachment *attach;
    186       1.1  riastrad 	int ret = 0;
    187       1.1  riastrad 
    188       1.1  riastrad 	attach = kmem_zalloc(sizeof(*attach), KM_SLEEP);
    189       1.1  riastrad 	attach->dmabuf = dmabuf;
    190      1.10  riastrad 	attach->dev = dmat;
    191      1.13  riastrad 	attach->dynamic_mapping = dynamic_mapping;
    192       1.1  riastrad 
    193       1.1  riastrad 	mutex_enter(&dmabuf->db_lock);
    194       1.1  riastrad 	if (dmabuf->ops->attach)
    195       1.8  riastrad 		ret = dmabuf->ops->attach(dmabuf, attach);
    196       1.1  riastrad 	mutex_exit(&dmabuf->db_lock);
    197       1.1  riastrad 	if (ret)
    198       1.1  riastrad 		goto fail0;
    199       1.1  riastrad 
    200      1.13  riastrad 	if (attach->dynamic_mapping != dmabuf->ops->dynamic_mapping)
    201      1.13  riastrad 		panic("%s: NYI", __func__);
    202      1.13  riastrad 
    203       1.1  riastrad 	return attach;
    204       1.1  riastrad 
    205       1.1  riastrad fail0:	kmem_free(attach, sizeof(*attach));
    206       1.1  riastrad 	return ERR_PTR(ret);
    207       1.1  riastrad }
    208       1.1  riastrad 
    209      1.13  riastrad struct dma_buf_attachment *
    210      1.13  riastrad dma_buf_attach(struct dma_buf *dmabuf, bus_dma_tag_t dmat)
    211      1.13  riastrad {
    212      1.13  riastrad 
    213      1.13  riastrad 	return dma_buf_dynamic_attach(dmabuf, dmat, /*dynamic_mapping*/false);
    214      1.13  riastrad }
    215      1.13  riastrad 
    216       1.1  riastrad void
    217       1.1  riastrad dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
    218       1.1  riastrad {
    219       1.1  riastrad 
    220       1.1  riastrad 	mutex_enter(&dmabuf->db_lock);
    221       1.1  riastrad 	if (dmabuf->ops->detach)
    222       1.1  riastrad 		dmabuf->ops->detach(dmabuf, attach);
    223       1.1  riastrad 	mutex_exit(&dmabuf->db_lock);
    224       1.1  riastrad 
    225       1.1  riastrad 	kmem_free(attach, sizeof(*attach));
    226       1.1  riastrad }
    227       1.1  riastrad 
    228       1.1  riastrad struct sg_table *
    229       1.1  riastrad dma_buf_map_attachment(struct dma_buf_attachment *attach,
    230       1.1  riastrad     enum dma_data_direction dir)
    231       1.1  riastrad {
    232      1.13  riastrad 	struct sg_table *sg;
    233      1.13  riastrad 
    234      1.13  riastrad 	if (attach->dmabuf->ops->dynamic_mapping)
    235      1.13  riastrad 		dma_resv_lock(attach->dmabuf->resv, NULL);
    236      1.13  riastrad 	sg = attach->dmabuf->ops->map_dma_buf(attach, dir);
    237      1.13  riastrad 	if (attach->dmabuf->ops->dynamic_mapping)
    238      1.13  riastrad 		dma_resv_unlock(attach->dmabuf->resv);
    239       1.1  riastrad 
    240      1.13  riastrad 	return sg;
    241       1.1  riastrad }
    242       1.1  riastrad 
    243       1.1  riastrad void
    244       1.1  riastrad dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
    245       1.1  riastrad     struct sg_table *sg, enum dma_data_direction dir)
    246       1.1  riastrad {
    247       1.1  riastrad 
    248      1.13  riastrad 	if (attach->dmabuf->ops->dynamic_mapping)
    249      1.13  riastrad 		dma_resv_lock(attach->dmabuf->resv, NULL);
    250      1.13  riastrad 	attach->dmabuf->ops->unmap_dma_buf(attach, sg, dir);
    251      1.13  riastrad 	if (attach->dmabuf->ops->dynamic_mapping)
    252      1.13  riastrad 		dma_resv_unlock(attach->dmabuf->resv);
    253       1.1  riastrad }
    254       1.1  riastrad 
    255       1.1  riastrad static int
    256       1.1  riastrad dmabuf_fop_close(struct file *file)
    257       1.1  riastrad {
    258      1.12  riastrad 	struct dma_buf *dmabuf = file->f_data;
    259       1.1  riastrad 
    260       1.1  riastrad 	dma_buf_put(dmabuf);
    261       1.1  riastrad 	return 0;
    262       1.1  riastrad }
    263       1.1  riastrad 
    264       1.1  riastrad static int
    265       1.1  riastrad dmabuf_fop_poll(struct file *file, int events)
    266       1.1  riastrad {
    267      1.12  riastrad 	struct dma_buf *dmabuf = file->f_data;
    268       1.9  riastrad 	struct dma_resv_poll *rpoll = &dmabuf->db_resv_poll;
    269       1.1  riastrad 
    270       1.9  riastrad 	return dma_resv_do_poll(dmabuf->resv, events, rpoll);
    271       1.1  riastrad }
    272       1.1  riastrad 
    273       1.1  riastrad static int
    274       1.4  riastrad dmabuf_fop_kqfilter(struct file *file, struct knote *kn)
    275       1.1  riastrad {
    276      1.12  riastrad 	struct dma_buf *dmabuf = file->f_data;
    277       1.9  riastrad 	struct dma_resv_poll *rpoll = &dmabuf->db_resv_poll;
    278       1.1  riastrad 
    279       1.9  riastrad 	return dma_resv_kqfilter(dmabuf->resv, kn, rpoll);
    280       1.1  riastrad }
    281       1.1  riastrad 
    282       1.1  riastrad static int
    283       1.1  riastrad dmabuf_fop_mmap(struct file *file, off_t *offp, size_t size, int prot,
    284       1.1  riastrad     int *flagsp, int *advicep, struct uvm_object **uobjp, int *maxprotp)
    285       1.1  riastrad {
    286      1.12  riastrad 	struct dma_buf *dmabuf = file->f_data;
    287       1.1  riastrad 
    288       1.1  riastrad 	if (size > dmabuf->size)
    289       1.1  riastrad 		return EINVAL;
    290       1.1  riastrad 
    291       1.1  riastrad 	return dmabuf->ops->mmap(dmabuf, offp, size, prot, flagsp, advicep,
    292       1.1  riastrad 	    uobjp, maxprotp);
    293       1.1  riastrad }
    294  1.15.4.1    martin 
    295  1.15.4.1    martin /*
    296  1.15.4.1    martin  * We don't actually do anything with the file offset; this is just how
    297  1.15.4.1    martin  * libdrm_amdgpu expects to find the size of the DMA buf.  (Why it
    298  1.15.4.1    martin  * doesn't use fstat is unclear, but it doesn't really matter.)
    299  1.15.4.1    martin  */
    300  1.15.4.1    martin static int
    301  1.15.4.1    martin dmabuf_fop_seek(struct file *fp, off_t delta, int whence, off_t *newoffp,
    302  1.15.4.1    martin     int flags)
    303  1.15.4.1    martin {
    304  1.15.4.1    martin 	const off_t OFF_MAX = __type_max(off_t);
    305  1.15.4.1    martin 	struct dma_buf *dmabuf = fp->f_data;
    306  1.15.4.1    martin 	off_t base, newoff;
    307  1.15.4.1    martin 	int error;
    308  1.15.4.1    martin 
    309  1.15.4.1    martin 	mutex_enter(&fp->f_lock);
    310  1.15.4.1    martin 
    311  1.15.4.1    martin 	switch (whence) {
    312  1.15.4.1    martin 	case SEEK_CUR:
    313  1.15.4.1    martin 		base = fp->f_offset;
    314  1.15.4.1    martin 		break;
    315  1.15.4.1    martin 	case SEEK_END:
    316  1.15.4.1    martin 		base = dmabuf->size;
    317  1.15.4.1    martin 		break;
    318  1.15.4.1    martin 	case SEEK_SET:
    319  1.15.4.1    martin 		base = 0;
    320  1.15.4.1    martin 		break;
    321  1.15.4.1    martin 	default:
    322  1.15.4.1    martin 		error = EINVAL;
    323  1.15.4.1    martin 		goto out;
    324  1.15.4.1    martin 	}
    325  1.15.4.1    martin 
    326  1.15.4.1    martin 	/* Check for arithmetic overflow and reject negative offsets.  */
    327  1.15.4.1    martin 	if (base < 0 || delta > OFF_MAX - base || base + delta < 0) {
    328  1.15.4.1    martin 		error = EINVAL;
    329  1.15.4.1    martin 		goto out;
    330  1.15.4.1    martin 	}
    331  1.15.4.1    martin 
    332  1.15.4.1    martin 	/* Compute the new offset.  */
    333  1.15.4.1    martin 	newoff = base + delta;
    334  1.15.4.1    martin 
    335  1.15.4.1    martin 	/* Success!  */
    336  1.15.4.1    martin 	if (newoffp)
    337  1.15.4.1    martin 		*newoffp = newoff;
    338  1.15.4.1    martin 	if (flags & FOF_UPDATE_OFFSET)
    339  1.15.4.1    martin 		fp->f_offset = newoff;
    340  1.15.4.1    martin 	error = 0;
    341  1.15.4.1    martin 
    342  1.15.4.1    martin out:	mutex_exit(&fp->f_lock);
    343  1.15.4.1    martin 	return error;
    344  1.15.4.1    martin }
    345