Module Name:    src
Committed By:   riastradh
Date:           Sun Dec 19 12:05:25 UTC 2021

Modified Files:
        src/sys/external/bsd/drm2/include/linux: xarray.h
        src/sys/external/bsd/drm2/linux: linux_xa.c

Log Message:
linux: Rework radix tree shims.


To generate a diff of this commit:
cvs rdiff -u -r1.8 -r1.9 src/sys/external/bsd/drm2/include/linux/xarray.h
cvs rdiff -u -r1.2 -r1.3 src/sys/external/bsd/drm2/linux/linux_xa.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/external/bsd/drm2/include/linux/xarray.h
diff -u src/sys/external/bsd/drm2/include/linux/xarray.h:1.8 src/sys/external/bsd/drm2/include/linux/xarray.h:1.9
--- src/sys/external/bsd/drm2/include/linux/xarray.h:1.8	Sun Dec 19 11:51:07 2021
+++ src/sys/external/bsd/drm2/include/linux/xarray.h	Sun Dec 19 12:05:25 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: xarray.h,v 1.8 2021/12/19 11:51:07 riastradh Exp $	*/
+/*	$NetBSD: xarray.h,v 1.9 2021/12/19 12:05:25 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2020 The NetBSD Foundation, Inc.
@@ -29,7 +29,7 @@
 #ifndef	_LINUX_XARRAY_H_
 #define	_LINUX_XARRAY_H_
 
-#include <sys/radixtree.h>
+#include <sys/rbtree.h>
 
 #include <linux/slab.h>
 
@@ -43,14 +43,14 @@ struct xa_limit {
 
 struct xarray {
 	kmutex_t		xa_lock;
-	struct radix_tree	xa_tree;
+	struct rb_tree		xa_tree;
 	gfp_t			xa_gfp;
 };
 
 #define	xa_for_each(XA, INDEX, ENTRY)					      \
 	for ((INDEX) = 0, (ENTRY) = xa_find((XA), &(INDEX), ULONG_MAX, -1);   \
 		(ENTRY) != NULL;					      \
-		(ENTRY) = xa_find_after((XA), &(INDEX), ULONG_MAX, 0))
+		(ENTRY) = xa_find_after((XA), &(INDEX), ULONG_MAX, -1))
 
 #define	XA_ERROR(error)	((void *)(((uintptr_t)error << 2) | 2))
 

Index: src/sys/external/bsd/drm2/linux/linux_xa.c
diff -u src/sys/external/bsd/drm2/linux/linux_xa.c:1.2 src/sys/external/bsd/drm2/linux/linux_xa.c:1.3
--- src/sys/external/bsd/drm2/linux/linux_xa.c:1.2	Sun Dec 19 12:05:18 2021
+++ src/sys/external/bsd/drm2/linux/linux_xa.c	Sun Dec 19 12:05:25 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: linux_xa.c,v 1.2 2021/12/19 12:05:18 riastradh Exp $	*/
+/*	$NetBSD: linux_xa.c,v 1.3 2021/12/19 12:05:25 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -27,14 +27,59 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_xa.c,v 1.2 2021/12/19 12:05:18 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_xa.c,v 1.3 2021/12/19 12:05:25 riastradh Exp $");
 
-#include <sys/radixtree.h>
+/*
+ * This is a lame-o implementation of the Linux xarray data type, which
+ * implements a map from 64-bit integers to pointers.  The operations
+ * it supports are designed to be implemented by a radix tree, but
+ * NetBSD's radixtree(9) doesn't quite support them all, and it's a bit
+ * of work to implement them, so this just uses a red/black tree
+ * instead at the cost of some performance in certain types of lookups
+ * (and negative-lookups -- finding a free key).
+ */
 
-#include <uvm/uvm_pdaemon.h>
+#include <sys/rbtree.h>
 
 #include <linux/xarray.h>
 
