Commit aba16dc5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'ida-4.19' of git://git.infradead.org/users/willy/linux-dax

Pull IDA updates from Matthew Wilcox:
 "A better IDA API:

      id = ida_alloc(ida, GFP_xxx);
      ida_free(ida, id);

  rather than the cumbersome ida_simple_get(), ida_simple_remove().

  The new IDA API is similar to ida_simple_get() but better named.  The
  internal restructuring of the IDA code removes the bitmap
  preallocation nonsense.

  I hope the net -200 lines of code is convincing"

* 'ida-4.19' of git://git.infradead.org/users/willy/linux-dax: (29 commits)
  ida: Change ida_get_new_above to return the id
  ida: Remove old API
  test_ida: check_ida_destroy and check_ida_alloc
  test_ida: Convert check_ida_conv to new API
  test_ida: Move ida_check_max
  test_ida: Move ida_check_leaf
  idr-test: Convert ida_check_nomem to new API
  ida: Start new test_ida module
  target/iscsi: Allocate session IDs from an IDA
  iscsi target: fix session creation failure handling
  drm/vmwgfx: Convert to new IDA API
  dmaengine: Convert to new IDA API
  ppc: Convert vas ID allocation to new IDA API
  media: Convert entity ID allocation to new IDA API
  ppc: Convert mmu context allocation to new IDA API
  Convert net_namespace to new IDA API
  cb710: Convert to new IDA API
  rsxx: Convert to new IDA API
  osd: Convert to new IDA API
  sd: Convert to new IDA API
  ...
