Home | History | Annotate | Line # | Download | only in linux
      1  1.17  riastrad /*	$NetBSD: linux_dma_buf.c,v 1.17 2024/05/20 11:35:10 riastradh 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.17  riastrad __KERNEL_RCSID(0, "$NetBSD: linux_dma_buf.c,v 1.17 2024/05/20 11:35:10 riastradh 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.16  riastrad static int	dmabuf_fop_seek(struct file *fp, off_t delta, int whence,
     52  1.16  riastrad 		    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.16  riastrad 	.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.17  riastrad 	if (size > dmabuf->size || *offp < 0 || *offp > dmabuf->size - 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.16  riastrad 
    295  1.16  riastrad /*
    296  1.16  riastrad  * We don't actually do anything with the file offset; this is just how
    297  1.16  riastrad  * libdrm_amdgpu expects to find the size of the DMA buf.  (Why it
    298  1.16  riastrad  * doesn't use fstat is unclear, but it doesn't really matter.)
    299  1.16  riastrad  */
    300  1.16  riastrad static int
    301  1.16  riastrad dmabuf_fop_seek(struct file *fp, off_t delta, int whence, off_t *newoffp,
    302  1.16  riastrad     int flags)
    303  1.16  riastrad {
    304  1.16  riastrad 	const off_t OFF_MAX = __type_max(off_t);
    305  1.16  riastrad 	struct dma_buf *dmabuf = fp->f_data;
    306  1.16  riastrad 	off_t base, newoff;
    307  1.16  riastrad 	int error;
    308  1.16  riastrad 
    309  1.16  riastrad 	mutex_enter(&fp->f_lock);
    310  1.16  riastrad 
    311  1.16  riastrad 	switch (whence) {
    312  1.16  riastrad 	case SEEK_CUR:
    313  1.16  riastrad 		base = fp->f_offset;
    314  1.16  riastrad 		break;
    315  1.16  riastrad 	case SEEK_END:
    316  1.16  riastrad 		base = dmabuf->size;
    317  1.16  riastrad 		break;
    318  1.16  riastrad 	case SEEK_SET:
    319  1.16  riastrad 		base = 0;
    320  1.16  riastrad 		break;
    321  1.16  riastrad 	default:
    322  1.16  riastrad 		error = EINVAL;
    323  1.16  riastrad 		goto out;
    324  1.16  riastrad 	}
    325  1.16  riastrad 
    326  1.16  riastrad 	/* Check for arithmetic overflow and reject negative offsets.  */
    327  1.16  riastrad 	if (base < 0 || delta > OFF_MAX - base || base + delta < 0) {
    328  1.16  riastrad 		error = EINVAL;
    329  1.16  riastrad 		goto out;
    330  1.16  riastrad 	}
    331  1.16  riastrad 
    332  1.16  riastrad 	/* Compute the new offset.  */
    333  1.16  riastrad 	newoff = base + delta;
    334  1.16  riastrad 
    335  1.16  riastrad 	/* Success!  */
    336  1.16  riastrad 	if (newoffp)
    337  1.16  riastrad 		*newoffp = newoff;
    338  1.16  riastrad 	if (flags & FOF_UPDATE_OFFSET)
    339  1.16  riastrad 		fp->f_offset = newoff;
    340  1.16  riastrad 	error = 0;
    341  1.16  riastrad 
    342  1.16  riastrad out:	mutex_exit(&fp->f_lock);
    343  1.16  riastrad 	return error;
    344  1.16  riastrad }
    345