Module Name: src
Committed By: riastradh
Date: Wed Jan 15 13:51:48 UTC 2014
Modified Files:
src/sys/external/bsd/drm2/include/linux [riastradh-drm2]: idr.h
src/sys/external/bsd/drm2/linux [riastradh-drm2]: linux_idr.c
Log Message:
Revert "Rewrite idr to use a dumber algorithm that admits pserialized use."
This reverts commit 3a389a1cb20777fb73575f0514b96265052ac1ea.
I don't know what I was smoking with this; just need to change the
rwlock to a spin lock and we'll be good!
To generate a diff of this commit:
cvs rdiff -u -r1.1.2.7 -r1.1.2.8 \
src/sys/external/bsd/drm2/include/linux/idr.h
cvs rdiff -u -r1.1.2.10 -r1.1.2.11 \
src/sys/external/bsd/drm2/linux/linux_idr.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/idr.h
diff -u src/sys/external/bsd/drm2/include/linux/idr.h:1.1.2.7 src/sys/external/bsd/drm2/include/linux/idr.h:1.1.2.8
--- src/sys/external/bsd/drm2/include/linux/idr.h:1.1.2.7 Mon Dec 30 04:52:11 2013
+++ src/sys/external/bsd/drm2/include/linux/idr.h Wed Jan 15 13:51:48 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: idr.h,v 1.1.2.7 2013/12/30 04:52:11 riastradh Exp $ */
+/* $NetBSD: idr.h,v 1.1.2.8 2014/01/15 13:51:48 riastradh Exp $ */
/*-
* Copyright (c) 2013 The NetBSD Foundation, Inc.
@@ -33,16 +33,15 @@
#define _LINUX_IDR_H_
#include <sys/types.h>
-#include <sys/mutex.h>
-#include <sys/pserialize.h>
+#include <sys/rwlock.h>
+#include <sys/rbtree.h>
/* XXX Stupid expedient algorithm should be replaced by something better. */
struct idr {
- kmutex_t idr_lock;
- pserialize_t idr_psz;
- struct idr_state *idr_state;
- struct idr_state *volatile idr_temp;
+ krwlock_t idr_lock;
+ rb_tree_t idr_tree;
+ struct idr_node *idr_temp;
};
/* XXX Make the nm output a little more greppable... */
Index: src/sys/external/bsd/drm2/linux/linux_idr.c
diff -u src/sys/external/bsd/drm2/linux/linux_idr.c:1.1.2.10 src/sys/external/bsd/drm2/linux/linux_idr.c:1.1.2.11
--- src/sys/external/bsd/drm2/linux/linux_idr.c:1.1.2.10 Mon Dec 30 04:52:11 2013
+++ src/sys/external/bsd/drm2/linux/linux_idr.c Wed Jan 15 13:51:48 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: linux_idr.c,v 1.1.2.10 2013/12/30 04:52:11 riastradh Exp $ */
+/* $NetBSD: linux_idr.c,v 1.1.2.11 2014/01/15 13:51:48 riastradh Exp $ */
/*-
* Copyright (c) 2013 The NetBSD Foundation, Inc.
@@ -30,226 +30,215 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_idr.c,v 1.1.2.10 2013/12/30 04:52:11 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_idr.c,v 1.1.2.11 2014/01/15 13:51:48 riastradh Exp $");
#include <sys/param.h>
#include <sys/atomic.h>
-#include <sys/mutex.h>
-#include <sys/pserialize.h>
+#include <sys/kmem.h>
+#include <sys/rbtree.h>
#include <linux/err.h>
#include <linux/idr.h>
-#include <linux/slab.h>
-struct idr_state {
- size_t is_n_allocated;
- size_t is_n_used;
- void **is_data;
+struct idr_node {
+ rb_node_t in_rb_node;
+ int in_index;
+ void *in_data;
};
-void
-idr_init(struct idr *idr)
+static signed int idr_tree_compare_nodes(void *, const void *, const void *);
+static signed int idr_tree_compare_key(void *, const void *, const void *);
+
+static const rb_tree_ops_t idr_rb_ops = {
+ .rbto_compare_nodes = &idr_tree_compare_nodes,
+ .rbto_compare_key = &idr_tree_compare_key,
+ .rbto_node_offset = offsetof(struct idr_node, in_rb_node),
+ .rbto_context = NULL,
+};
+
+static signed int
+idr_tree_compare_nodes(void *ctx __unused, const void *na, const void *nb)
{
+ const int a = ((const struct idr_node *)na)->in_index;
+ const int b = ((const struct idr_node *)nb)->in_index;
- mutex_init(&idr->idr_lock, MUTEX_DEFAULT, IPL_VM);
- idr->idr_psz = pserialize_create();
- idr->idr_state = kmalloc(sizeof(*idr->idr_state), GFP_KERNEL);
- KASSERT(idr->idr_state != NULL);
- idr->idr_state->is_n_allocated = 1;
- idr->idr_state->is_n_used = 0;
- idr->idr_state->is_data = kcalloc(1, sizeof(void *), GFP_KERNEL);
- KASSERT(idr->idr_state->is_data != NULL);
- idr->idr_temp = NULL;
+ if (a < b)
+ return -1;
+ else if (b < a)
+ return +1;
+ else
+ return 0;
}
-static struct idr_state *
-idr_state(struct idr *idr)
+static signed int
+idr_tree_compare_key(void *ctx __unused, const void *n, const void *key)
{
- struct idr_state *const is = idr->idr_state;
-
- membar_consumer(); /* match state */
+ const int a = ((const struct idr_node *)n)->in_index;
+ const int b = *(const int *)key;
- return is;
+ if (a < b)
+ return -1;
+ else if (b < a)
+ return +1;
+ else
+ return 0;
}
void
-idr_destroy(struct idr *idr)
+idr_init(struct idr *idr)
{
- struct idr_state *const is = idr_state(idr);
-
- kfree(is->is_data);
- kfree(is);
- KASSERT(idr->idr_temp == NULL);
- pserialize_destroy(idr->idr_psz);
- mutex_destroy(&idr->idr_lock);
+ rw_init(&idr->idr_lock);
+ rb_tree_init(&idr->idr_tree, &idr_rb_ops);
+ idr->idr_temp = NULL;
}
-static void **
-idr_find_ptr(struct idr *idr, int id)
+void
+idr_destroy(struct idr *idr)
{
- if (id < 0)
- return NULL;
- struct idr_state *const is = idr_state(idr);
- if (is->is_n_used <= id)
- return NULL;
-
- return &is->is_data[id];
+ if (idr->idr_temp != NULL) {
+ /* XXX Probably shouldn't happen. */
+ kmem_free(idr->idr_temp, sizeof(*idr->idr_temp));
+ idr->idr_temp = NULL;
+ }
+#if 0 /* XXX No rb_tree_destroy? */
+ rb_tree_destroy(&idr->idr_tree);
+#endif
+ rw_destroy(&idr->idr_lock);
}
void *
idr_find(struct idr *idr, int id)
{
- void **const ptr = idr_find_ptr(idr, id);
-
- if (ptr == NULL)
- return NULL;
+ const struct idr_node *node;
+ void *data;
- void *const datum = *ptr;
+ rw_enter(&idr->idr_lock, RW_READER);
+ node = rb_tree_find_node(&idr->idr_tree, &id);
+ data = (node == NULL? NULL : node->in_data);
+ rw_exit(&idr->idr_lock);
- membar_consumer(); /* match datum */
- return datum;
+ return data;
}
void *
idr_replace(struct idr *idr, void *replacement, int id)
{
- void **const ptr = idr_find_ptr(idr, id);
-
- if (ptr == NULL)
- return ERR_PTR(-ENOENT);
+ struct idr_node *node;
+ void *result;
- void *const datum = *ptr;
-
- membar_producer(); /* match datum */
- *ptr = replacement;
+ rw_enter(&idr->idr_lock, RW_WRITER);
+ node = rb_tree_find_node(&idr->idr_tree, &id);
+ if (node == NULL) {
+ result = ERR_PTR(-ENOENT);
+ } else {
+ result = node->in_data;
+ node->in_data = replacement;
+ }
+ rw_exit(&idr->idr_lock);
- return datum;
+ return result;
}
void
idr_remove(struct idr *idr, int id)
{
- if (id < 0)
- return;
+ struct idr_node *node;
- struct idr_state *const is = idr_state(idr);
-
- if (is->is_n_used < id)
- return;
-
- is->is_data[id] = NULL;
-
- if ((id + 1) == is->is_n_used) {
- while ((0 < id--) && (is->is_data[id] == NULL))
- continue;
- is->is_n_used = id;
- }
+ rw_enter(&idr->idr_lock, RW_WRITER);
+ node = rb_tree_find_node(&idr->idr_tree, &id);
+ KASSERT(node != NULL);
+ rb_tree_remove_node(&idr->idr_tree, node);
+ rw_exit(&idr->idr_lock);
+ kmem_free(node, sizeof(*node));
}
void
idr_remove_all(struct idr *idr)
{
- struct idr_state *const is = idr_state(idr);
+ struct idr_node *node;
- is->is_n_used = 0;
+ rw_enter(&idr->idr_lock, RW_WRITER);
+ while ((node = RB_TREE_MIN(&idr->idr_tree)) != NULL) {
+ rb_tree_remove_node(&idr->idr_tree, node);
+ rw_exit(&idr->idr_lock);
+ kmem_free(node, sizeof(*node));
+ rw_enter(&idr->idr_lock, RW_WRITER);
+ }
+ rw_exit(&idr->idr_lock);
}
int
-idr_pre_get(struct idr *idr, gfp_t gfp)
+idr_pre_get(struct idr *idr, int flags __unused /* XXX */)
{
- struct idr_state *old_is, *new_is, *temp_is;
- size_t n_used;
+ struct idr_node *temp = kmem_alloc(sizeof(*temp), KM_SLEEP);
- old_is = idr_state(idr);
- n_used = old_is->is_n_used;
-
- new_is = kmalloc(sizeof(*new_is), gfp);
- if (new_is == NULL)
- return 0;
- new_is->is_data = kcalloc((n_used + 1), sizeof(*new_is->is_data), gfp);
- if (new_is->is_data == NULL) {
- kfree(new_is);
- return 0;
+ rw_enter(&idr->idr_lock, RW_WRITER);
+ if (idr->idr_temp == NULL) {
+ idr->idr_temp = temp;
+ temp = NULL;
}
+ rw_exit(&idr->idr_lock);
- new_is->is_n_allocated = (n_used + 1);
-
- membar_producer(); /* match temp */
-
- /*
- * If someone already put one in, replace it -- we're probably
- * more up-to-date.
- */
- temp_is = atomic_swap_ptr(&idr->idr_temp, new_is);
- if (temp_is != NULL) {
- membar_consumer(); /* match temp */
- kfree(temp_is->is_data);
- kfree(temp_is);
- }
+ if (temp != NULL)
+ kmem_free(temp, sizeof(*temp));
return 1;
}
int
-idr_get_new_above(struct idr *idr, void *datum, int min_id, int *idp)
+idr_get_new_above(struct idr *idr, void *data, int min_id, int *id)
{
- struct idr_state *is = idr_state(idr);
- const size_t n_used = is->is_n_used;
- const size_t n_allocated = is->is_n_allocated;
- int id;
-
- if (min_id < 0)
- min_id = 0;
-
- for (id = min_id; id < n_used; id++)
- if (is->is_data[id] == NULL)
- goto win;
- if (id < n_allocated) {
- is->is_n_used++;
- goto win;
- }
-
- struct idr_state *const it = atomic_swap_ptr(&idr->idr_temp, NULL);
- if (it == NULL)
- return -EAGAIN;
-
- membar_consumer(); /* match temp */
- if (id < it->is_n_allocated) {
- (void)memcpy(it->is_data, is->is_data, sizeof(void *) *
- n_used);
- it->is_n_used = (is->is_n_used + 1);
- membar_producer(); /* match state */
- idr->idr_state = it;
- pserialize_perform(idr->idr_psz);
- kfree(is->is_data);
- kfree(is);
- is = it;
- goto win;
- }
-
-win: membar_producer(); /* match datum */
- is->is_data[id] = datum;
- *idp = id;
- return 0;
+ struct idr_node *node, *search, *collision __unused;
+ int want_id = min_id;
+ int error;
+
+ rw_enter(&idr->idr_lock, RW_WRITER);
+
+ node = idr->idr_temp;
+ if (node == NULL) {
+ error = -EAGAIN;
+ goto out;
+ }
+ idr->idr_temp = NULL;
+
+ search = rb_tree_find_node_geq(&idr->idr_tree, &min_id);
+ while ((search != NULL) && (search->in_index == want_id)) {
+ if (want_id == INT_MAX) {
+ error = -ENOSPC;
+ goto out;
+ }
+ search = rb_tree_iterate(&idr->idr_tree, search, RB_DIR_RIGHT);
+ want_id++;
+ }
+
+ node->in_index = want_id;
+ node->in_data = data;
+
+ collision = rb_tree_insert_node(&idr->idr_tree, node);
+ KASSERT(collision == node);
+
+ *id = want_id;
+ error = 0;
+
+out: rw_exit(&idr->idr_lock);
+ return error;
}
int
idr_for_each(struct idr *idr, int (*proc)(int, void *, void *), void *arg)
{
- struct idr_state *const is = idr_state(idr);
- int id;
+ struct idr_node *node;
int error = 0;
- for (id = 0; id < is->is_n_used; id++) {
- void *const datum = is->is_data[id];
-
- membar_consumer(); /* match datum */
- error = (*proc)(id, datum, arg);
+ rw_enter(&idr->idr_lock, RW_READER);
+ RB_TREE_FOREACH(node, &idr->idr_tree) {
+ error = (*proc)(node->in_index, node->in_data, arg);
if (error)
break;
}
+ rw_exit(&idr->idr_lock);
return error;
}