Home | History | Annotate | Line # | Download | only in drm
      1  1.8  riastrad /*	$NetBSD: drm_vma_manager.c,v 1.8 2021/12/19 11:57:27 riastradh Exp $	*/
      2  1.1  riastrad 
      3  1.1  riastrad /*-
      4  1.1  riastrad  * Copyright (c) 2014 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.8  riastrad __KERNEL_RCSID(0, "$NetBSD: drm_vma_manager.c,v 1.8 2021/12/19 11:57:27 riastradh Exp $");
     34  1.1  riastrad 
     35  1.1  riastrad #include <sys/kmem.h>
     36  1.1  riastrad #include <sys/rbtree.h>
     37  1.1  riastrad #include <sys/vmem.h>
     38  1.1  riastrad 
     39  1.1  riastrad #include <drm/drm_vma_manager.h>
     40  1.1  riastrad 
     41  1.1  riastrad static int
     42  1.1  riastrad drm_vma_node_compare(void *cookie __unused, const void *va, const void *vb)
     43  1.1  riastrad {
     44  1.1  riastrad 	const struct drm_vma_offset_node *const na = va;
     45  1.1  riastrad 	const struct drm_vma_offset_node *const nb = vb;
     46  1.1  riastrad 
     47  1.1  riastrad 	if (na->von_startpage < nb->von_startpage)
     48  1.1  riastrad 		return -1;
     49  1.1  riastrad 	if (na->von_startpage > nb->von_startpage)
     50  1.1  riastrad 		return +1;
     51  1.1  riastrad 	return 0;
     52  1.1  riastrad }
     53  1.1  riastrad 
     54  1.1  riastrad static int
     55  1.1  riastrad drm_vma_node_compare_key(void *cookie __unused, const void *vn, const void *vk)
     56  1.1  riastrad {
     57  1.1  riastrad 	const struct drm_vma_offset_node *const n = vn;
     58  1.1  riastrad 	const vmem_addr_t *const k = vk;
     59  1.1  riastrad 
     60  1.1  riastrad 	if (n->von_startpage < *k)
     61  1.1  riastrad 		return -1;
     62  1.1  riastrad 	if (n->von_startpage > *k)
     63  1.1  riastrad 		return +1;
     64  1.1  riastrad 	return 0;
     65  1.1  riastrad }
     66  1.1  riastrad 
     67  1.1  riastrad static const rb_tree_ops_t drm_vma_node_rb_ops = {
     68  1.1  riastrad 	.rbto_compare_nodes = &drm_vma_node_compare,
     69  1.1  riastrad 	.rbto_compare_key = &drm_vma_node_compare_key,
     70  1.1  riastrad 	.rbto_node_offset = offsetof(struct drm_vma_offset_node, von_rb_node),
     71  1.1  riastrad 	.rbto_context = NULL,
     72  1.1  riastrad };
     73  1.1  riastrad 
     74  1.1  riastrad static int
     75  1.1  riastrad drm_vma_file_compare(void *cookie __unused, const void *va, const void *vb)
     76  1.1  riastrad {
     77  1.1  riastrad 	const struct drm_vma_offset_file *const fa = va;
     78  1.1  riastrad 	const struct drm_vma_offset_file *const fb = vb;
     79  1.1  riastrad 
     80  1.1  riastrad 	if (fa->vof_file < fb->vof_file)
     81  1.1  riastrad 		return -1;
     82  1.1  riastrad 	if (fa->vof_file > fb->vof_file)
     83  1.1  riastrad 		return +1;
     84  1.1  riastrad 	return 0;
     85  1.1  riastrad }
     86  1.1  riastrad 
     87  1.1  riastrad static int
     88  1.1  riastrad drm_vma_file_compare_key(void *cookie __unused, const void *vf, const void *vk)
     89  1.1  riastrad {
     90  1.1  riastrad 	const struct drm_vma_offset_file *const f = vf;
     91  1.6  riastrad 	const struct drm_file *const k = vk;
     92  1.1  riastrad 
     93  1.1  riastrad 	if (f->vof_file < k)
     94  1.1  riastrad 		return -1;
     95  1.1  riastrad 	if (f->vof_file > k)
     96  1.1  riastrad 		return +1;
     97  1.1  riastrad 	return 0;
     98  1.1  riastrad }
     99  1.1  riastrad 
    100  1.1  riastrad static const rb_tree_ops_t drm_vma_file_rb_ops = {
    101  1.1  riastrad 	.rbto_compare_nodes = &drm_vma_file_compare,
    102  1.1  riastrad 	.rbto_compare_key = &drm_vma_file_compare_key,
    103  1.1  riastrad 	.rbto_node_offset = offsetof(struct drm_vma_offset_file, vof_rb_node),
    104  1.1  riastrad 	.rbto_context = NULL,
    105  1.1  riastrad };
    106  1.1  riastrad 
    107  1.1  riastrad void
    108  1.1  riastrad drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
    109  1.1  riastrad     unsigned long startpage, unsigned long npages)
    110  1.1  riastrad {
    111  1.1  riastrad 
    112  1.1  riastrad 	rw_init(&mgr->vom_lock);
    113  1.1  riastrad 	rb_tree_init(&mgr->vom_nodes, &drm_vma_node_rb_ops);
    114  1.1  riastrad 	mgr->vom_vmem = vmem_create("drm_vma", startpage, npages, 1,
    115  1.1  riastrad 	    NULL, NULL, NULL, 0, VM_SLEEP, IPL_NONE);
    116  1.1  riastrad }
    117  1.1  riastrad 
    118  1.1  riastrad void
    119  1.1  riastrad drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
    120  1.1  riastrad {
    121  1.1  riastrad 
    122  1.1  riastrad 	vmem_destroy(mgr->vom_vmem);
    123  1.4  riastrad 	KASSERTMSG((RB_TREE_MIN(&mgr->vom_nodes) == NULL),
    124  1.4  riastrad 	    "drm vma offset manager %p not empty", mgr);
    125  1.1  riastrad #if 0
    126  1.1  riastrad 	rb_tree_destroy(&mgr->vom_nodes);
    127  1.1  riastrad #endif
    128  1.1  riastrad 	rw_destroy(&mgr->vom_lock);
    129  1.1  riastrad }
    130  1.1  riastrad 
    131  1.1  riastrad void
    133  1.1  riastrad drm_vma_node_init(struct drm_vma_offset_node *node)
    134  1.1  riastrad {
    135  1.1  riastrad 	static const struct drm_vma_offset_node zero_node;
    136  1.1  riastrad 
    137  1.1  riastrad 	*node = zero_node;
    138  1.1  riastrad 
    139  1.1  riastrad 	rw_init(&node->von_lock);
    140  1.1  riastrad 	node->von_startpage = 0;
    141  1.1  riastrad 	node->von_npages = 0;
    142  1.1  riastrad 	rb_tree_init(&node->von_files, &drm_vma_file_rb_ops);
    143  1.1  riastrad }
    144  1.1  riastrad 
    145  1.1  riastrad void
    146  1.1  riastrad drm_vma_node_destroy(struct drm_vma_offset_node *node)
    147  1.1  riastrad {
    148  1.4  riastrad 
    149  1.4  riastrad 	KASSERTMSG((RB_TREE_MIN(&node->von_files) == NULL),
    150  1.1  riastrad 	    "drm vma node %p not empty", node);
    151  1.1  riastrad #if 0
    152  1.1  riastrad 	rb_tree_destroy(&node->von_files);
    153  1.1  riastrad #endif
    154  1.1  riastrad 	KASSERT(node->von_startpage == 0);
    155  1.1  riastrad 	KASSERT(node->von_npages == 0);
    156  1.1  riastrad 	rw_destroy(&node->von_lock);
    157  1.1  riastrad }
    158  1.1  riastrad 
    159  1.1  riastrad int
    160  1.1  riastrad drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
    161  1.1  riastrad     struct drm_vma_offset_node *node, unsigned long npages)
    162  1.1  riastrad {
    163  1.1  riastrad 	vmem_size_t startpage;
    164  1.1  riastrad 	struct drm_vma_offset_node *collision __diagused;
    165  1.1  riastrad 	int error;
    166  1.1  riastrad 
    167  1.1  riastrad 	KASSERT(npages != 0);
    168  1.1  riastrad 
    169  1.1  riastrad 	if (0 < node->von_npages)
    170  1.1  riastrad 		return 0;
    171  1.2  riastrad 
    172  1.1  riastrad 	error = vmem_alloc(mgr->vom_vmem, npages, VM_NOSLEEP|VM_BESTFIT,
    173  1.2  riastrad 	    &startpage);
    174  1.2  riastrad 	if (error) {
    175  1.2  riastrad 		if (error == ENOMEM)
    176  1.1  riastrad 			error = ENOSPC;
    177  1.1  riastrad 		/* XXX errno NetBSD->Linux */
    178  1.2  riastrad 		return -error;
    179  1.1  riastrad 	}
    180  1.1  riastrad 
    181  1.1  riastrad 	node->von_startpage = startpage;
    182  1.1  riastrad 	node->von_npages = npages;
    183  1.5      maya 
    184  1.1  riastrad 	rw_enter(&mgr->vom_lock, RW_WRITER);
    185  1.1  riastrad 	collision = rb_tree_insert_node(&mgr->vom_nodes, node);
    186  1.5      maya 	KASSERT(collision == node);
    187  1.1  riastrad 	rw_exit(&mgr->vom_lock);
    188  1.1  riastrad 
    189  1.1  riastrad 	return 0;
    190  1.1  riastrad }
    191  1.1  riastrad 
    192  1.1  riastrad void
    193  1.1  riastrad drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
    194  1.1  riastrad     struct drm_vma_offset_node *node)
    195  1.1  riastrad {
    196  1.1  riastrad 
    197  1.1  riastrad 	if (node->von_npages == 0)
    198  1.1  riastrad 		return;
    199  1.5      maya 
    200  1.1  riastrad 	rw_enter(&mgr->vom_lock, RW_WRITER);
    201  1.5      maya 	rb_tree_remove_node(&mgr->vom_nodes, node);
    202  1.1  riastrad 	rw_exit(&mgr->vom_lock);
    203  1.1  riastrad 
    204  1.1  riastrad 	vmem_free(mgr->vom_vmem, node->von_startpage, node->von_npages);
    205  1.1  riastrad 
    206  1.1  riastrad 	node->von_npages = 0;
    207  1.1  riastrad 	node->von_startpage = 0;
    208  1.1  riastrad }
    209  1.1  riastrad 
    210  1.1  riastrad void
    212  1.1  riastrad drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr)
    213  1.1  riastrad {
    214  1.1  riastrad 
    215  1.1  riastrad 	rw_enter(&mgr->vom_lock, RW_READER);
    216  1.1  riastrad }
    217  1.1  riastrad 
    218  1.1  riastrad void
    219  1.1  riastrad drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)
    220  1.1  riastrad {
    221  1.1  riastrad 
    222  1.1  riastrad 	rw_exit(&mgr->vom_lock);
    223  1.1  riastrad }
    224  1.1  riastrad 
    225  1.1  riastrad struct drm_vma_offset_node *
    226  1.1  riastrad drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
    227  1.1  riastrad     unsigned long startpage, unsigned long npages)
    228  1.1  riastrad {
    229  1.1  riastrad 	const vmem_addr_t key = startpage;
    230  1.1  riastrad 	struct drm_vma_offset_node *node;
    231  1.1  riastrad 
    232  1.1  riastrad 	KASSERT(rw_lock_held(&mgr->vom_lock));
    233  1.1  riastrad 
    234  1.1  riastrad 	node = rb_tree_find_node_leq(&mgr->vom_nodes, &key);
    235  1.1  riastrad 	if (node == NULL)
    236  1.7  riastrad 		return NULL;
    237  1.1  riastrad 	KASSERT(node->von_startpage <= startpage);
    238  1.1  riastrad 	if (node->von_npages < npages)
    239  1.1  riastrad 		return NULL;
    240  1.1  riastrad 	if (node->von_npages - npages < startpage - node->von_startpage)
    241  1.1  riastrad 		return NULL;
    242  1.1  riastrad 
    243  1.1  riastrad 	return node;
    244  1.1  riastrad }
    245  1.8  riastrad 
    246  1.1  riastrad struct drm_vma_offset_node *
    247  1.1  riastrad drm_vma_offset_exact_lookup_locked(struct drm_vma_offset_manager *mgr,
    248  1.1  riastrad     unsigned long startpage, unsigned long npages)
    249  1.1  riastrad {
    250  1.1  riastrad 	const vmem_addr_t key = startpage;
    251  1.8  riastrad 	struct drm_vma_offset_node *node;
    252  1.1  riastrad 
    253  1.1  riastrad 	KASSERT(rw_lock_held(&mgr->vom_lock));
    254  1.1  riastrad 
    255  1.8  riastrad 	node = rb_tree_find_node(&mgr->vom_nodes, &key);
    256  1.1  riastrad 	if (node == NULL)
    257  1.8  riastrad 		return NULL;
    258  1.8  riastrad 	KASSERT(node->von_startpage == startpage);
    259  1.8  riastrad 	if (node->von_npages != npages)
    260  1.8  riastrad 		return NULL;
    261  1.8  riastrad 
    262  1.8  riastrad 	return node;
    263  1.8  riastrad }
    264  1.8  riastrad 
    265  1.8  riastrad struct drm_vma_offset_node *
    266  1.8  riastrad drm_vma_offset_exact_lookup(struct drm_vma_offset_manager *mgr,
    267  1.8  riastrad     unsigned long startpage, unsigned long npages)
    268  1.8  riastrad {
    269  1.8  riastrad 	struct drm_vma_offset_node *node;
    270  1.8  riastrad 
    271  1.8  riastrad 	rw_enter(&mgr->vom_lock, RW_READER);
    272  1.1  riastrad 	node = drm_vma_offset_exact_lookup_locked(mgr, startpage, npages);
    273  1.1  riastrad 	rw_exit(&mgr->vom_lock);
    274  1.1  riastrad 
    275  1.1  riastrad 	return node;
    276  1.1  riastrad }
    277  1.6  riastrad 
    278  1.1  riastrad int
    280  1.1  riastrad drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *file)
    281  1.1  riastrad {
    282  1.1  riastrad 	struct drm_vma_offset_file *new, *old;
    283  1.1  riastrad 
    284  1.1  riastrad 	new = kmem_alloc(sizeof(*new), KM_NOSLEEP);
    285  1.1  riastrad 	if (new == NULL)
    286  1.1  riastrad 		return -ENOMEM;
    287  1.1  riastrad 	new->vof_file = file;
    288  1.1  riastrad 
    289  1.1  riastrad 	rw_enter(&node->von_lock, RW_WRITER);
    290  1.1  riastrad 	old = rb_tree_insert_node(&node->von_files, new);
    291  1.1  riastrad 	rw_exit(&node->von_lock);
    292  1.1  riastrad 
    293  1.1  riastrad 	if (old != new)		/* collision */
    294  1.1  riastrad 		kmem_free(new, sizeof(*new));
    295  1.1  riastrad 
    296  1.1  riastrad 	return 0;
    297  1.6  riastrad }
    298  1.1  riastrad 
    299  1.1  riastrad void
    300  1.1  riastrad drm_vma_node_revoke(struct drm_vma_offset_node *node, struct drm_file *file)
    301  1.1  riastrad {
    302  1.1  riastrad 
    303  1.1  riastrad 	rw_enter(&node->von_lock, RW_WRITER);
    304  1.1  riastrad 	struct drm_vma_offset_file *const found =
    305  1.1  riastrad 	    rb_tree_find_node(&node->von_files, file);
    306  1.3       chs 	if (found != NULL)
    307  1.3       chs 		rb_tree_remove_node(&node->von_files, found);
    308  1.1  riastrad 	rw_exit(&node->von_lock);
    309  1.1  riastrad 	if (found != NULL)
    310  1.1  riastrad 		kmem_free(found, sizeof(*found));
    311  1.6  riastrad }
    312  1.6  riastrad 
    313  1.1  riastrad bool
    314  1.1  riastrad drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
    315  1.1  riastrad     struct drm_file *file)
    316  1.1  riastrad {
    317  1.1  riastrad 
    318  1.1  riastrad 	rw_enter(&node->von_lock, RW_READER);
    319  1.1  riastrad 	const bool allowed =
    320  1.1  riastrad 	    (rb_tree_find_node(&node->von_files, file) != NULL);
    321  1.1  riastrad 	rw_exit(&node->von_lock);
    322  1.1  riastrad 
    323  1.1  riastrad 	return allowed;
    324  1.6  riastrad }
    325  1.6  riastrad 
    326  1.1  riastrad int
    327  1.1  riastrad drm_vma_node_verify_access(struct drm_vma_offset_node *node,
    328  1.1  riastrad     struct drm_file *file)
    329  1.1  riastrad {
    330  1.1  riastrad 
    331  1.1  riastrad 	if (!drm_vma_node_is_allowed(node, file))
    332  1.1  riastrad 		return -EACCES;
    333                
    334                	return 0;
    335                }
    336