aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorCorey Minyard <minyard@acm.org>2004-06-17 17:55:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-06-17 17:55:36 -0700
commit90e518e12d2393e2fd818860915dc9d3937cd88f (patch)
treeb6eb261a895fa14675fb820f46f5c716d7e587a7 /lib
parentf1372916922995b0ef43c1dfabaeceee9bea71e5 (diff)
downloadhistory-90e518e12d2393e2fd818860915dc9d3937cd88f.tar.gz
[PATCH] Fixes for idr code
* On a 32-bit architecture, the idr code will cease to work if you add more than 2^20 entries. You will not be able to find many of the entries. The problem is that the IDR code uses 5-bit chunks of the number and the lower portion used by IDR is 24 bits, so you have one bit that leaks over into the comparisons that should not be there. The solution is to mask off that bit before doing IDR processing. This actually causes the POSIX timer code to crash if you create that many timers. I have included an idr_test.tar.gz file that demonstrates this with and without the fix, in case you need more evidence :). * When the IDR fills up, it returns -1. However, there was no way to check for this condition. This patch adds the ability to check for the idr being full and fixes all the users. It also fixes a problem in fs/super.c where the idr code wasn't checking for -1. * There was a race condition creating POSIX timers. The timer was added to a task struct for another process then the data for the timer was filled out. The other task could use/destroy time timer as soon as it is in the task's queue and the lock is released. This moves settup up the timer data to before the timer is enqueued or (for some data) into the lock. * Change things so that the caller doesn't need to run idr_full() to find out the reason for an idr_get_new() failure. Just return -ENOSPC if the tree was full, or -EAGAIN if the caller needs to re-run idr_pre_get() and try again. Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/idr.c45
1 files changed, 37 insertions, 8 deletions
diff --git a/lib/idr.c b/lib/idr.c
index 8cdc5e8212d6c4..4d7963c727ddda 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -70,14 +70,15 @@
* sleep, so must not be called with any spinlocks held. If the system is
* REALLY out of memory this function returns 0, other wise 1.
- * int idr_get_new(struct idr *idp, void *ptr);
+ * int idr_get_new(struct idr *idp, void *ptr, int *id);
* This is the allocate id function. It should be called with any
* required locks. In fact, in the SMP case, you MUST lock prior to
- * calling this function to avoid possible out of memory problems. If
- * memory is required, it will return a -1, in which case you should
- * unlock and go back to the idr_pre_get() call. ptr is the pointer
- * you want associated with the id. In other words:
+ * calling this function to avoid possible out of memory problems.
+ * If memory is required, it will return -EAGAIN, you should unlock
+ * and go back to the idr_pre_get() call. If the idr is full, it
+ * will return a -ENOSPC. ptr is the pointer you want associated
+ * with the id. The value is returned in the "id" field.
* void *idr_find(struct idr *idp, int id);
@@ -92,6 +93,10 @@
* removes the given id, freeing that slot and any memory that may
* now be unused. See idr_find() for locking restrictions.
+ * int idr_full(struct idr *idp);
+
+ * Returns true if the idr is full and false if not.
+
*/
@@ -276,12 +281,30 @@ build_up:
}
EXPORT_SYMBOL(idr_get_new_above);
-int idr_get_new(struct idr *idp, void *ptr)
+static int idr_full(struct idr *idp)
{
- return idr_get_new_above(idp, ptr, 0);
+ return ((idp->layers >= MAX_LEVEL)
+ && (idp->top->bitmap == TOP_LEVEL_FULL));
}
-EXPORT_SYMBOL(idr_get_new);
+int idr_get_new(struct idr *idp, void *ptr, int *id)
+{
+ int rv;
+ rv = idr_get_new_above(idp, ptr, 0);
+ /*
+ * This is a cheap hack until the IDR code can be fixed to
+ * return proper error values.
+ */
+ if (rv == -1) {
+ if (idr_full(idp))
+ return -ENOSPC;
+ else
+ return -EAGAIN;
+ }
+ *id = rv;
+ return 0;
+}
+EXPORT_SYMBOL(idr_get_new);
static void sub_remove(struct idr *idp, int shift, int id)
{
@@ -315,6 +338,9 @@ void idr_remove(struct idr *idp, int id)
{
struct idr_layer *p;
+ /* Mask off upper bits we don't use for the search. */
+ id &= MAX_ID_MASK;
+
sub_remove(idp, (idp->layers - 1) * IDR_BITS, id);
if ( idp->top && idp->top->count == 1 &&
(idp->layers > 1) &&
@@ -350,6 +376,9 @@ void *idr_find(struct idr *idp, int id)
if ( unlikely( (id & ~(~0 << MAX_ID_SHIFT)) >> (n + IDR_BITS)))
return NULL;
#endif
+ /* Mask off upper bits we don't use for the search. */
+ id &= MAX_ID_MASK;
+
while (n > 0 && p) {
n -= IDR_BITS;
p = p->ary[(id >> n) & IDR_MASK];