diff --git a/sys/dev/drm/drm_crtc.c b/sys/dev/drm/drm_crtc.c
index 32b1c8e..203dc34 100644
--- a/sys/dev/drm/drm_crtc.c
+++ b/sys/dev/drm/drm_crtc.c
@@ -223,12 +223,12 @@ static int drm_mode_object_get(struct drm_device *dev,
 	int ret;
 
 again:
-	if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) {
+	spin_lock(&dev->mode_config.idr_mutex);
+	if (idr_pre_get(&dev->mode_config.idr_mutex,
+			&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) {
 		DRM_ERROR("Ran out memory getting a mode number\n");
 		return -ENOMEM;
 	}
-
-	spin_lock(&dev->mode_config.idr_mutex);
 	ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id);
 	spin_unlock(&dev->mode_config.idr_mutex);
 	if (ret == -EAGAIN)
diff --git a/sys/dev/drm/drm_gem.c b/sys/dev/drm/drm_gem.c
index 7fabfda..1dfa56e 100644
--- a/sys/dev/drm/drm_gem.c
+++ b/sys/dev/drm/drm_gem.c
@@ -272,12 +272,14 @@ drm_gem_handle_create(struct drm_file *file_priv,
 	 * Get the user-visible handle using idr.
 	 */
 again:
+	spin_lock(&file_priv->table_lock);
 	/* ensure there is space available to allocate a handle */
-	if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0)
+	if (idr_pre_get(&file_priv->table_lock,
+			&file_priv->object_idr, GFP_KERNEL) == 0) {
 		return -ENOMEM;
+	}
 
 	/* do the allocation under our spinlock */
-	spin_lock(&file_priv->table_lock);
 	ret = idr_get_new_above(&file_priv->object_idr, obj, 1, (int *)handlep);
 	spin_unlock(&file_priv->table_lock);
 	if (ret == -EAGAIN)
@@ -356,12 +358,12 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
 		return -ENOENT;
 
 again:
-	if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) {
+	spin_lock(&dev->object_name_lock);
+	if (idr_pre_get(&dev->object_name_lock,
+			&dev->object_name_idr, GFP_KERNEL) == 0) {
 		ret = -ENOMEM;
 		goto err;
 	}
-
-	spin_lock(&dev->object_name_lock);
 	if (!obj->name) {
 		ret = idr_get_new_above(&dev->object_name_idr, obj, 1,
 					&obj->name);
diff --git a/sys/libkern/linux_idr.c b/sys/libkern/linux_idr.c
index af93165..44e1363 100644
--- a/sys/libkern/linux_idr.c
+++ b/sys/libkern/linux_idr.c
@@ -49,11 +49,10 @@
 
 MALLOC_DEFINE(M_IDR, "idr", "Integer ID management");
 
-static void	idr_grow(struct idr *idp, int want);
+static void	idr_grow(struct spinlock *spin, struct idr *idp, int want);
 static void	idr_reserve(struct idr *idp, int id, int incr);
 static void	idr_set(struct idr *idp, int id, void *ptr);
 static int	idr_find_free(struct idr *idp, int want, int lim);
-static int	idr_pre_get1(struct idr *idp, int want, int lim);
 
 /*
  * Number of nodes in right subtree, including the root.
@@ -168,41 +167,64 @@ idr_find_free(struct idr *idp, int want, int lim)
 	return (-1);
 }
 
-static int
-idr_pre_get1(struct idr *idp, int want, int lim)
+/*
+ * Blocking pre-get support, allows callers to use idr_pre_get() in
+ * combination with idr_get_new_above() such that idr_get_new_above()
+ * can be called safely with a spinlock held.
+ *
+ * Returns 0 on failure, 1 on success.
+ *
+ * Caller must hold spinlock, this routine may temporarily release the
+ * spinlock.
+ */
+int
+idr_pre_get(struct spinlock *spin, struct idr *idp, __unused unsigned gfp_mask)
 {
+	int want = idp->idr_maxwant;
+	int lim = INT_MAX;
+	int result = 1;				/* success */
 	int id;
 
-	if (want >= idp->idr_count)
-		idr_grow(idp, want);
+	for (;;) {
+		/*
+		 * Grow if necessary (or if forced by the loop)
+		 */
+		if (want >= idp->idr_count)
+			idr_grow(spin, idp, want);
 
-retry:
-	id = idr_find_free(idp, want, lim);
-	if (id > -1)
-		goto found;
+		/*
+		 * Check if a spot is available, break and return 0 if true,
+		 * unless the available spot is beyond our limit.  It is
+		 * possible to exceed the limit due to the way array growth
+		 * works.
+		 */
+		id = idr_find_free(idp, want, lim);
+		if (id != -1) {
+			if (id >= lim)
+				result = 0;	/* failure */
+			break;
+		}
 
-	/*
-	 * No space in current array.  Expand?
-	 */
-	if (idp->idr_count >= lim) {
-		return (ENOSPC);
+		/*
+		 * Return ENOSPC if our limit has been reached, otherwise
+		 * loop and force growth.
+		 */
+		if (idp->idr_count >= lim) {
+			result = 0;		/* failure */
+			break;
+		}
+		want = idp->idr_count;
 	}
-	idr_grow(idp, want);
-	goto retry;
-
-found:
-	return (0);
-}
-
-int
-idr_pre_get(struct idr *idp, __unused unsigned gfp_mask)
-{
-	lwkt_gettoken(&idp->idr_token);
-	int error = idr_pre_get1(idp, idp->idr_maxwant, INT_MAX);
-	lwkt_reltoken(&idp->idr_token);
-	return (error == 0);
+	return result;
 }
 
+/*
+ * Allocate an integer.  If -EAGAIN is returned the caller should loop
+ * and call idr_pre_get() with no locks held, and then retry the call
+ * to idr_get_new_above().
+ *
+ * Can be safely called with spinlocks held.
+ */
 int
 idr_get_new_above(struct idr *idp, void *ptr, int sid, int *id)
 {
@@ -210,21 +232,18 @@ idr_get_new_above(struct idr *idp, void *ptr, int sid, int *id)
 
 	KKASSERT(ptr != NULL);
 
-	lwkt_gettoken(&idp->idr_token);
 	if (sid >= idp->idr_count) {
 		idp->idr_maxwant = max(idp->idr_maxwant, sid);
-		lwkt_reltoken(&idp->idr_token);
 		return -EAGAIN;
 	}
 
 	resid = idr_find_free(idp, sid, INT_MAX);
 	if (resid == -1) {
-		lwkt_reltoken(&idp->idr_token);
 		return -EAGAIN;
 	}
 
 	if (resid >= idp->idr_count)
-		idr_grow(idp, resid);
+		panic("idr_get_new_above(): illegal resid %d", resid);
 	if (resid > idp->idr_lastindex)
 		idp->idr_lastindex = resid;
 	if (sid <= idp->idr_freeindex)
@@ -235,7 +254,6 @@ idr_get_new_above(struct idr *idp, void *ptr, int sid, int *id)
 	idr_reserve(idp, resid, 1);
 	idr_set(idp, resid, ptr);
 
-	lwkt_reltoken(&idp->idr_token);
 	return (0);
 }
 
@@ -247,9 +265,12 @@ idr_get_new(struct idr *idp, void *ptr, int *id)
 
 /*
  * Grow the file table so it can hold through descriptor (want).
+ *
+ * Caller must hold spinlock, this routine may temporarily release the
+ * spinlock.
  */
 static void
-idr_grow(struct idr *idp, int want)
+idr_grow(struct spinlock *spin, struct idr *idp, int want)
 {
 	struct idr_node *oldnodes, *newnodes;
 	int nf;
@@ -260,27 +281,39 @@ idr_grow(struct idr *idp, int want)
 		nf = 2 * nf + 1;
 	} while (nf <= want);
 
+	if (nf <= idp->idr_count)
+		return;
+
 	/* Allocate a new zero'ed node array */
-	newnodes = kmalloc(nf * sizeof(struct idr_node), M_IDR, M_ZERO|M_WAITOK);
-	
+	spin_unlock(spin);
+	newnodes = kmalloc(nf * sizeof(struct idr_node),
+			   M_IDR, M_ZERO|M_WAITOK);
+	spin_lock(spin);
+
 	/* We might race another grow */
 	if (nf <= idp->idr_count) {
+		spin_unlock(spin);
 		kfree(newnodes, M_IDR);
+		spin_lock(spin);
 		return;
 	}
 
-	/* Copy the existing nodes at the beginning of the new array */
-	if (idp->idr_nodes != NULL)
-		bcopy(idp->idr_nodes, newnodes, idp->idr_count * sizeof(struct idr_node));
-
+	/*
+	 * Copy existing nodes to the beginning of the new array
+	 */
 	oldnodes = idp->idr_nodes;
+	if (oldnodes) {
+		bcopy(idp->idr_nodes, newnodes,
+		      idp->idr_count * sizeof(struct idr_node));
+	}
 	idp->idr_nodes = newnodes;
 	idp->idr_count = nf;
 
-	if (oldnodes != NULL) {
+	if (oldnodes) {
+		spin_unlock(spin);
 		kfree(oldnodes, M_IDR);
+		spin_lock(spin);
 	}
-
 	idp->idr_nexpands++;
 }
 
@@ -289,73 +322,54 @@ idr_remove(struct idr *idp, int id)
 {
 	void *ptr;
 
-	lwkt_gettoken(&idp->idr_token);
-
 	if (id < 0 || id >= idp->idr_count)
-		goto out;
+		return;
 	if ((ptr = idp->idr_nodes[id].data) == NULL)
-		goto out;
+		return;
 	idp->idr_nodes[id].data = NULL;
 
 	idr_reserve(idp, id, -1);
 	idrfixup(idp, id);
-
-out:
-	lwkt_reltoken(&idp->idr_token);
 }
 
+/*
+ * Remove all int allocations, leave array intact.
+ *
+ * Caller must hold spinlock (or be in a context where holding the
+ * spinlock is not relevant).
+ */
 void
 idr_remove_all(struct idr *idp)
 {
-	struct      idr_node *oldnodes;
-	struct      idr_node *newnodes;
-
-	lwkt_gettoken(&idp->idr_token);
-
-retry:
-	oldnodes = idp->idr_nodes;
-	newnodes = kmalloc(idp->idr_count * sizeof(struct idr_node), M_IDR, M_WAITOK | M_ZERO);
-
-	if (idp->idr_nodes != oldnodes) {
-		kfree(newnodes, M_IDR);
-		goto retry;
-	}
-
-	idp->idr_nodes = newnodes;
+	bzero(idp->idr_nodes, idp->idr_count * sizeof(struct idr_node));
 	idp->idr_lastindex = -1;
 	idp->idr_freeindex = 0;
 	idp->idr_nexpands = 0;
 	idp->idr_maxwant = 0;
-	lwkt_reltoken(&idp->idr_token);
-
-	kfree(oldnodes, M_IDR);
-
 }
 
 void
 idr_destroy(struct idr *idp)
 {
-	lwkt_token_uninit(&idp->idr_token);
-	kfree(idp->idr_nodes, M_IDR);
-	memset(idp, 0, sizeof(struct idr));
+	if (idp->idr_nodes) {
+		kfree(idp->idr_nodes, M_IDR);
+		idp->idr_nodes = NULL;
+	}
+	bzero(idp, sizeof(*idp));
 }
 
 void *
 idr_find(struct idr *idp, int id)
 {
-	void * ret = NULL;
-
-	lwkt_gettoken(&idp->idr_token);
+	void *ret;
 
 	if (id < 0 || id >= idp->idr_count) {
-		goto out;
+		ret = NULL;
 	} else if (idp->idr_nodes[id].allocated == 0) {
-		goto out;
+		ret = NULL;
+	} else {
+		ret = idp->idr_nodes[id].data;
 	}
-	ret = idp->idr_nodes[id].data;
-
-out:
-	lwkt_reltoken(&idp->idr_token);
 	return ret;
 }
 
@@ -375,22 +389,20 @@ idr_set(struct idr *idp, int id, void *ptr)
 }
 
 int
-idr_for_each(struct idr *idp, int (*fn)(int id, void *p, void *data), void *data)
+idr_for_each(struct idr *idp, int (*fn)(int id, void *p, void *data),
+	     void *data)
 {
 	int i, error = 0;
 	struct idr_node *nodes;
 
-	lwkt_gettoken(&idp->idr_token);
 	nodes = idp->idr_nodes;
 	for (i = 0; i < idp->idr_count; i++) {
 		if (nodes[i].data != NULL && nodes[i].allocated > 0) {
 			error = fn(i, nodes[i].data, data);
 			if (error != 0)
-				goto out;
+				break;
 		}
 	}
-out:
-	lwkt_reltoken(&idp->idr_token);
 	return error;
 }
 
@@ -398,19 +410,15 @@ void *
 idr_replace(struct idr *idp, void *ptr, int id)
 {
 	struct idr_node *idrnp;
-	void *ret = NULL;
-
-	lwkt_gettoken(&idp->idr_token);
+	void *ret;
 
 	idrnp = idr_get_node(idp, id);
-	if (idrnp == NULL || ptr == NULL)
-		goto out;
-
-	ret = idrnp->data;
-	idrnp->data = ptr;
-
-out:
-	lwkt_reltoken(&idp->idr_token);
+	if (idrnp == NULL || ptr == NULL) {
+		ret = NULL;
+	} else {
+		ret = idrnp->data;
+		idrnp->data = ptr;
+	}
 	return (ret);
 }
 
@@ -423,5 +431,4 @@ idr_init(struct idr *idp)
 	idp->idr_count = IDR_DEFAULT_SIZE;
 	idp->idr_lastindex = -1;
 	idp->idr_maxwant = 0;
-	lwkt_token_init(&idp->idr_token, "idr token");
 }
diff --git a/sys/sys/idr.h b/sys/sys/idr.h
index 0ccb2a5..b1cd2aa 100644
--- a/sys/sys/idr.h
+++ b/sys/sys/idr.h
@@ -69,7 +69,6 @@ struct idr {
 	int	    idr_freeindex;
 	int	    idr_nexpands;
 	int	    idr_maxwant;
-	struct	    lwkt_token idr_token;
 };
 
 void	*idr_find(struct idr *idp, int id);
@@ -80,7 +79,7 @@ void	 idr_destroy(struct idr *idp);
 int	 idr_for_each(struct idr *idp, int (*fn)(int id, void *p, void *data), void *data);
 int	 idr_get_new(struct idr *idp, void *ptr, int *id);
 int	 idr_get_new_above(struct idr *idp, void *ptr, int sid, int *id);
-int	 idr_pre_get(struct idr *idp, unsigned gfp_mask);
+int	 idr_pre_get(struct spinlock *spin, struct idr *idp, unsigned gfp_mask);
 
 void	 idr_init(struct idr *idp);
 
