
From: Corey Minyard <minyard@acm.org>

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.


---

 25-akpm/fs/proc/generic.c        |   14 +++------
 25-akpm/fs/super.c               |   17 ++++-------
 25-akpm/include/linux/idr.h      |    3 -
 25-akpm/kernel/posix-timers.c    |   59 +++++++++++++++++++--------------------
 25-akpm/lib/idr.c                |   33 ++++++++++++++-------
 25-akpm/net/sctp/sm_make_chunk.c |   27 +++++++++--------
 6 files changed, 80 insertions(+), 73 deletions(-)

diff -puN fs/proc/generic.c~idr-overflow-fixes-2 fs/proc/generic.c
--- 25/fs/proc/generic.c~idr-overflow-fixes-2	2004-05-08 22:19:23.142866856 -0700
+++ 25-akpm/fs/proc/generic.c	2004-05-08 22:19:23.154865032 -0700
@@ -288,22 +288,20 @@ static spinlock_t proc_inum_lock = SPIN_
  */
 static unsigned int get_inode_number(void)
 {
-	unsigned int i, inum = 0;
+	int i, inum = 0;
+	int error;
 
 retry:
 	if (idr_pre_get(&proc_inum_idr, GFP_KERNEL) == 0)
 		return 0;
 
 	spin_lock(&proc_inum_lock);
-	i = idr_get_new(&proc_inum_idr, NULL);
-	if ((i == -1) && idr_full(&proc_inum_idr)) {
-		spin_unlock(&proc_inum_lock);
-		return 0;
-	}
+	error = idr_get_new(&proc_inum_idr, NULL, &i);
 	spin_unlock(&proc_inum_lock);
-
-	if (i == -1)
+	if (error == -EAGAIN)
 		goto retry;
+	else if (error)
+		return 0;
 
 	inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST;
 
diff -puN fs/super.c~idr-overflow-fixes-2 fs/super.c
--- 25/fs/super.c~idr-overflow-fixes-2	2004-05-08 22:19:23.144866552 -0700
+++ 25-akpm/fs/super.c	2004-05-08 22:19:23.155864880 -0700
@@ -555,22 +555,19 @@ static spinlock_t unnamed_dev_lock = SPI
 int set_anon_super(struct super_block *s, void *data)
 {
 	int dev;
+	int error;
 
  retry:
-	spin_lock(&unnamed_dev_lock);
-	if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0) {
-		spin_unlock(&unnamed_dev_lock);
+	if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0)
 		return -ENOMEM;
-	}
-	dev = idr_get_new(&unnamed_dev_idr, NULL);
-	if ((dev == -1) && idr_full(&unnamed_dev_idr)) {
-		spin_unlock(&unnamed_dev_lock);
-		return -EAGAIN;
-	}
+	spin_lock(&unnamed_dev_lock);
+	error = idr_get_new(&unnamed_dev_idr, NULL, &dev);
 	spin_unlock(&unnamed_dev_lock);
-	if (dev == -1)
+	if (error == -EAGAIN)
 		/* We raced and lost with another CPU. */
 		goto retry;
+	else if (error)
+		return -EAGAIN;
 
 	if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {
 		spin_lock(&unnamed_dev_lock);
diff -puN include/linux/idr.h~idr-overflow-fixes-2 include/linux/idr.h
--- 25/include/linux/idr.h~idr-overflow-fixes-2	2004-05-08 22:19:23.145866400 -0700
+++ 25-akpm/include/linux/idr.h	2004-05-08 22:19:23.155864880 -0700
@@ -77,10 +77,9 @@ struct idr {
 
 void *idr_find(struct idr *idp, int id);
 int idr_pre_get(struct idr *idp, unsigned gfp_mask);
-int idr_get_new(struct idr *idp, void *ptr);
+int idr_get_new(struct idr *idp, void *ptr, int *id);
 void idr_remove(struct idr *idp, int id);
 void idr_init(struct idr *idp);
-int idr_full(struct idr *idp);
 
 extern kmem_cache_t *idr_layer_cache;
 
diff -puN kernel/posix-timers.c~idr-overflow-fixes-2 kernel/posix-timers.c
--- 25/kernel/posix-timers.c~idr-overflow-fixes-2	2004-05-08 22:19:23.147866096 -0700
+++ 25-akpm/kernel/posix-timers.c	2004-05-08 22:19:23.157864576 -0700
@@ -397,7 +397,6 @@ static struct k_itimer * alloc_posix_tim
 	if (!tmr)
 		return tmr;
 	memset(tmr, 0, sizeof (struct k_itimer));
-	tmr->it_id = (timer_t)-1;
 	if (unlikely(!(tmr->sigq = sigqueue_alloc()))) {
 		kmem_cache_free(posix_timers_cache, tmr);
 		tmr = 0;
@@ -405,9 +404,11 @@ static struct k_itimer * alloc_posix_tim
 	return tmr;
 }
 
-static void release_posix_timer(struct k_itimer *tmr)
+#define IT_ID_SET	1
+#define IT_ID_NOT_SET	0
+static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
 {
-	if (tmr->it_id != -1) {
+	if (it_id_set) {
 		unsigned long flags;
 		spin_lock_irqsave(&idr_lock, flags);
 		idr_remove(&posix_timers_id, tmr->it_id);
@@ -429,10 +430,11 @@ sys_timer_create(clockid_t which_clock,
 {
 	int error = 0;
 	struct k_itimer *new_timer = NULL;
-	timer_t new_timer_id;
+	int new_timer_id;
 	struct task_struct *process = 0;
 	unsigned long flags;
 	sigevent_t event;
+	int it_id_set = IT_ID_NOT_SET;
 
 	if ((unsigned) which_clock >= MAX_CLOCKS ||
 				!posix_clocks[which_clock].res)
@@ -443,30 +445,29 @@ sys_timer_create(clockid_t which_clock,
 		return -EAGAIN;
 
 	spin_lock_init(&new_timer->it_lock);
-	for (;;) {
-		if (unlikely(!idr_pre_get(&posix_timers_id, GFP_KERNEL))) {
-			error = -EAGAIN;
-			new_timer->it_id = (timer_t)-1;
-			goto out;
-		}
-		spin_lock_irq(&idr_lock);
-		new_timer_id = (timer_t) idr_get_new(&posix_timers_id,
-							(void *) new_timer);
-		if (new_timer_id == -1) {
-			if (idr_full(&posix_timers_id)) {
-				spin_unlock_irq(&idr_lock);
-				error = -EAGAIN;
-				new_timer->it_id = (timer_t)-1;
-				goto out;
-			}
-			spin_unlock_irq(&idr_lock);
-			continue;
-		}
-		spin_unlock_irq(&idr_lock);
-		break;
+ retry:
+	if (unlikely(!idr_pre_get(&posix_timers_id, GFP_KERNEL))) {
+		error = -EAGAIN;
+		goto out;
+	}
+	spin_lock_irq(&idr_lock);
+	error = idr_get_new(&posix_timers_id,
+			    (void *) new_timer,
+			    &new_timer_id);
+	spin_unlock_irq(&idr_lock);
+	if (error == -EAGAIN)
+		goto retry;
+	else if (error) {
+		/*
+		 * Wierd looking, but we return EAGAIN if the IDR is
+		 * full (proper POSIX return value for this)
+		 */
+		error = -EAGAIN;
+		goto out;
 	}
 
-	new_timer->it_id = new_timer_id;
+	it_id_set = IT_ID_SET;
+	new_timer->it_id = (timer_t) new_timer_id;
 	new_timer->it_clock = which_clock;
 	new_timer->it_incr = 0;
 	new_timer->it_overrun = -1;
@@ -548,7 +549,7 @@ sys_timer_create(clockid_t which_clock,
 
 out:
 	if (error)
-		release_posix_timer(new_timer);
+		release_posix_timer(new_timer, it_id_set);
 
 	return error;
 }
@@ -969,7 +970,7 @@ retry_delete:
 	timer->it_process = NULL;
 	}
 	unlock_timer(timer, flags);
-	release_posix_timer(timer);
+	release_posix_timer(timer, IT_ID_SET);
 	return 0;
 }
 /*
@@ -1006,7 +1007,7 @@ retry_delete:
 		timer->it_process = NULL;
 	}
 	unlock_timer(timer, flags);
-	release_posix_timer(timer);
+	release_posix_timer(timer, IT_ID_SET);
 }
 
 /*
diff -puN lib/idr.c~idr-overflow-fixes-2 lib/idr.c
--- 25/lib/idr.c~idr-overflow-fixes-2	2004-05-08 22:19:23.149865792 -0700
+++ 25-akpm/lib/idr.c	2004-05-08 22:19:23.158864424 -0700
@@ -70,15 +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 or if the idr is full, it will return a -1, in
- *   which case you should check to see if the idr is full and if not
- *   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);
  
@@ -281,20 +281,31 @@ build_up:
 }
 EXPORT_SYMBOL(idr_get_new_above);
 
-int idr_full(struct idr *idp)
+static int idr_full(struct idr *idp)
 {
 	return ((idp->layers >= MAX_LEVEL)
 		&& (idp->top->bitmap == TOP_LEVEL_FULL));
 }
-EXPORT_SYMBOL(idr_full);
 
-int idr_get_new(struct idr *idp, void *ptr)
+int idr_get_new(struct idr *idp, void *ptr, int *id)
 {
-	return idr_get_new_above(idp, ptr, 0);
+	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)
 {
 	struct idr_layer *p = idp->top;
diff -puN net/sctp/sm_make_chunk.c~idr-overflow-fixes-2 net/sctp/sm_make_chunk.c
--- 25/net/sctp/sm_make_chunk.c~idr-overflow-fixes-2	2004-05-08 22:19:23.150865640 -0700
+++ 25-akpm/net/sctp/sm_make_chunk.c	2004-05-08 22:19:23.160864120 -0700
@@ -1835,26 +1835,27 @@ int sctp_process_init(struct sctp_associ
 	 */
 	if (!asoc->temp) {
 		int assoc_id;
+		int error;
 
 		asoc->ssnmap = sctp_ssnmap_new(asoc->c.sinit_max_instreams,
 					       asoc->c.sinit_num_ostreams, gfp);
 		if (!asoc->ssnmap)
 			goto clean_up;
 
-		do {
-			if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp)))
-				goto clean_up;
-			spin_lock_bh(&sctp_assocs_id_lock);
-			assoc_id = idr_get_new(&sctp_assocs_id, (void *)asoc);
-			if (assoc_id == -1 && idr_full(&sctp_assocs_id)) {
-				spin_unlock_bh(&sctp_assocs_id_lock);
-				goto clean_up;
-			}
-
-			spin_unlock_bh(&sctp_assocs_id_lock);
-		} while (unlikely(assoc_id == -1));
+	retry:
+		if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp)))
+			goto clean_up;
+		spin_lock_bh(&sctp_assocs_id_lock);
+		error = idr_get_new(&sctp_assocs_id,
+				    (void *)asoc,
+				    &assoc_id);
+		spin_unlock_bh(&sctp_assocs_id_lock);
+		if (error == -EAGAIN)
+			goto retry;
+		else if (error)
+			goto clean_up;
 
-		asoc->assoc_id = (sctp_assoc_t)assoc_id;
+		asoc->assoc_id = (sctp_assoc_t) assoc_id;
 	}
 
 	/* ADDIP Section 4.1 ASCONF Chunk Procedures

_
