> Problem is, I think, you'll need to preallocate IDR_FREE_MAX items. And > then free them all again when none of them were consumed (usual).
Actually I think it's OK if we just pass in no more than one extra layer for each try to add something with idr_get_new(). In the worst case, this leads to a lot of extra calls to idr_get_new(), but in the usual case it's fine. I'm including a lightly tested big patch with all my idr changes for comments -- I'll split it up into a form more suitable for merging. (Some of the changes are unrelated and obviously good, eg using kmem_cache_zalloc() instead of a slab cache with a constructor that does memset(0)). I'm not sure I'm thrilled with this approach, but it does seem to be a net win. With an allyesconfig with debugging options turned off (so spinlocks shrink back down to 8 bytes), I get the following: text data bss dec hex filename 24347759 5971210 2322176 32641145 1f21079 vmlinux.old 24347370 5970474 2320704 32638548 1f20654 vmlinux.new Most of the savings comes from ocfs2, which has a static array of 255 structures that each contain an idr -- so removing the lock from struct idr saves 255 * 8 = 2040 bytes. However, even without factoring that in, this does seem to be a net win: add/remove: 2/4 grow/shrink: 23/51 up/down: 719/-3215 (-2496) function old new delta idr_get_new_above 38 554 +516 dm_create 957 1000 +43 ipath_init_one 3294 3329 +35 get_layer - 32 +32 idr_alloc_layer - 16 +16 sd_probe 871 881 +10 rtc_device_register 493 503 +10 proc_register 277 286 +9 mmc_add_host_sysfs 126 135 +9 idr_add_uobj 80 85 +5 cma_alloc_port 224 229 +5 sys_timer_create 876 880 +4 set_anon_super 173 177 +4 sctp_process_init 1312 1316 +4 ib_ucm_ctx_alloc 197 201 +4 ib_create_cm_id 287 290 +3 proc_mkdir_mode 95 97 +2 vfs_kern_mount 279 280 +1 send_mad 325 326 +1 proc_symlink 141 142 +1 proc_file_write 40 41 +1 kill_block_super 56 57 +1 get_sb_single 175 176 +1 free_proc_entry 108 109 +1 deactivate_super 126 127 +1 proc_readdir 353 352 -1 proc_getattr 40 39 -1 get_sb_nodev 150 149 -1 o2net_send_message_vec 2032 2030 -2 hwmon_device_register 198 196 -2 create_proc_entry 170 168 -2 __put_super_and_need_restart 54 52 -2 v9fs_read_work 1424 1421 -3 v9fs_mux_init 1333 1330 -3 v9fs_mux_flush_cb 303 300 -3 v9fs_mux_destroy 369 366 -3 v9fs_mux_cancel 382 379 -3 o2net_init 441 438 -3 inotify_add_watch 285 280 -5 v9fs_session_init 1490 1484 -6 unnamed_dev_idr 32 24 -8 unit_table 32 24 -8 tcp_ps 32 24 -8 sdp_ps 32 24 -8 sd_index_idr 32 24 -8 sctp_assocs_id 32 24 -8 rtc_idr 32 24 -8 query_idr 32 24 -8 proc_inum_idr 32 24 -8 posix_timers_id 32 24 -8 mmc_host_idr 32 24 -8 lpfc_hba_index 32 24 -8 ib_uverbs_srq_idr 32 24 -8 ib_uverbs_qp_idr 32 24 -8 ib_uverbs_pd_idr 32 24 -8 ib_uverbs_mw_idr 32 24 -8 ib_uverbs_mr_idr 32 24 -8 ib_uverbs_cq_idr 32 24 -8 ib_uverbs_ah_idr 32 24 -8 i2c_adapter_idr 32 24 -8 hwmon_idr 32 24 -8 ctx_id_table 32 24 -8 cm 112 104 -8 allocated_ptys 32 24 -8 _minor_idr 32 24 -8 i2c_add_adapter 494 484 -10 idr_cache_ctor 12 - -12 v9fs_get_idpool 149 134 -15 ib_cm_init 237 220 -17 free_layer 55 34 -21 idr_init 88 62 -26 idr_get_new 40 13 -27 idr_remove 357 328 -29 infinipath_init 227 197 -30 lpfc_pci_probe_one 2752 2710 -42 ptmx_open 427 379 -48 idr_pre_get 59 - -59 alloc_layer 66 - -66 idr_get_new_above_int 533 - -533 o2net_nodes 99960 97920 -2040 --- diff --git a/arch/powerpc/mm/mmu_context_64.c b/arch/powerpc/mm/mmu_context_64.c index 90a06ac..85d6a03 100644 --- a/arch/powerpc/mm/mmu_context_64.c +++ b/arch/powerpc/mm/mmu_context_64.c @@ -26,20 +26,21 @@ static DEFINE_IDR(mmu_context_idr); int init_new_context(struct task_struct *tsk, struct mm_struct *mm) { + struct idr_layer *layer = NULL; int index; int err; again: - if (!idr_pre_get(&mmu_context_idr, GFP_KERNEL)) - return -ENOMEM; - spin_lock(&mmu_context_lock); - err = idr_get_new_above(&mmu_context_idr, NULL, 1, &index); + err = idr_get_new_above(&mmu_context_idr, NULL, 1, &index, layer); spin_unlock(&mmu_context_lock); - if (err == -EAGAIN) + if (err == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + return -ENOMEM; goto again; - else if (err) + } else if (err) return err; if (index > MAX_CONTEXT) { diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index bfdb902..0ae099e 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2135,6 +2135,7 @@ #ifdef CONFIG_UNIX98_PTYS static int ptmx_open(struct inode * inode, struct file * filp) { struct tty_struct *tty; + struct idr_layer *layer = NULL; int retval; int index; int idr_ret; @@ -2143,24 +2144,27 @@ static int ptmx_open(struct inode * inod /* find a device that is not in use. */ down(&allocated_ptys_lock); - if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) { - up(&allocated_ptys_lock); - return -ENOMEM; - } - idr_ret = idr_get_new(&allocated_ptys, NULL, &index); - if (idr_ret < 0) { - up(&allocated_ptys_lock); - if (idr_ret == -EAGAIN) - return -ENOMEM; - return -EIO; - } - if (index >= pty_limit) { - idr_remove(&allocated_ptys, index); - up(&allocated_ptys_lock); - return -EIO; - } + do { + idr_ret = idr_get_new(&allocated_ptys, NULL, &index, layer); + if (idr_ret == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) { + idr_ret = -ENOMEM; + break; + } + continue; + } + + if (index >= pty_limit) { + idr_remove(&allocated_ptys, index); + idr_ret = -EIO; + } + } while (idr_ret == -EAGAIN); up(&allocated_ptys_lock); + if (idr_ret) + return idr_ret == -EAGAIN ? -ENOMEM : -EIO; + mutex_lock(&tty_mutex); retval = init_dev(ptm_driver, index, &tty); mutex_unlock(&tty_mutex); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 106fa01..82d6d04 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -38,20 +38,21 @@ static DEFINE_SPINLOCK(idr_lock); */ struct class_device *hwmon_device_register(struct device *dev) { + struct idr_layer *layer = NULL; struct class_device *cdev; int id, err; again: - if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0)) - return ERR_PTR(-ENOMEM); - spin_lock(&idr_lock); - err = idr_get_new(&hwmon_idr, NULL, &id); + err = idr_get_new(&hwmon_idr, NULL, &id, layer); spin_unlock(&idr_lock); - if (unlikely(err == -EAGAIN)) + if (unlikely(err == -EAGAIN)) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + return ERR_PTR(-ENOMEM); goto again; - else if (unlikely(err)) + } else if (unlikely(err)) return ERR_PTR(err); id = id & MAX_ID_MASK; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 9cb277d..f8aa8ea 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -151,22 +151,24 @@ static struct device_attribute dev_attr_ int i2c_add_adapter(struct i2c_adapter *adap) { int id, res = 0; + struct idr_layer *layer = NULL; struct list_head *item; struct i2c_driver *driver; mutex_lock(&core_lists); - if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) { - res = -ENOMEM; - goto out_unlock; - } - - res = idr_get_new(&i2c_adapter_idr, adap, &id); - if (res < 0) { - if (res == -EAGAIN) +again: + res = idr_get_new(&i2c_adapter_idr, adap, &id, layer); + if (res == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) { res = -ENOMEM; - goto out_unlock; + goto out_unlock; + } + goto again; } + if (res < 0) + goto out_unlock; adap->nr = id & MAX_ID_MASK; mutex_init(&adap->bus_lock); diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index f85c97f..cf14d01 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -298,6 +298,7 @@ static int cm_init_av_by_path(struct ib_ static int cm_alloc_id(struct cm_id_private *cm_id_priv) { + struct idr_layer *layer = NULL; unsigned long flags; int ret; static int next_id; @@ -305,9 +306,15 @@ static int cm_alloc_id(struct cm_id_priv do { spin_lock_irqsave(&cm.lock, flags); ret = idr_get_new_above(&cm.local_id_table, cm_id_priv, next_id++, - (__force int *) &cm_id_priv->id.local_id); + (__force int *) &cm_id_priv->id.local_id, + layer); spin_unlock_irqrestore(&cm.lock, flags); - } while( (ret == -EAGAIN) && idr_pre_get(&cm.local_id_table, GFP_KERNEL) ); + if (ret == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + ret = -ENOMEM; + } + } while (ret == -EAGAIN); return ret; } @@ -3347,7 +3354,6 @@ static int __init ib_cm_init(void) cm.remote_qp_table = RB_ROOT; cm.remote_sidr_table = RB_ROOT; idr_init(&cm.local_id_table); - idr_pre_get(&cm.local_id_table, GFP_KERNEL); cm.wq = create_workqueue("ib_cm"); if (!cm.wq) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index d6f99d5..314b150 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -1433,6 +1433,7 @@ static void cma_bind_port(struct rdma_bi static int cma_alloc_port(struct idr *ps, struct rdma_id_private *id_priv, unsigned short snum) { + struct idr_layer *layer = NULL; struct rdma_bind_list *bind_list; int port, start, ret; @@ -1443,8 +1444,13 @@ static int cma_alloc_port(struct idr *ps start = snum ? snum : sysctl_local_port_range[0]; do { - ret = idr_get_new_above(ps, bind_list, start, &port); - } while ((ret == -EAGAIN) && idr_pre_get(ps, GFP_KERNEL)); + ret = idr_get_new_above(ps, bind_list, start, &port, layer); + if (ret == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + ret = -ENOMEM; + } + } while (ret == -EAGAIN); if (ret) goto err; diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index aeda484..824b652 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -490,17 +490,20 @@ static void init_mad(struct ib_sa_mad *m static int send_mad(struct ib_sa_query *query, int timeout_ms, gfp_t gfp_mask) { + struct idr_layer *layer = NULL; unsigned long flags; int ret, id; retry: - if (!idr_pre_get(&query_idr, gfp_mask)) - return -ENOMEM; spin_lock_irqsave(&idr_lock, flags); - ret = idr_get_new(&query_idr, query, &id); + ret = idr_get_new(&query_idr, query, &id, layer); spin_unlock_irqrestore(&idr_lock, flags); - if (ret == -EAGAIN) - goto retry; + if (ret == -EAGAIN) { + layer = idr_alloc_layer(gfp_mask); + if (layer) + goto retry; + ret = -ENOMEM; + } if (ret) return ret; diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index c1c6fda..22d5e24 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -173,6 +173,7 @@ static void ib_ucm_cleanup_events(struct static struct ib_ucm_context *ib_ucm_ctx_alloc(struct ib_ucm_file *file) { + struct idr_layer *layer = NULL; struct ib_ucm_context *ctx; int result; @@ -186,13 +187,15 @@ static struct ib_ucm_context *ib_ucm_ctx INIT_LIST_HEAD(&ctx->events); do { - result = idr_pre_get(&ctx_id_table, GFP_KERNEL); - if (!result) - goto error; - mutex_lock(&ctx_id_mutex); - result = idr_get_new(&ctx_id_table, ctx, &ctx->id); + result = idr_get_new(&ctx_id_table, ctx, &ctx->id, layer); mutex_unlock(&ctx_id_mutex); + + if (result == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + result = -ENOMEM; + } } while (result == -EAGAIN); if (result) diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index bdf5d50..71dea88 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -109,18 +109,20 @@ static void put_uobj_write(struct ib_uob static int idr_add_uobj(struct idr *idr, struct ib_uobject *uobj) { + struct idr_layer *layer = NULL; int ret; -retry: - if (!idr_pre_get(idr, GFP_KERNEL)) - return -ENOMEM; + do { + spin_lock(&ib_uverbs_idr_lock); + ret = idr_get_new(idr, uobj, &uobj->id, layer); + spin_unlock(&ib_uverbs_idr_lock); - spin_lock(&ib_uverbs_idr_lock); - ret = idr_get_new(idr, uobj, &uobj->id); - spin_unlock(&ib_uverbs_idr_lock); - - if (ret == -EAGAIN) - goto retry; + if (ret == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + ret = -ENOMEM; + } + } while (ret == -EAGAIN); return ret; } diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index 823131d..6f2a711 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -168,15 +168,11 @@ static void ipath_free_devdata(struct pc static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev) { + struct idr_layer *layer = NULL; unsigned long flags; struct ipath_devdata *dd; int ret; - if (!idr_pre_get(&unit_table, GFP_KERNEL)) { - dd = ERR_PTR(-ENOMEM); - goto bail; - } - dd = vmalloc(sizeof(*dd)); if (!dd) { dd = ERR_PTR(-ENOMEM); @@ -187,7 +183,19 @@ static struct ipath_devdata *ipath_alloc spin_lock_irqsave(&ipath_devs_lock, flags); - ret = idr_get_new(&unit_table, dd, &dd->ipath_unit); + do { + ret = idr_get_new(&unit_table, dd, &dd->ipath_unit, layer); + if (ret == -EAGAIN) { + spin_unlock_irqrestore(&ipath_devs_lock, flags); + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) { + dd = ERR_PTR(-ENOMEM); + goto bail; + } + spin_lock_irqsave(&ipath_devs_lock, flags); + } + } while (ret == -EAGAIN); + if (ret < 0) { printk(KERN_ERR IPATH_DRV_NAME ": Could not allocate unit ID: error %d\n", -ret); @@ -1754,10 +1762,6 @@ static int __init infinipath_init(void) * the PCI subsystem. */ idr_init(&unit_table); - if (!idr_pre_get(&unit_table, GFP_KERNEL)) { - ret = -ENOMEM; - goto bail; - } ret = pci_register_driver(&ipath_driver); if (ret < 0) { @@ -1780,7 +1784,7 @@ static int __init infinipath_init(void) goto bail_group; } - goto bail; + return ret; bail_group: ipath_driver_remove_group(&ipath_driver.driver); @@ -1791,7 +1795,6 @@ bail_pci: bail_unit: idr_destroy(&unit_table); -bail: return ret; } diff --git a/drivers/md/dm.c b/drivers/md/dm.c index c99bf9f..e2dd20a 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -807,23 +807,30 @@ static void free_minor(int minor) */ static int specific_minor(struct mapped_device *md, int minor) { + struct idr_layer *layer = NULL; int r, m; if (minor >= (1 << MINORBITS)) return -EINVAL; - r = idr_pre_get(&_minor_idr, GFP_KERNEL); - if (!r) - return -ENOMEM; - spin_lock(&_minor_lock); +again: if (idr_find(&_minor_idr, minor)) { r = -EBUSY; goto out; } - r = idr_get_new_above(&_minor_idr, MINOR_ALLOCED, minor, &m); + r = idr_get_new_above(&_minor_idr, MINOR_ALLOCED, minor, &m, layer); + if (r == -EAGAIN) { + spin_unlock(&_minor_lock); + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + return -ENOMEM; + + spin_lock(&_minor_lock); + goto again; + } if (r) goto out; @@ -840,18 +847,21 @@ out: static int next_free_minor(struct mapped_device *md, int *minor) { + struct idr_layer *layer = NULL; int r, m; - r = idr_pre_get(&_minor_idr, GFP_KERNEL); - if (!r) - return -ENOMEM; - +again: spin_lock(&_minor_lock); - - r = idr_get_new(&_minor_idr, MINOR_ALLOCED, &m); - if (r) { - goto out; + r = idr_get_new(&_minor_idr, MINOR_ALLOCED, &m, layer); + if (r == -EAGAIN) { + spin_unlock(&_minor_lock); + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + return -ENOMEM; + goto again; } + if (r) + goto out; if (m >= (1 << MINORBITS)) { idr_remove(&_minor_idr, m); diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index a2a35fd..bf61ff4 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -280,14 +280,19 @@ struct mmc_host *mmc_alloc_host_sysfs(in */ int mmc_add_host_sysfs(struct mmc_host *host) { + struct idr_layer *layer = NULL; int err; - if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) - return -ENOMEM; - +again: spin_lock(&mmc_host_lock); - err = idr_get_new(&mmc_host_idr, host, &host->index); + err = idr_get_new(&mmc_host_idr, host, &host->index, layer); spin_unlock(&mmc_host_lock); + if (err == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + return -ENOMEM; + goto again; + } if (err) return err; diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 1cb61a7..3388059 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -42,19 +42,21 @@ struct rtc_device *rtc_device_register(c struct rtc_class_ops *ops, struct module *owner) { + struct idr_layer *layer = NULL; struct rtc_device *rtc; int id, err; - if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { - err = -ENOMEM; - goto exit; - } - - +again: mutex_lock(&idr_lock); - err = idr_get_new(&rtc_idr, NULL, &id); + err = idr_get_new(&rtc_idr, NULL, &id, layer); mutex_unlock(&idr_lock); + if (err == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + return ERR_PTR(-ENOMEM); + goto again; + } if (err < 0) goto exit; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 81755a3..401608b 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1421,6 +1421,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, struct lpfc_hba *phba; struct lpfc_sli *psli; struct lpfc_iocbq *iocbq_entry = NULL, *iocbq_next = NULL; + struct idr_layer *layer = NULL; unsigned long bar0map_len, bar2map_len; int error = -ENODEV, retval; int i; @@ -1443,10 +1444,16 @@ lpfc_pci_probe_one(struct pci_dev *pdev, phba->pcidev = pdev; /* Assign an unused board number */ - if (!idr_pre_get(&lpfc_hba_index, GFP_KERNEL)) - goto out_put_host; - - error = idr_get_new(&lpfc_hba_index, NULL, &phba->brd_no); +again: + error = idr_get_new(&lpfc_hba_index, NULL, &phba->brd_no, layer); + if (error == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) { + error = -ENOMEM; + goto out_put_host; + } + goto again; + } if (error) goto out_put_host; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3225d31..fe504be 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1617,6 +1617,7 @@ static int sd_probe(struct device *dev) struct scsi_device *sdp = to_scsi_device(dev); struct scsi_disk *sdkp; struct gendisk *gd; + struct idr_layer *layer = NULL; u32 index; int error; @@ -1636,13 +1637,19 @@ static int sd_probe(struct device *dev) if (!gd) goto out_free; - if (!idr_pre_get(&sd_index_idr, GFP_KERNEL)) - goto out_put; - +again: spin_lock(&sd_index_lock); - error = idr_get_new(&sd_index_idr, NULL, &index); + error = idr_get_new(&sd_index_idr, NULL, &index, layer); spin_unlock(&sd_index_lock); + if (error == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) { + error = -ENOMEM; + goto out_put; + } + goto again; + } if (index >= SD_MAX_DISKS) error = -EBUSY; if (error) diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 22f7ccd..d03c3ba 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -198,25 +198,26 @@ struct v9fs_session_info *v9fs_inode2v9s int v9fs_get_idpool(struct v9fs_idpool *p) { + struct idr_layer *layer = NULL; int i = 0; int error; retry: - if (idr_pre_get(&p->pool, GFP_KERNEL) == 0) - return 0; - if (down_interruptible(&p->lock) == -EINTR) { eprintk(KERN_WARNING, "Interrupted while locking\n"); return -1; } /* no need to store exactly p, we just need something non-null */ - error = idr_get_new(&p->pool, p, &i); + error = idr_get_new(&p->pool, p, &i, layer); up(&p->lock); - if (error == -EAGAIN) + if (error == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + return -1; goto retry; - else if (error) + } else if (error) return -1; return i; diff --git a/fs/inotify.c b/fs/inotify.c index 723836a..7e12bed 100644 --- a/fs/inotify.c +++ b/fs/inotify.c @@ -131,12 +131,16 @@ EXPORT_SYMBOL_GPL(put_inotify_watch); static int inotify_handle_get_wd(struct inotify_handle *ih, struct inotify_watch *watch) { + struct idr_layer *layer = NULL; int ret; do { - if (unlikely(!idr_pre_get(&ih->idr, GFP_KERNEL))) - return -ENOSPC; - ret = idr_get_new_above(&ih->idr, watch, ih->last_wd+1, &watch->wd); + ret = idr_get_new_above(&ih->idr, watch, ih->last_wd+1, &watch->wd, layer); + if (ret == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + ret = -ENOSPC; + } } while (ret == -EAGAIN); if (likely(!ret)) diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index b650efa..f9d1b04 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -175,19 +175,21 @@ static u8 o2net_num_from_nn(struct o2net static int o2net_prep_nsw(struct o2net_node *nn, struct o2net_status_wait *nsw) { + struct idr_layer *layer = NULL; int ret = 0; do { - if (!idr_pre_get(&nn->nn_status_idr, GFP_ATOMIC)) { - ret = -EAGAIN; - break; - } spin_lock(&nn->nn_lock); - ret = idr_get_new(&nn->nn_status_idr, nsw, &nsw->ns_id); + ret = idr_get_new(&nn->nn_status_idr, nsw, &nsw->ns_id, layer); if (ret == 0) list_add_tail(&nsw->ns_node_item, &nn->nn_status_list); spin_unlock(&nn->nn_lock); + if (ret == -EAGAIN) { + layer = idr_alloc_layer(GFP_ATOMIC); + if (!layer) + ret = -ENOMEM; + } } while (ret == -EAGAIN); if (ret == 0) { diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 4ba0300..60bdd42 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -318,19 +318,20 @@ #define PROC_DYNAMIC_FIRST 0xF0000000UL */ static unsigned int get_inode_number(void) { + struct idr_layer *layer = NULL; int i, inum = 0; int error; retry: - if (idr_pre_get(&proc_inum_idr, GFP_KERNEL) == 0) - return 0; - spin_lock(&proc_inum_lock); - error = idr_get_new(&proc_inum_idr, NULL, &i); + error = idr_get_new(&proc_inum_idr, NULL, &i, layer); spin_unlock(&proc_inum_lock); - if (error == -EAGAIN) + if (error == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (!layer) + return 0; goto retry; - else if (error) + } else if (error) return 0; inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST; diff --git a/fs/super.c b/fs/super.c index 6d4e817..67455f5 100644 --- a/fs/super.c +++ b/fs/super.c @@ -607,19 +607,20 @@ static DEFINE_SPINLOCK(unnamed_dev_lock) int set_anon_super(struct super_block *s, void *data) { + struct idr_layer *layer = NULL; int dev; int error; retry: - if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0) - return -ENOMEM; spin_lock(&unnamed_dev_lock); - error = idr_get_new(&unnamed_dev_idr, NULL, &dev); + error = idr_get_new(&unnamed_dev_idr, NULL, &dev, layer); spin_unlock(&unnamed_dev_lock); - if (error == -EAGAIN) - /* We raced and lost with another CPU. */ + if (error == -EAGAIN) { + layer = idr_alloc_layer(GFP_ATOMIC); + if (!layer) + return -ENOMEM; goto retry; - else if (error) + } else if (error) return -EAGAIN; if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) { diff --git a/include/linux/idr.h b/include/linux/idr.h index 8268034..de34c4e 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -44,7 +44,7 @@ #define MAX_ID_MASK (MAX_ID_BIT - 1) #define MAX_LEVEL (MAX_ID_SHIFT + IDR_BITS - 1) / IDR_BITS /* Number of id_layer structs to leave in free list */ -#define IDR_FREE_MAX MAX_LEVEL + MAX_LEVEL +#define IDR_FREE_MAX (MAX_LEVEL + MAX_LEVEL) struct idr_layer { unsigned long bitmap; /* A zero bit means "space here" */ @@ -57,7 +57,6 @@ struct idr { struct idr_layer *id_free; int layers; int id_free_cnt; - spinlock_t lock; }; #define IDR_INIT(name) \ @@ -66,7 +65,6 @@ #define IDR_INIT(name) \ .id_free = NULL, \ .layers = 0, \ .id_free_cnt = 0, \ - .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ } #define DEFINE_IDR(name) struct idr name = IDR_INIT(name) @@ -75,9 +73,10 @@ #define DEFINE_IDR(name) struct idr name */ void *idr_find(struct idr *idp, int id); -int idr_pre_get(struct idr *idp, gfp_t gfp_mask); -int idr_get_new(struct idr *idp, void *ptr, int *id); -int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id); +struct idr_layer *idr_alloc_layer(gfp_t gfp_mask); +int idr_get_new(struct idr *idp, void *ptr, int *id, struct idr_layer *layer); +int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id, + struct idr_layer *layer); void *idr_replace(struct idr *idp, void *ptr, int id); void idr_remove(struct idr *idp, int id); void idr_destroy(struct idr *idp); diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index ac6dc87..18891b2 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -434,6 +434,7 @@ sys_timer_create(const clockid_t which_c struct sigevent __user *timer_event_spec, timer_t __user * created_timer_id) { + struct idr_layer *layer = NULL; int error = 0; struct k_itimer *new_timer = NULL; int new_timer_id; @@ -451,17 +452,16 @@ sys_timer_create(const clockid_t which_c spin_lock_init(&new_timer->it_lock); 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); + &new_timer_id, layer); spin_unlock_irq(&idr_lock); - if (error == -EAGAIN) - goto retry; - else if (error) { + if (error == -EAGAIN) { + layer = idr_alloc_layer(GFP_KERNEL); + if (layer) + goto retry; + } + if (error) { /* * Wierd looking, but we return EAGAIN if the IDR is * full (proper POSIX return value for this) diff --git a/lib/idr.c b/lib/idr.c index 16d2143..187ce4d 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -35,65 +35,39 @@ #include <linux/idr.h> static kmem_cache_t *idr_layer_cache; -static struct idr_layer *alloc_layer(struct idr *idp) +static struct idr_layer *get_layer(struct idr *idp) { struct idr_layer *p; - unsigned long flags; - spin_lock_irqsave(&idp->lock, flags); - if ((p = idp->id_free)) { + p = idp->id_free; + if (p) { idp->id_free = p->ary[0]; idp->id_free_cnt--; p->ary[0] = NULL; } - spin_unlock_irqrestore(&idp->lock, flags); - return(p); -} -/* only called when idp->lock is held */ -static void __free_layer(struct idr *idp, struct idr_layer *p) -{ - p->ary[0] = idp->id_free; - idp->id_free = p; - idp->id_free_cnt++; + return p; } static void free_layer(struct idr *idp, struct idr_layer *p) { - unsigned long flags; - - /* - * Depends on the return element being zeroed. - */ - spin_lock_irqsave(&idp->lock, flags); - __free_layer(idp, p); - spin_unlock_irqrestore(&idp->lock, flags); + if (idp->id_free_cnt < IDR_FREE_MAX) { + p->ary[0] = idp->id_free; + idp->id_free = p; + idp->id_free_cnt++; + } else + kmem_cache_free(idr_layer_cache, p); } /** - * idr_pre_get - reserver resources for idr allocation - * @idp: idr handle + * idr_alloc_layer - reserve resources for idr allocation * @gfp_mask: memory allocation flags - * - * This function should be called prior to locking and calling the - * following function. It preallocates enough memory to satisfy - * the worst possible allocation. - * - * If the system is REALLY out of memory this function returns 0, - * otherwise 1. */ -int idr_pre_get(struct idr *idp, gfp_t gfp_mask) +struct idr_layer *idr_alloc_layer(gfp_t gfp_mask) { - while (idp->id_free_cnt < IDR_FREE_MAX) { - struct idr_layer *new; - new = kmem_cache_alloc(idr_layer_cache, gfp_mask); - if (new == NULL) - return (0); - free_layer(idp, new); - } - return 1; + return kmem_cache_zalloc(idr_layer_cache, gfp_mask); } -EXPORT_SYMBOL(idr_pre_get); +EXPORT_SYMBOL(idr_alloc_layer); static int sub_alloc(struct idr *idp, void *ptr, int *starting_id) { @@ -136,7 +110,7 @@ static int sub_alloc(struct idr *idp, vo * Create the layer below if it is missing. */ if (!p->ary[m]) { - if (!(new = alloc_layer(idp))) + if (!(new = get_layer(idp))) return -1; p->ary[m] = new; p->count++; @@ -171,14 +145,13 @@ static int idr_get_new_above_int(struct { struct idr_layer *p, *new; int layers, v, id; - unsigned long flags; id = starting_id; build_up: p = idp->top; layers = idp->layers; if (unlikely(!p)) { - if (!(p = alloc_layer(idp))) + if (!(p = get_layer(idp))) return -1; layers = 1; } @@ -190,19 +163,17 @@ build_up: layers++; if (!p->count) continue; - if (!(new = alloc_layer(idp))) { + if (!(new = get_layer(idp))) { /* * The allocation failed. If we built part of * the structure tear it down. */ - spin_lock_irqsave(&idp->lock, flags); for (new = p; p && p != idp->top; new = p) { p = p->ary[0]; new->ary[0] = NULL; new->bitmap = new->count = 0; - __free_layer(idp, new); + free_layer(idp, new); } - spin_unlock_irqrestore(&idp->lock, flags); return -1; } new->ary[0] = p; @@ -216,7 +187,7 @@ build_up: v = sub_alloc(idp, ptr, &id); if (v == -2) goto build_up; - return(v); + return v; } /** @@ -225,20 +196,25 @@ build_up: * @ptr: pointer you want associated with the ide * @start_id: id to start search at * @id: pointer to the allocated handle + * @layer: pointer to extra storage * * This is the allocate id function. It should be called with any * required locks. * * 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 -ENOSPC. + * and call idr_alloc_layer() to get a new layer to pass in as the + * @layer parameter. If the idr is full, it will return -ENOSPC. * * @id returns a value in the range 0 ... 0x7fffffff */ -int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id) +int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id, + struct idr_layer *layer) { int rv; + if (layer) + free_layer(idp, layer); + rv = idr_get_new_above_int(idp, ptr, starting_id); /* * This is a cheap hack until the IDR code can be fixed to @@ -260,33 +236,20 @@ EXPORT_SYMBOL(idr_get_new_above); * @idp: idr handle * @ptr: pointer you want associated with the ide * @id: pointer to the allocated handle + * @layer: pointer to extra storage * * This is the allocate id function. It should be called with any * required locks. * * 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 -ENOSPC. + * and call idr_alloc_layer() to get a new layer to pass in as the + * @layer parameter. If the idr is full, it will return -ENOSPC. * * @id returns a value in the range 0 ... 0x7fffffff */ -int idr_get_new(struct idr *idp, void *ptr, int *id) +int idr_get_new(struct idr *idp, void *ptr, int *id, struct idr_layer *layer) { - int rv; - - rv = idr_get_new_above_int(idp, ptr, 0); - /* - * This is a cheap hack until the IDR code can be fixed to - * return proper error values. - */ - if (rv < 0) { - if (rv == -1) - return -EAGAIN; - else /* Will be -3 */ - return -ENOSPC; - } - *id = rv; - return 0; + return idr_get_new_above(idp, ptr, 0, id, layer); } EXPORT_SYMBOL(idr_get_new); @@ -349,11 +312,6 @@ void idr_remove(struct idr *idp, int id) idp->top = p; --idp->layers; } - while (idp->id_free_cnt >= IDR_FREE_MAX) { - p = alloc_layer(idp); - kmem_cache_free(idr_layer_cache, p); - return; - } } EXPORT_SYMBOL(idr_remove); @@ -364,7 +322,7 @@ EXPORT_SYMBOL(idr_remove); void idr_destroy(struct idr *idp) { while (idp->id_free_cnt) { - struct idr_layer *p = alloc_layer(idp); + struct idr_layer *p = get_layer(idp); kmem_cache_free(idr_layer_cache, p); } } @@ -445,17 +403,11 @@ void *idr_replace(struct idr *idp, void } EXPORT_SYMBOL(idr_replace); -static void idr_cache_ctor(void * idr_layer, kmem_cache_t *idr_layer_cache, - unsigned long flags) -{ - memset(idr_layer, 0, sizeof(struct idr_layer)); -} - static int init_id_cache(void) { if (!idr_layer_cache) idr_layer_cache = kmem_cache_create("idr_layer_cache", - sizeof(struct idr_layer), 0, 0, idr_cache_ctor, NULL); + sizeof(struct idr_layer), 0, 0, NULL, NULL); return 0; } @@ -470,6 +422,5 @@ void idr_init(struct idr *idp) { init_id_cache(); memset(idp, 0, sizeof(struct idr)); - spin_lock_init(&idp->lock); } EXPORT_SYMBOL(idr_init); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 2a87736..fc712c4 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1928,6 +1928,7 @@ int sctp_process_init(struct sctp_associ * association. */ if (!asoc->temp) { + struct idr_layer *layer = NULL; int assoc_id; int error; @@ -1937,15 +1938,16 @@ int sctp_process_init(struct sctp_associ goto clean_up; retry: - if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) - goto clean_up; spin_lock_bh(&sctp_assocs_id_lock); error = idr_get_new_above(&sctp_assocs_id, (void *)asoc, 1, - &assoc_id); + &assoc_id, layer); spin_unlock_bh(&sctp_assocs_id_lock); - if (error == -EAGAIN) - goto retry; - else if (error) + if (error == -EAGAIN) { + layer = idr_alloc_layer(gfp); + if (layer) + goto retry; + } + if (error) goto clean_up; asoc->assoc_id = (sctp_assoc_t) assoc_id; _______________________________________________ openib-general mailing list openib-general@openib.org http://openib.org/mailman/listinfo/openib-general To unsubscribe, please visit http://openib.org/mailman/listinfo/openib-general