parents c4726e77 1df89519
......@@ -26,48 +26,16 @@
#include <asm/mmu_context.h>
#include <asm/pgalloc.h>
static DEFINE_SPINLOCK(mmu_context_lock);
static DEFINE_IDA(mmu_context_ida);
static int alloc_context_id(int min_id, int max_id)
{
int index, err;
again:
if (!ida_pre_get(&mmu_context_ida, GFP_KERNEL))
return -ENOMEM;
spin_lock(&mmu_context_lock);
err = ida_get_new_above(&mmu_context_ida, min_id, &index);
spin_unlock(&mmu_context_lock);
if (err == -EAGAIN)
goto again;
else if (err)
return err;
if (index > max_id) {
spin_lock(&mmu_context_lock);
ida_remove(&mmu_context_ida, index);
spin_unlock(&mmu_context_lock);
return -ENOMEM;
}
return index;
return ida_alloc_range(&mmu_context_ida, min_id, max_id, GFP_KERNEL);
}
void hash__reserve_context_id(int id)
{
int rc, result = 0;
do {
if (!ida_pre_get(&mmu_context_ida, GFP_KERNEL))
break;
spin_lock(&mmu_context_lock);
rc = ida_get_new_above(&mmu_context_ida, id, &result);
spin_unlock(&mmu_context_lock);
} while (rc == -EAGAIN);
int result = ida_alloc_range(&mmu_context_ida, id, id, GFP_KERNEL);
WARN(result != id, "mmu: Failed to reserve context id %d (rc %d)\n", id, result);
}
......@@ -172,9 +140,7 @@ int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
void __destroy_context(int context_id)
{
spin_lock(&mmu_context_lock);
ida_remove(&mmu_context_ida, context_id);
spin_unlock(&mmu_context_lock);
ida_free(&mmu_context_ida, context_id);
}
EXPORT_SYMBOL_GPL(__destroy_context);
......@@ -182,13 +148,11 @@ static void destroy_contexts(mm_context_t *ctx)
{
int index, context_id;
spin_lock(&mmu_context_lock);
for (index = 0; index < ARRAY_SIZE(ctx->extended_id); index++) {
context_id = ctx->extended_id[index];
if (context_id)
ida_remove(&mmu_context_ida, context_id);
ida_free(&mmu_context_ida, context_id);
}
spin_unlock(&mmu_context_lock);
}
static void pte_frag_destroy(void *pte_frag)
......
......@@ -515,35 +515,17 @@ int init_winctx_regs(struct vas_window *window, struct vas_winctx *winctx)
return 0;
}
static DEFINE_SPINLOCK(vas_ida_lock);
static void vas_release_window_id(struct ida *ida, int winid)
{
spin_lock(&vas_ida_lock);
ida_remove(ida, winid);
spin_unlock(&vas_ida_lock);
ida_free(ida, winid);
}
static int vas_assign_window_id(struct ida *ida)
{
int rc, winid;
do {
rc = ida_pre_get(ida, GFP_KERNEL);
if (!rc)
return -EAGAIN;
spin_lock(&vas_ida_lock);
rc = ida_get_new(ida, &winid);
spin_unlock(&vas_ida_lock);
} while (rc == -EAGAIN);
if (rc)
return rc;
int winid = ida_alloc_max(ida, VAS_WINDOWS_PER_CHIP - 1, GFP_KERNEL);
if (winid > VAS_WINDOWS_PER_CHIP) {
pr_err("Too many (%d) open windows\n", winid);
vas_release_window_id(ida, winid);
if (winid == -ENOSPC) {
pr_err("Too many (%d) open windows\n", VAS_WINDOWS_PER_CHIP);
return -EAGAIN;
}
......
......@@ -118,7 +118,6 @@ static struct dentry *dfs_device_status;
static u32 cpu_use[NR_CPUS];
static DEFINE_SPINLOCK(rssd_index_lock);
static DEFINE_IDA(rssd_index_ida);
static int mtip_block_initialize(struct driver_data *dd);
......@@ -3767,20 +3766,10 @@ static int mtip_block_initialize(struct driver_data *dd)
goto alloc_disk_error;
}
/* Generate the disk name, implemented same as in sd.c */
do {
if (!ida_pre_get(&rssd_index_ida, GFP_KERNEL)) {
rv = -ENOMEM;
goto ida_get_error;
}
spin_lock(&rssd_index_lock);
rv = ida_get_new(&rssd_index_ida, &index);
spin_unlock(&rssd_index_lock);
} while (rv == -EAGAIN);
if (rv)
rv = ida_alloc(&rssd_index_ida, GFP_KERNEL);
if (rv < 0)
goto ida_get_error;
index = rv;
rv = rssd_disk_name_format("rssd",
index,
......@@ -3922,9 +3911,7 @@ static int mtip_block_initialize(struct driver_data *dd)
block_queue_alloc_tag_error:
mtip_hw_debugfs_exit(dd);
disk_index_error:
spin_lock(&rssd_index_lock);
ida_remove(&rssd_index_ida, index);
spin_unlock(&rssd_index_lock);
ida_free(&rssd_index_ida, index);
ida_get_error:
put_disk(dd->disk);
......@@ -4012,9 +3999,7 @@ static int mtip_block_remove(struct driver_data *dd)
}
dd->disk = NULL;
spin_lock(&rssd_index_lock);
ida_remove(&rssd_index_ida, dd->index);
spin_unlock(&rssd_index_lock);
ida_free(&rssd_index_ida, dd->index);
/* De-initialize the protocol layer. */
mtip_hw_exit(dd);
......@@ -4054,9 +4039,7 @@ static int mtip_block_shutdown(struct driver_data *dd)
dd->queue = NULL;
}
spin_lock(&rssd_index_lock);
ida_remove(&rssd_index_ida, dd->index);
spin_unlock(&rssd_index_lock);
ida_free(&rssd_index_ida, dd->index);
return 0;
}
......
......@@ -58,7 +58,6 @@ MODULE_PARM_DESC(sync_start, "On by Default: Driver load will not complete "
"until the card startup has completed.");
static DEFINE_IDA(rsxx_disk_ida);
static DEFINE_SPINLOCK(rsxx_ida_lock);
/* --------------------Debugfs Setup ------------------- */
......@@ -771,19 +770,10 @@ static int rsxx_pci_probe(struct pci_dev *dev,
card->dev = dev;
pci_set_drvdata(dev, card);
do {
if (!ida_pre_get(&rsxx_disk_ida, GFP_KERNEL)) {
st = -ENOMEM;
goto failed_ida_get;
}
spin_lock(&rsxx_ida_lock);
st = ida_get_new(&rsxx_disk_ida, &card->disk_id);
spin_unlock(&rsxx_ida_lock);
} while (st == -EAGAIN);
if (st)
st = ida_alloc(&rsxx_disk_ida, GFP_KERNEL);
if (st < 0)
goto failed_ida_get;
card->disk_id = st;
st = pci_enable_device(dev);
if (st)
......@@ -985,9 +975,7 @@ static int rsxx_pci_probe(struct pci_dev *dev,
failed_dma_mask:
pci_disable_device(dev);
failed_enable:
spin_lock(&rsxx_ida_lock);
ida_remove(&rsxx_disk_ida, card->disk_id);
spin_unlock(&rsxx_ida_lock);
ida_free(&rsxx_disk_ida, card->disk_id);
failed_ida_get:
kfree(card);
......@@ -1050,6 +1038,7 @@ static void rsxx_pci_remove(struct pci_dev *dev)
pci_disable_device(dev);
pci_release_regions(dev);
ida_free(&rsxx_disk_ida, card->disk_id);
kfree(card);
}
......
......@@ -161,9 +161,7 @@ static void chan_dev_release(struct device *dev)
chan_dev = container_of(dev, typeof(*chan_dev), device);
if (atomic_dec_and_test(chan_dev->idr_ref)) {
mutex_lock(&dma_list_mutex);
ida_remove(&dma_ida, chan_dev->dev_id);
mutex_unlock(&dma_list_mutex);
ida_free(&dma_ida, chan_dev->dev_id);
kfree(chan_dev->idr_ref);
}
kfree(chan_dev);
......@@ -898,17 +896,12 @@ static bool device_has_all_tx_types(struct dma_device *device)
static int get_dma_id(struct dma_device *device)
{
int rc;
do {
if (!ida_pre_get(&dma_ida, GFP_KERNEL))
return -ENOMEM;
mutex_lock(&dma_list_mutex);
rc = ida_get_new(&dma_ida, &device->dev_id);
mutex_unlock(&dma_list_mutex);
} while (rc == -EAGAIN);
int rc = ida_alloc(&dma_ida, GFP_KERNEL);
return rc;
if (rc < 0)
return rc;
device->dev_id = rc;
return 0;
}
/**
......@@ -1092,9 +1085,7 @@ int dma_async_device_register(struct dma_device *device)
err_out:
/* if we never registered a channel just release the idr */
if (atomic_read(idr_ref) == 0) {
mutex_lock(&dma_list_mutex);
ida_remove(&dma_ida, device->dev_id);
mutex_unlock(&dma_list_mutex);
ida_free(&dma_ida, device->dev_id);
kfree(idr_ref);
return rc;
}
......
......@@ -51,51 +51,34 @@ static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man,
{
struct vmwgfx_gmrid_man *gman =
(struct vmwgfx_gmrid_man *)man->priv;
int ret = 0;
int id;
mem->mm_node = NULL;
id = ida_alloc_max(&gman->gmr_ida, gman->max_gmr_ids - 1, GFP_KERNEL);
if (id < 0)
return id;
spin_lock(&gman->lock);
if (gman->max_gmr_pages > 0) {
gman->used_gmr_pages += bo->num_pages;
if (unlikely(gman->used_gmr_pages > gman->max_gmr_pages))
goto out_err_locked;
goto nospace;
}
do {
spin_unlock(&gman->lock);
if (unlikely(ida_pre_get(&gman->gmr_ida, GFP_KERNEL) == 0)) {
ret = -ENOMEM;
goto out_err;
}
spin_lock(&gman->lock);
ret = ida_get_new(&gman->gmr_ida, &id);
if (unlikely(ret == 0 && id >= gman->max_gmr_ids)) {
ida_remove(&gman->gmr_ida, id);
ret = 0;
goto out_err_locked;
}
} while (ret == -EAGAIN);
if (likely(ret == 0)) {
mem->mm_node = gman;
mem->start = id;
mem->num_pages = bo->num_pages;
} else
goto out_err_locked;
mem->mm_node = gman;
mem->start = id;
mem->num_pages = bo->num_pages;
spin_unlock(&gman->lock);
return 0;
out_err:
spin_lock(&gman->lock);
out_err_locked:
nospace:
gman->used_gmr_pages -= bo->num_pages;
spin_unlock(&gman->lock);
return ret;
ida_free(&gman->gmr_ida, id);
return 0;
}
static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man,
......@@ -105,8 +88,8 @@ static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man,
(struct vmwgfx_gmrid_man *)man->priv;
if (mem->mm_node) {
ida_free(&gman->gmr_ida, mem->start);
spin_lock(&gman->lock);
ida_remove(&gman->gmr_ida, mem->start);
gman->used_gmr_pages -= mem->num_pages;
spin_unlock(&gman->lock);
mem->mm_node = NULL;
......
......@@ -585,18 +585,12 @@ int __must_check media_device_register_entity(struct media_device *mdev,
entity->num_links = 0;
entity->num_backlinks = 0;
if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL))
return -ENOMEM;
mutex_lock(&mdev->graph_mutex);
ret = ida_get_new_above(&mdev->entity_internal_idx, 1,
&entity->internal_idx);
if (ret < 0) {
mutex_unlock(&mdev->graph_mutex);
ret = ida_alloc_min(&mdev->entity_internal_idx, 1, GFP_KERNEL);
if (ret < 0)
return ret;
}
entity->internal_idx = ret;
mutex_lock(&mdev->graph_mutex);
mdev->entity_internal_idx_max =
max(mdev->entity_internal_idx_max, entity->internal_idx);
......@@ -642,7 +636,7 @@ static void __media_device_unregister_entity(struct media_entity *entity)
struct media_interface *intf;
unsigned int i;
ida_simple_remove(&mdev->entity_internal_idx, entity->internal_idx);
ida_free(&mdev->entity_internal_idx, entity->internal_idx);
/* Remove all interface links pointing to this entity */
list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) {
......
......@@ -16,7 +16,6 @@
#include <linux/gfp.h>
static DEFINE_IDA(cb710_ida);
static DEFINE_SPINLOCK(cb710_ida_lock);
void cb710_pci_update_config_reg(struct pci_dev *pdev,
int reg, uint32_t mask, uint32_t xor)
......@@ -205,7 +204,6 @@ static int cb710_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct cb710_chip *chip;
unsigned long flags;
u32 val;
int err;
int n = 0;
......@@ -256,18 +254,10 @@ static int cb710_probe(struct pci_dev *pdev,
if (err)
return err;
do {
if (!ida_pre_get(&cb710_ida, GFP_KERNEL))
return -ENOMEM;
spin_lock_irqsave(&cb710_ida_lock, flags);
err = ida_get_new(&cb710_ida, &chip->platform_id);
spin_unlock_irqrestore(&cb710_ida_lock, flags);
if (err && err != -EAGAIN)
return err;
} while (err);
err = ida_alloc(&cb710_ida, GFP_KERNEL);
if (err < 0)
return err;
chip->platform_id = err;
dev_info(&pdev->dev, "id %d, IO 0x%p, IRQ %d\n",
chip->platform_id, chip->iobase, pdev->irq);
......@@ -308,7 +298,6 @@ static int cb710_probe(struct pci_dev *pdev,
static void cb710_remove_one(struct pci_dev *pdev)
{
struct cb710_chip *chip = pci_get_drvdata(pdev);
unsigned long flags;
cb710_unregister_slot(chip, CB710_SLOT_SM);
cb710_unregister_slot(chip, CB710_SLOT_MS);
......@@ -317,9 +306,7 @@ static void cb710_remove_one(struct pci_dev *pdev)
BUG_ON(atomic_read(&chip->slot_refs_count) != 0);
#endif
spin_lock_irqsave(&cb710_ida_lock, flags);
ida_remove(&cb710_ida, chip->platform_id);
spin_unlock_irqrestore(&cb710_ida_lock, flags);
ida_free(&cb710_ida, chip->platform_id);
}
static const struct pci_device_id cb710_pci_tbl[] = {
......
......@@ -423,19 +423,11 @@ static int osd_probe(struct device *dev)
if (scsi_device->type != TYPE_OSD)
return -ENODEV;
do {
if (!ida_pre_get(&osd_minor_ida, GFP_KERNEL))
return -ENODEV;
error = ida_get_new(&osd_minor_ida, &minor);
} while (error == -EAGAIN);
if (error)
return error;
if (minor >= SCSI_OSD_MAX_MINOR) {
error = -EBUSY;
goto err_retract_minor;
}
minor = ida_alloc_max(&osd_minor_ida, SCSI_OSD_MAX_MINOR, GFP_KERNEL);
if (minor == -ENOSPC)
return -EBUSY;
if (minor < 0)
return -ENODEV;
error = -ENOMEM;
oud = kzalloc(sizeof(*oud), GFP_KERNEL);
......@@ -499,7 +491,7 @@ static int osd_probe(struct device *dev)
err_free_osd:
put_device(&oud->class_dev);
err_retract_minor:
ida_remove(&osd_minor_ida, minor);
ida_free(&osd_minor_ida, minor);
return error;
}
......@@ -514,7 +506,7 @@ static int osd_remove(struct device *dev)
}
cdev_device_del(&oud->cdev, &oud->class_dev);
ida_remove(&osd_minor_ida, oud->minor);
ida_free(&osd_minor_ida, oud->minor);
put_device(&oud->class_dev);
return 0;
......
......@@ -123,7 +123,6 @@ static void scsi_disk_release(struct device *cdev);
static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *);
static void sd_print_result(const struct scsi_disk *, const char *, int);
static DEFINE_SPINLOCK(sd_index_lock);
static DEFINE_IDA(sd_index_ida);
/* This semaphore is used to mediate the 0->1 reference get in the
......@@ -3340,16 +3339,8 @@ static int sd_probe(struct device *dev)
if (!gd)
goto out_free;
do {
if (!ida_pre_get(&sd_index_ida, GFP_KERNEL))
goto out_put;
spin_lock(&sd_index_lock);
error = ida_get_new(&sd_index_ida, &index);
spin_unlock(&sd_index_lock);
} while (error == -EAGAIN);
if (error) {
index = ida_alloc(&sd_index_ida, GFP_KERNEL);
if (index < 0) {
sdev_printk(KERN_WARNING, sdp, "sd_probe: memory exhausted.\n");
goto out_put;
}
......@@ -3393,9 +3384,7 @@ static int sd_probe(struct device *dev)
return 0;
out_free_index:
spin_lock(&sd_index_lock);
ida_remove(&sd_index_ida, index);
spin_unlock(&sd_index_lock);
ida_free(&sd_index_ida, index);
out_put:
put_disk(gd);
out_free:
......@@ -3460,9 +3449,7 @@ static void scsi_disk_release(struct device *dev)
struct scsi_disk *sdkp = to_scsi_disk(dev);
struct gendisk *disk = sdkp->disk;
spin_lock(&sd_index_lock);
ida_remove(&sd_index_ida, sdkp->index);
spin_unlock(&sd_index_lock);
ida_free(&sd_index_ida, sdkp->index);
disk->private_data = NULL;
put_disk(disk);
......
......@@ -57,9 +57,8 @@ static DEFINE_SPINLOCK(tiqn_lock);
static DEFINE_MUTEX(np_lock);
static struct idr tiqn_idr;
struct idr sess_idr;
DEFINE_IDA(sess_ida);
struct mutex auth_id_lock;
spinlock_t sess_idr_lock;
struct iscsit_global *iscsit_global;
......@@ -700,9 +699,7 @@ static int __init iscsi_target_init_module(void)
spin_lock_init(&iscsit_global->ts_bitmap_lock);
mutex_init(&auth_id_lock);
spin_lock_init(&sess_idr_lock);
idr_init(&tiqn_idr);
idr_init(&sess_idr);
ret = target_register_template(&iscsi_ops);
if (ret)
......@@ -4375,10 +4372,7 @@ int iscsit_close_session(struct iscsi_session *sess)
pr_debug("Decremented number of active iSCSI Sessions on"
" iSCSI TPG: %hu to %u\n", tpg->tpgt, tpg->nsessions);
spin_lock(&sess_idr_lock);
idr_remove(&sess_idr, sess->session_index);
spin_unlock(&sess_idr_lock);
ida_free(&sess_ida, sess->session_index);
kfree(sess->sess_ops);
sess->sess_ops = NULL;
spin_unlock_bh(&se_tpg->session_lock);
......
......@@ -55,9 +55,7 @@ extern struct kmem_cache *lio_ooo_cache;
extern struct kmem_cache *lio_qr_cache;
extern struct kmem_cache *lio_r2t_cache;
extern struct idr sess_idr;
extern struct ida sess_ida;
extern struct mutex auth_id_lock;
extern spinlock_t sess_idr_lock;
#endif /*** ISCSI_TARGET_H ***/
......@@ -336,22 +336,15 @@ static int iscsi_login_zero_tsih_s1(
timer_setup(&sess->time2retain_timer,
iscsit_handle_time2retain_timeout, 0);
idr_preload(GFP_KERNEL);
spin_lock_bh(&sess_idr_lock);
ret = idr_alloc(&sess_idr, NULL, 0, 0, GFP_NOWAIT);
if (ret >= 0)
sess->session_index = ret;
spin_unlock_bh(&sess_idr_lock);
idr_preload_end();
ret = ida_alloc(&sess_ida, GFP_KERNEL);
if (ret < 0) {
pr_err("idr_alloc() for sess_idr failed\n");
pr_err("Session ID allocation failed %d\n", ret);
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES);
kfree(sess);
return -ENOMEM;
goto free_sess;
}
sess->session_index = ret;
sess->creation_time = get_jiffies_64();
/*
* The FFP CmdSN window values will be allocated from the TPG's
......@@ -365,20 +358,26 @@ static int iscsi_login_zero_tsih_s1(
ISCSI_LOGIN_STATUS_NO_RESOURCES);
pr_err("Unable to allocate memory for"
" struct iscsi_sess_ops.\n");
kfree(sess);
return -ENOMEM;
goto free_id;
}
sess->se_sess = transport_alloc_session(TARGET_PROT_NORMAL);
if (IS_ERR(sess->se_sess)) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES);
kfree(sess->sess_ops);
kfree(sess);
return -ENOMEM;
goto free_ops;
}
return 0;
free_ops:
kfree(sess->sess_ops);
free_id:
ida_free(&sess_ida, sess->session_index);
free_sess:
kfree(sess);
conn->sess = NULL;
return -ENOMEM;
}
static int iscsi_login_zero_tsih_s2(
......@@ -1161,13 +1160,9 @@ void iscsi_target_login_sess_out(struct iscsi_conn *conn,
ISCSI_LOGIN_STATUS_INIT_ERR);
if (!zero_tsih || !conn->sess)
goto old_sess_out;
if (conn->sess->se_sess)
transport_free_session(conn->sess->se_sess);
if (conn->sess->session_index != 0) {
spin_lock_bh(&sess_idr_lock);
idr_remove(&sess_idr, conn->sess->session_index);
spin_unlock_bh(&sess_idr_lock);
}
transport_free_session(conn->sess->se_sess);
ida_free(&sess_ida, conn->sess->session_index);
kfree(conn->sess->sess_ops);
kfree(conn->sess);
conn->sess = NULL;
......
......@@ -46,7 +46,7 @@ static int pty_limit = NR_UNIX98_PTY_DEFAULT;
static int pty_reserve = NR_UNIX98_PTY_RESERVE;
static int pty_limit_min;
static int pty_limit_max = INT_MAX;
static int pty_count;
static atomic_t pty_count = ATOMIC_INIT(0);
static struct ctl_table pty_table[] = {
{
......@@ -93,8 +93,6 @@ static struct ctl_table pty_root_table[] = {
{}
};
static DEFINE_MUTEX(allocated_ptys_lock);
struct pts_mount_opts {
int setuid;
int setgid;
......@@ -533,44 +531,25 @@ static struct file_system_type devpts_fs_type = {
int devpts_new_index(struct pts_fs_info *fsi)
{
int index;
int ida_ret;
retry:
if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL))
return -ENOMEM;
mutex_lock(&allocated_ptys_lock);
if (pty_count >= (pty_limit -
(fsi->mount_opts.reserve ? 0 : pty_reserve))) {
mutex_unlock(&allocated_ptys_lock);
return -ENOSPC;
}
int index = -ENOSPC;
ida_ret = ida_get_new(&fsi->allocated_ptys, &index);
if (ida_ret < 0) {
mutex_unlock(&allocated_ptys_lock);
if (ida_ret == -EAGAIN)
goto retry;
return -EIO;
}
if (atomic_inc_return(&pty_count) >= (pty_limit -
(fsi->mount_opts.reserve ? 0 : pty_reserve)))
goto out;
if (index >= fsi->mount_opts.max) {
ida_remove(&fsi->allocated_ptys, index);
mutex_unlock(&allocated_ptys_lock);
return -ENOSPC;
}
pty_count++;
mutex_unlock(&allocated_ptys_lock);
index = ida_alloc_max(&fsi->allocated_ptys, fsi->mount_opts.max - 1,
GFP_KERNEL);
out:
if (index < 0)
atomic_dec(&pty_count);
return index;
}
void devpts_kill_index(struct pts_fs_info *fsi, int idx)
{
mutex_lock(&allocated_ptys_lock);
ida_remove(&fsi->allocated_ptys, idx);
pty_count--;
mutex_unlock(&allocated_ptys_lock);
ida_free(&fsi->allocated_ptys, idx);
atomic_dec(&pty_count);
}
/**
......
......@@ -61,9 +61,6 @@ __setup("mphash_entries=", set_mphash_entries);
static u64 event;
static DEFINE_IDA(mnt_id_ida);
static DEFINE_IDA(mnt_group_ida);
static DEFINE_SPINLOCK(mnt_id_lock);
static int mnt_id_start = 0;
static int mnt_group_start = 1;
static struct hlist_head *mount_hashtable __read_mostly;
static struct hlist_head *mountpoint_hashtable __read_mostly;
......@@ -101,50 +98,30 @@ static inline struct hlist_head *mp_hash(struct dentry *dentry)
static int mnt_alloc_id(struct mount *mnt)
{
int res;
int res = ida_alloc(&mnt_id_ida, GFP_KERNEL);
retry:
ida_pre_get(&mnt_id_ida, GFP_KERNEL);
spin_lock(&mnt_id_lock);
res = ida_get_new_above(&mnt_id_ida, mnt_id_start, &mnt->mnt_id);
if (!res)
mnt_id_start = mnt->mnt_id + 1;
spin_unlock(&mnt_id_lock);
if (res == -EAGAIN)
goto retry;
return res;
if (res < 0)
return res;
mnt->mnt_id = res;
return 0;
}
static void mnt_free_id(struct mount *mnt)
{
int id = mnt->mnt_id;
spin_lock(&mnt_id_lock);
ida_remove(&mnt_id_ida, id);
if (mnt_id_start > id)
mnt_id_start = id;
spin_unlock(&mnt_id_lock);
ida_free(&mnt_id_ida, mnt->mnt_id);
}
/*
* Allocate a new peer group ID
*
* mnt_group_ida is protected by namespace_sem
*/
static int mnt_alloc_group_id(struct mount *mnt)
{
int res;
int res = ida_alloc_min(&mnt_group_ida, 1, GFP_KERNEL);
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
return -ENOMEM;
res = ida_get_new_above(&mnt_group_ida,
mnt_group_start,
&mnt->mnt_group_id);
if (!res)
mnt_group_start = mnt->mnt_group_id + 1;
return res;
if (res < 0)
return res;
mnt->mnt_group_id = res;
return 0;
}
/*
......@@ -152,10 +129,7 @@ static int mnt_alloc_group_id(struct mount *mnt)
*/
void mnt_release_group_id(struct mount *mnt)
{
int id = mnt->mnt_group_id;
ida_remove(&mnt_group_ida, id);
if (mnt_group_start > id)
mnt_group_start = id;
ida_free(&mnt_group_ida, mnt->mnt_group_id);
mnt->mnt_group_id = 0;
}
......
......@@ -981,58 +981,42 @@ void emergency_thaw_all(void)
}
}
/*
* Unnamed block devices are dummy devices used by virtual
* filesystems which don't use real block-devices. -- jrs
*/
static DEFINE_IDA(unnamed_dev_ida);
static DEFINE_SPINLOCK(unnamed_dev_lock);/* protects the above */
/* Many userspace utilities consider an FSID of 0 invalid.
* Always return at least 1 from get_anon_bdev.
*/
static int unnamed_dev_start = 1;
/**
* get_anon_bdev - Allocate a block device for filesystems which don't have one.
* @p: Pointer to a dev_t.
*
* Filesystems which don't use real block devices can call this function
* to allocate a virtual block device.
*
* Context: Any context. Frequently called while holding sb_lock.
* Return: 0 on success, -EMFILE if there are no anonymous bdevs left
* or -ENOMEM if memory allocation failed.
*/
int get_anon_bdev(dev_t *p)
{
int dev;
int error;
retry:
if (ida_pre_get(&unnamed_dev_ida, GFP_ATOMIC) == 0)
return -ENOMEM;
spin_lock(&unnamed_dev_lock);
error = ida_get_new_above(&unnamed_dev_ida, unnamed_dev_start, &dev);
if (!error)
unnamed_dev_start = dev + 1;
spin_unlock(&unnamed_dev_lock);
if (error == -EAGAIN)
/* We raced and lost with another CPU. */
goto retry;
else if (error)
return -EAGAIN;
if (dev >= (1 << MINORBITS)) {
spin_lock(&unnamed_dev_lock);
ida_remove(&unnamed_dev_ida, dev);
if (unnamed_dev_start > dev)
unnamed_dev_start = dev;
spin_unlock(&unnamed_dev_lock);
return -EMFILE;
}
*p = MKDEV(0, dev & MINORMASK);
/*
* Many userspace utilities consider an FSID of 0 invalid.
* Always return at least 1 from get_anon_bdev.
*/
dev = ida_alloc_range(&unnamed_dev_ida, 1, (1 << MINORBITS) - 1,
GFP_ATOMIC);
if (dev == -ENOSPC)
dev = -EMFILE;
if (dev < 0)
return dev;
*p = MKDEV(0, dev);
return 0;
}
EXPORT_SYMBOL(get_anon_bdev);
void free_anon_bdev(dev_t dev)
{
int slot = MINOR(dev);
spin_lock(&unnamed_dev_lock);
ida_remove(&unnamed_dev_ida, slot);
if (slot < unnamed_dev_start)
unnamed_dev_start = slot;
spin_unlock(&unnamed_dev_lock);
ida_free(&unnamed_dev_ida, MINOR(dev));
}
EXPORT_SYMBOL(free_anon_bdev);
......@@ -1040,7 +1024,6 @@ int set_anon_super(struct super_block *s, void *data)
{
return get_anon_bdev(&s->s_dev);
}
EXPORT_SYMBOL(set_anon_super);
void kill_anon_super(struct super_block *sb)
......@@ -1049,7 +1032,6 @@ void kill_anon_super(struct super_block *sb)
generic_shutdown_super(sb);
free_anon_bdev(dev);
}
EXPORT_SYMBOL(kill_anon_super);
void kill_litter_super(struct super_block *sb)
......@@ -1058,7 +1040,6 @@ void kill_litter_super(struct super_block *sb)
d_genocide(sb->s_root);
kill_anon_super(sb);
}
EXPORT_SYMBOL(kill_litter_super);
static int ns_test_super(struct super_block *sb, void *data)
......
......@@ -236,34 +236,74 @@ struct ida {
}
#define DEFINE_IDA(name) struct ida name = IDA_INIT(name)
int ida_pre_get(struct ida *ida, gfp_t gfp_mask);
int ida_get_new_above(struct ida *ida, int starting_id, int *p_id);
void ida_remove(struct ida *ida, int id);
int ida_alloc_range(struct ida *, unsigned int min, unsigned int max, gfp_t);
void ida_free(struct ida *, unsigned int id);
void ida_destroy(struct ida *ida);
int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
gfp_t gfp_mask);
void ida_simple_remove(struct ida *ida, unsigned int id);
/**
* ida_alloc() - Allocate an unused ID.
* @ida: IDA handle.
* @gfp: Memory allocation flags.
*
* Allocate an ID between 0 and %INT_MAX, inclusive.
*
* Context: Any context.
* Return: The allocated ID, or %-ENOMEM if memory could not be allocated,
* or %-ENOSPC if there are no free IDs.
*/
static inline int ida_alloc(struct ida *ida, gfp_t gfp)
{
return ida_alloc_range(ida, 0, ~0, gfp);
}
static inline void ida_init(struct ida *ida)
/**
* ida_alloc_min() - Allocate an unused ID.
* @ida: IDA handle.
* @min: Lowest ID to allocate.
* @gfp: Memory allocation flags.
*
* Allocate an ID between @min and %INT_MAX, inclusive.
*
* Context: Any context.
* Return: The allocated ID, or %-ENOMEM if memory could not be allocated,
* or %-ENOSPC if there are no free IDs.
*/
static inline int ida_alloc_min(struct ida *ida, unsigned int min, gfp_t gfp)
{
INIT_RADIX_TREE(&ida->ida_rt, IDR_RT_MARKER | GFP_NOWAIT);
return ida_alloc_range(ida, min, ~0, gfp);
}
/**
* ida_get_new - allocate new ID
* @ida: idr handle
* @p_id: pointer to the allocated handle
* ida_alloc_max() - Allocate an unused ID.
* @ida: IDA handle.
* @max: Highest ID to allocate.
* @gfp: Memory allocation flags.
*
* Allocate an ID between 0 and @max, inclusive.
*
* Simple wrapper around ida_get_new_above() w/ @starting_id of zero.
* Context: Any context.
* Return: The allocated ID, or %-ENOMEM if memory could not be allocated,
* or %-ENOSPC if there are no free IDs.
*/
static inline int ida_get_new(struct ida *ida, int *p_id)
static inline int ida_alloc_max(struct ida *ida, unsigned int max, gfp_t gfp)
{
return ida_get_new_above(ida, 0, p_id);
return ida_alloc_range(ida, 0, max, gfp);
}
static inline void ida_init(struct ida *ida)
{
INIT_RADIX_TREE(&ida->ida_rt, IDR_RT_MARKER | GFP_NOWAIT);
}
#define ida_simple_get(ida, start, end, gfp) \
ida_alloc_range(ida, start, (end) - 1, gfp)
#define ida_simple_remove(ida, id) ida_free(ida, id)
static inline bool ida_is_empty(const struct ida *ida)
{
return radix_tree_empty(&ida->ida_rt);
}
/* in lib/radix-tree.c */
int ida_pre_get(struct ida *ida, gfp_t gfp_mask);
#endif /* __IDR_H__ */
......@@ -1833,6 +1833,9 @@ config TEST_HASH
This is intended to help people writing architecture-specific
optimized versions. If unsure, say N.
config TEST_IDA
tristate "Perform selftest on IDA functions"
config TEST_PARMAN
tristate "Perform selftest on priority array manager"
depends on PARMAN
......
......@@ -50,6 +50,7 @@ obj-$(CONFIG_TEST_BPF) += test_bpf.o
obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o
obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o
obj-$(CONFIG_TEST_HASH) += test_hash.o test_siphash.o
obj-$(CONFIG_TEST_IDA) += test_ida.o
obj-$(CONFIG_TEST_KASAN) += test_kasan.o
CFLAGS_test_kasan.o += -fno-builtin
obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o
......
......@@ -317,18 +317,12 @@ EXPORT_SYMBOL(idr_replace);
* bit per ID, and so is more space efficient than an IDR. To use an IDA,
* define it using DEFINE_IDA() (or embed a &struct ida in a data structure,
* then initialise it using ida_init()). To allocate a new ID, call
* ida_simple_get(). To free an ID, call ida_simple_remove().
* ida_alloc(), ida_alloc_min(), ida_alloc_max() or ida_alloc_range().
* To free an ID, call ida_free().
*
* If you have more complex locking requirements, use a loop around
* ida_pre_get() and ida_get_new() to allocate a new ID. Then use
* ida_remove() to free an ID. You must make sure that ida_get_new() and
* ida_remove() cannot be called at the same time as each other for the
* same IDA.
*
* You can also use ida_get_new_above() if you need an ID to be allocated
* above a particular number. ida_destroy() can be used to dispose of an
* IDA without needing to free the individual IDs in it. You can use
* ida_is_empty() to find out whether the IDA has any IDs currently allocated.
* ida_destroy() can be used to dispose of an IDA without needing to
* free the individual IDs in it. You can use ida_is_empty() to find
* out whether the IDA has any IDs currently allocated.
*
* IDs are currently limited to the range [0-INT_MAX]. If this is an awkward
* limitation, it should be quite straightforward to raise the maximum.
......@@ -369,25 +363,7 @@ EXPORT_SYMBOL(idr_replace);
#define IDA_MAX (0x80000000U / IDA_BITMAP_BITS - 1)
/**
* ida_get_new_above - allocate new ID above or equal to a start id
* @ida: ida handle
* @start: id to start search at
* @id: pointer to the allocated handle
*
* Allocate new ID above or equal to @start. It should be called
* with any required locks to ensure that concurrent calls to
* ida_get_new_above() / ida_get_new() / ida_remove() are not allowed.
* Consider using ida_simple_get() if you do not have complex locking
* requirements.
*
* If memory is required, it will return %-EAGAIN, you should unlock
* and go back to the ida_pre_get() call. If the ida is full, it will
* return %-ENOSPC. On success, it will return 0.
*
* @id returns a value in the range @start ... %0x7fffffff.
*/
int ida_get_new_above(struct ida *ida, int start, int *id)
static int ida_get_new_above(struct ida *ida, int start)
{
struct radix_tree_root *root = &ida->ida_rt;
void __rcu **slot;
......@@ -426,8 +402,8 @@ int ida_get_new_above(struct ida *ida, int start, int *id)
if (ebit < BITS_PER_LONG) {
tmp |= 1UL << ebit;
rcu_assign_pointer(*slot, (void *)tmp);
*id = new + ebit - RADIX_TREE_EXCEPTIONAL_SHIFT;
return 0;
return new + ebit -
RADIX_TREE_EXCEPTIONAL_SHIFT;
}
bitmap = this_cpu_xchg(ida_bitmap, NULL);
if (!bitmap)
......@@ -458,8 +434,7 @@ int ida_get_new_above(struct ida *ida, int start, int *id)
RADIX_TREE_EXCEPTIONAL_ENTRY);
radix_tree_iter_replace(root, &iter, slot,
bitmap);
*id = new;
return 0;
return new;
}
bitmap = this_cpu_xchg(ida_bitmap, NULL);
if (!bitmap)
......@@ -468,20 +443,11 @@ int ida_get_new_above(struct ida *ida, int start, int *id)
radix_tree_iter_replace(root, &iter, slot, bitmap);
}
*id = new;
return 0;
return new;
}
}
EXPORT_SYMBOL(ida_get_new_above);
/**
* ida_remove - Free the given ID
* @ida: ida handle
* @id: ID to free
*
* This function should not be called at the same time as ida_get_new_above().
*/
void ida_remove(struct ida *ida, int id)
static void ida_remove(struct ida *ida, int id)
{
unsigned long index = id / IDA_BITMAP_BITS;
unsigned offset = id % IDA_BITMAP_BITS;
......@@ -518,99 +484,90 @@ void ida_remove(struct ida *ida, int id)
}
return;
err:
WARN(1, "ida_remove called for id=%d which is not allocated.\n", id);
WARN(1, "ida_free called for id=%d which is not allocated.\n", id);
}
EXPORT_SYMBOL(ida_remove);
/**
* ida_destroy - Free the contents of an ida
* @ida: ida handle
* ida_destroy() - Free all IDs.
* @ida: IDA handle.
*
* Calling this function frees all IDs and releases all resources used
* by an IDA. When this call returns, the IDA is empty and can be reused
* or freed. If the IDA is already empty, there is no need to call this
* function.
*
* Calling this function releases all resources associated with an IDA. When
* this call returns, the IDA is empty and can be reused or freed. The caller
* should not allow ida_remove() or ida_get_new_above() to be called at the
* same time.
* Context: Any context.
*/
void ida_destroy(struct ida *ida)
{
unsigned long flags;
struct radix_tree_iter iter;
void __rcu **slot;
xa_lock_irqsave(&ida->ida_rt, flags);
radix_tree_for_each_slot(slot, &ida->ida_rt, &iter, 0) {
struct ida_bitmap *bitmap = rcu_dereference_raw(*slot);
if (!radix_tree_exception(bitmap))
kfree(bitmap);
radix_tree_iter_delete(&ida->ida_rt, &iter, slot);
}
xa_unlock_irqrestore(&ida->ida_rt, flags);
}
EXPORT_SYMBOL(ida_destroy);
/**
* ida_simple_get - get a new id.
* @ida: the (initialized) ida.
* @start: the minimum id (inclusive, < 0x8000000)
* @end: the maximum id (exclusive, < 0x8000000 or 0)
* @gfp_mask: memory allocation flags
*
* Allocates an id in the range start <= id < end, or returns -ENOSPC.
* On memory allocation failure, returns -ENOMEM.
* ida_alloc_range() - Allocate an unused ID.
* @ida: IDA handle.
* @min: Lowest ID to allocate.
* @max: Highest ID to allocate.
* @gfp: Memory allocation flags.
*
* Compared to ida_get_new_above() this function does its own locking, and
* should be used unless there are special requirements.
* Allocate an ID between @min and @max, inclusive. The allocated ID will
* not exceed %INT_MAX, even if @max is larger.
*
* Use ida_simple_remove() to get rid of an id.
* Context: Any context.
* Return: The allocated ID, or %-ENOMEM if memory could not be allocated,
* or %-ENOSPC if there are no free IDs.
*/
int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
gfp_t gfp_mask)
int ida_alloc_range(struct ida *ida, unsigned int min, unsigned int max,
gfp_t gfp)
{
int ret, id;
unsigned int max;
int id = 0;
unsigned long flags;
BUG_ON((int)start < 0);
BUG_ON((int)end < 0);
if ((int)min < 0)
return -ENOSPC;
if (end == 0)
max = 0x80000000;
else {
BUG_ON(end < start);
max = end - 1;
}
if ((int)max < 0)
max = INT_MAX;
again:
if (!ida_pre_get(ida, gfp_mask))
return -ENOMEM;
xa_lock_irqsave(&ida->ida_rt, flags);
ret = ida_get_new_above(ida, start, &id);
if (!ret) {
if (id > max) {
ida_remove(ida, id);
ret = -ENOSPC;
} else {
ret = id;
}
id = ida_get_new_above(ida, min);
if (id > (int)max) {
ida_remove(ida, id);
id = -ENOSPC;
}
xa_unlock_irqrestore(&ida->ida_rt, flags);
if (unlikely(ret == -EAGAIN))
if (unlikely(id == -EAGAIN)) {
if (!ida_pre_get(ida, gfp))
return -ENOMEM;
goto again;
}
return ret;
return id;
}
EXPORT_SYMBOL(ida_simple_get);
EXPORT_SYMBOL(ida_alloc_range);
/**
* ida_simple_remove - remove an allocated id.
* @ida: the (initialized) ida.
* @id: the id returned by ida_simple_get.
*
* Use to release an id allocated with ida_simple_get().
* ida_free() - Release an allocated ID.
* @ida: IDA handle.
* @id: Previously allocated ID.
*
* Compared to ida_remove() this function does its own locking, and should be
* used unless there are special requirements.
* Context: Any context.
*/
void ida_simple_remove(struct ida *ida, unsigned int id)
void ida_free(struct ida *ida, unsigned int id)
{
unsigned long flags;
......@@ -619,4 +576,4 @@ void ida_simple_remove(struct ida *ida, unsigned int id)
ida_remove(ida, id);
xa_unlock_irqrestore(&ida->ida_rt, flags);
}
EXPORT_SYMBOL(ida_simple_remove);
EXPORT_SYMBOL(ida_free);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment