From: Corey Minyard 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 _