+struct node {
+	struct rb_node	n_rb;
+	uint64_t	n_key;
+	void		*n_datum;
+};
+
+static int
+compare_nodes(void *cookie, const void *va, const void *vb)
+{
+	const struct node *a = va, *b = vb;
+
+	if (a->n_key < b->n_key)
+		return -1;
+	if (a->n_key > b->n_key)
+		return +1;
+	return 0;
+}
+
+static int
+compare_node_key(void *cookie, const void *vn, const void *vk)
+{
+	const struct node *n = vn;
+	const uint64_t *k = vk;
+
+	if (n->n_key < *k)
+		return -1;
+	if (n->n_key > *k)
+		return +1;
+	return 0;
+}
+
+static const rb_tree_ops_t xa_rb_ops = {
+	.rbto_compare_nodes = compare_nodes,
+	.rbto_compare_key = compare_node_key,
+	.rbto_node_offset = offsetof(struct node, n_rb),
+};
+
 const struct xa_limit xa_limit_32b = { .min = 0, .max = UINT32_MAX };
 
 void
@@ -42,91 +87,102 @@ xa_init_flags(struct xarray *xa, gfp_t g
 {
 
 	mutex_init(&xa->xa_lock, MUTEX_DEFAULT, IPL_VM);
-	radix_tree_init_tree(&xa->xa_tree);
+	rb_tree_init(&xa->xa_tree, &xa_rb_ops);
 	xa->xa_gfp = gfp;
 }
 
 void
 xa_destroy(struct xarray *xa)
 {
+	struct node *n;
 
-	KASSERT(radix_tree_empty_tree_p(&xa->xa_tree));
-	radix_tree_fini_tree(&xa->xa_tree);
+	/*
+	 * Linux allows xa to remain populated on destruction; it is
+	 * our job to free any internal node structures.
+	 */
+	while ((n = RB_TREE_MIN(&xa->xa_tree)) != NULL) {
+		rb_tree_remove_node(&xa->xa_tree, n);
+		kmem_free(n, sizeof(*n));
+	}
 	mutex_destroy(&xa->xa_lock);
 }
 
 void *
 xa_load(struct xarray *xa, unsigned long key)
 {
-	void *datum;
+	const uint64_t key64 = key;
+	struct node *n;
 
 	/* XXX pserialize */
 	mutex_enter(&xa->xa_lock);
-	datum = radix_tree_lookup_node(&xa->xa_tree, key);
+	n = rb_tree_find_node(&xa->xa_tree, &key64);
 	mutex_exit(&xa->xa_lock);
 
-	return datum;
+	return n ? n->n_datum : NULL;
 }
 
 void *
 xa_store(struct xarray *xa, unsigned long key, void *datum, gfp_t gfp)
 {
-	void *collision;
-	int error;
+	struct node *n, *collision;
 
 	KASSERT(datum != NULL);
 	KASSERT(((uintptr_t)datum & 0x3) == 0);
 
-retry:	mutex_enter(&xa->xa_lock);
-	collision = radix_tree_lookup_node(&xa->xa_tree, key);
-	error = radix_tree_insert_node(&xa->xa_tree, key, datum);
+	n = kmem_zalloc(sizeof(*n), gfp & __GFP_WAIT ? KM_SLEEP : KM_NOSLEEP);
+	if (n == NULL)
+		return XA_ERROR(-ENOMEM);
+	n->n_key = key;
+	n->n_datum = datum;
+
+	mutex_enter(&xa->xa_lock);
+	collision = rb_tree_insert_node(&xa->xa_tree, n);
 	mutex_exit(&xa->xa_lock);
-	if (error) {
-		if (gfp & __GFP_WAIT) {
-			uvm_wait("xa");
-			goto retry;
-		}
-		/* XXX errno NetBSD->Linux */
-		return XA_ERROR(-error);
-	}
 
-	return collision;
+	if (collision != n) {
+		datum = collision->n_datum;
+		kmem_free(collision, sizeof(*collision));
+	}
+	return datum;
 }
 
 int
 xa_alloc(struct xarray *xa, uint32_t *idp, void *datum, struct xa_limit limit,
     gfp_t gfp)
 {
-	uint32_t key = limit.min;
-	void *collision;
+	uint64_t key64 = limit.min;
+	struct node *n, *n1, *collision __diagused;
 	int error;
 
 	KASSERTMSG(limit.min < limit.max, "min=%"PRIu32" max=%"PRIu32,
 	    limit.min, limit.max);
 
-retry:	mutex_enter(&xa->xa_lock);
-	/* XXX use the radix tree to make this fast */
-	while ((collision = radix_tree_lookup_node(&xa->xa_tree, key))
-	    != NULL) {
-		if (key++ == limit.max)
-			goto out;
-	}
-	error = radix_tree_insert_node(&xa->xa_tree, key, datum);
-out:	mutex_exit(&xa->xa_lock);
+	n = kmem_zalloc(sizeof(*n), gfp & __GFP_WAIT ? KM_SLEEP : KM_NOSLEEP);
+	if (n == NULL)
+		return -ENOMEM;
+	n->n_datum = datum;
 
-	if (collision)
-		return -EBUSY;
-	if (error) {
-		if (gfp & __GFP_WAIT) {
-			uvm_wait("xa");
-			goto retry;
+	mutex_enter(&xa->xa_lock);
+	while ((n1 = rb_tree_find_node_geq(&xa->xa_tree, &key64)) != NULL &&
+	    n1->n_key == key64) {
+		if (key64 == limit.max) {
+			error = -EBUSY;
+			goto out;
 		}
-		/* XXX errno NetBSD->Linux */
-		return -error;
+		KASSERT(key64 < UINT32_MAX);
+		key64++;
 	}
+	/* Found a hole -- insert in it.  */
+	KASSERT(n1 == NULL || n1->n_key > key64);
+	n->n_key = key64;
+	collision = rb_tree_insert_node(&xa->xa_tree, n);
+	KASSERT(collision == n);
+	error = 0;
+out:	mutex_exit(&xa->xa_lock);
 
-	/* Success!  */
-	*idp = key;
+	if (error)
+		return error;
+	*idp = key64;
 	return 0;
 }
 
@@ -134,23 +190,20 @@ void *
 xa_find(struct xarray *xa, unsigned long *startp, unsigned long max,
     unsigned tagmask)
 {
-	uint64_t key = *startp;
-	void *candidate = NULL;
+	uint64_t key64 = *startp;
+	struct node *n = NULL;
+
+	KASSERT(tagmask == -1);	/* not yet supported */
 
 	mutex_enter(&xa->xa_lock);
-	for (; key < max; key++, candidate = NULL) {
-		candidate = radix_tree_lookup_node(&xa->xa_tree, key);
-		if (candidate == NULL)
-			continue;
-		if (tagmask == -1 ||
-		    radix_tree_get_tag(&xa->xa_tree, key, tagmask))
-			break;
-	}
+	n = rb_tree_find_node_geq(&xa->xa_tree, &key64);
 	mutex_exit(&xa->xa_lock);
 
-	if (candidate)
-		*startp = key;
-	return candidate;
+	if (n == NULL || n->n_key > max)
+		return NULL;
+
+	*startp = n->n_key;
+	return n->n_datum;
 }
 
 void *
@@ -171,11 +224,19 @@ xa_find_after(struct xarray *xa, unsigne
 void *
 xa_erase(struct xarray *xa, unsigned long key)
 {
-	void *datum;
+	uint64_t key64 = key;
+	struct node *n;
+	void *datum = NULL;
 
 	mutex_enter(&xa->xa_lock);
-	datum = radix_tree_remove_node(&xa->xa_tree, key);
+	n = rb_tree_find_node(&xa->xa_tree, &key64);
+	if (n)
+		rb_tree_remove_node(&xa->xa_tree, n);
 	mutex_exit(&xa->xa_lock);
 
+	if (n) {
+		datum = n->n_datum;
+		kmem_free(n, sizeof(*n));
+	}
 	return datum;
 }

Reply via email to