Skip to content

Commit

Permalink
ida: Move ida_bitmap to a percpu variable
Browse files Browse the repository at this point in the history
When we preload the IDA, we allocate an IDA bitmap.  Instead of storing
that preallocated bitmap in the IDA, we store it in a percpu variable.
Generally there are more IDAs in the system than CPUs, so this cuts down
on the number of preallocated bitmaps that are unused, and about half
of the IDA users did not call ida_destroy() so they were leaking IDA
bitmaps.

Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
  • Loading branch information
Matthew Wilcox committed Feb 14, 2017
1 parent 0a835c4 commit 7ad3d4d
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 45 deletions.
5 changes: 3 additions & 2 deletions include/linux/idr.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <linux/radix-tree.h>
#include <linux/gfp.h>
#include <linux/percpu.h>

struct idr {
struct radix_tree_root idr_rt;
Expand Down Expand Up @@ -171,9 +172,10 @@ struct ida_bitmap {
unsigned long bitmap[IDA_BITMAP_LONGS];
};

DECLARE_PER_CPU(struct ida_bitmap *, ida_bitmap);

struct ida {
struct radix_tree_root ida_rt;
struct ida_bitmap *free_bitmap;
};

#define IDA_INIT { \
Expand All @@ -193,7 +195,6 @@ void ida_simple_remove(struct ida *ida, unsigned int id);
static inline void ida_init(struct ida *ida)
{
INIT_RADIX_TREE(&ida->ida_rt, IDR_RT_MARKER | GFP_NOWAIT);
ida->free_bitmap = NULL;
}

/**
Expand Down
39 changes: 2 additions & 37 deletions lib/idr.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>

DEFINE_PER_CPU(struct ida_bitmap *, ida_bitmap);
static DEFINE_SPINLOCK(simple_ida_lock);

/**
Expand Down Expand Up @@ -193,38 +194,6 @@ EXPORT_SYMBOL(idr_replace);
* limitation, it should be quite straightforward to raise the maximum.
*/

/**
* ida_pre_get - reserve resources for ida allocation
* @ida: ida handle
* @gfp: memory allocation flags
*
* This function should be called before calling ida_get_new_above(). If it
* is unable to allocate memory, it will return %0. On success, it returns %1.
*/
int ida_pre_get(struct ida *ida, gfp_t gfp)
{
struct ida_bitmap *bitmap;

/*
* This looks weird, but the IDA API has no preload_end() equivalent.
* Instead, ida_get_new() can return -EAGAIN, prompting the caller
* to return to the ida_pre_get() step.
*/
idr_preload(gfp);
idr_preload_end();

if (!ida->free_bitmap) {
bitmap = kmalloc(sizeof(struct ida_bitmap), gfp);
if (!bitmap)
return 0;
bitmap = xchg(&ida->free_bitmap, bitmap);
kfree(bitmap);
}

return 1;
}
EXPORT_SYMBOL(ida_pre_get);

#define IDA_MAX (0x80000000U / IDA_BITMAP_BITS)

/**
Expand Down Expand Up @@ -292,10 +261,9 @@ int ida_get_new_above(struct ida *ida, int start, int *id)
new += bit;
if (new < 0)
return -ENOSPC;
bitmap = ida->free_bitmap;
bitmap = this_cpu_xchg(ida_bitmap, NULL);
if (!bitmap)
return -EAGAIN;
ida->free_bitmap = NULL;
memset(bitmap, 0, sizeof(*bitmap));
__set_bit(bit, bitmap->bitmap);
radix_tree_iter_replace(root, &iter, slot, bitmap);
Expand Down Expand Up @@ -361,9 +329,6 @@ void ida_destroy(struct ida *ida)
kfree(bitmap);
radix_tree_iter_delete(&ida->ida_rt, &iter, slot);
}

kfree(ida->free_bitmap);
ida->free_bitmap = NULL;
}
EXPORT_SYMBOL(ida_destroy);

Expand Down
45 changes: 42 additions & 3 deletions lib/radix-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ static struct kmem_cache *radix_tree_node_cachep;
RADIX_TREE_MAP_SHIFT))
#define IDR_PRELOAD_SIZE (IDR_MAX_PATH * 2 - 1)

/*
* The IDA is even shorter since it uses a bitmap at the last level.
*/
#define IDA_INDEX_BITS (8 * sizeof(int) - 1 - ilog2(IDA_BITMAP_BITS))
#define IDA_MAX_PATH (DIV_ROUND_UP(IDA_INDEX_BITS, \
RADIX_TREE_MAP_SHIFT))
#define IDA_PRELOAD_SIZE (IDA_MAX_PATH * 2 - 1)

/*
* Per-cpu pool of preloaded nodes
*/
Expand Down Expand Up @@ -346,9 +354,8 @@ static void dump_ida_node(void *entry, unsigned long index)
static void ida_dump(struct ida *ida)
{
struct radix_tree_root *root = &ida->ida_rt;
pr_debug("ida: %p %p free %d bitmap %p\n", ida, root->rnode,
root->gfp_mask >> ROOT_TAG_SHIFT,
ida->free_bitmap);
pr_debug("ida: %p node %p free %d\n", ida, root->rnode,
root->gfp_mask >> ROOT_TAG_SHIFT);
dump_ida_node(root->rnode, 0);
}
#endif
Expand Down Expand Up @@ -2080,6 +2087,36 @@ void idr_preload(gfp_t gfp_mask)
}
EXPORT_SYMBOL(idr_preload);

/**
* ida_pre_get - reserve resources for ida allocation
* @ida: ida handle
* @gfp: memory allocation flags
*
* This function should be called before calling ida_get_new_above(). If it
* is unable to allocate memory, it will return %0. On success, it returns %1.
*/
int ida_pre_get(struct ida *ida, gfp_t gfp)
{
__radix_tree_preload(gfp, IDA_PRELOAD_SIZE);
/*
* The IDA API has no preload_end() equivalent. Instead,
* ida_get_new() can return -EAGAIN, prompting the caller
* to return to the ida_pre_get() step.
*/
preempt_enable();

if (!this_cpu_read(ida_bitmap)) {
struct ida_bitmap *bitmap = kmalloc(sizeof(*bitmap), gfp);
if (!bitmap)
return 0;
bitmap = this_cpu_cmpxchg(ida_bitmap, NULL, bitmap);
kfree(bitmap);
}

return 1;
}
EXPORT_SYMBOL(ida_pre_get);

void **idr_get_free(struct radix_tree_root *root,
struct radix_tree_iter *iter, gfp_t gfp, int end)
{
Expand Down Expand Up @@ -2219,6 +2256,8 @@ static int radix_tree_cpu_dead(unsigned int cpu)
kmem_cache_free(radix_tree_node_cachep, node);
rtp->nr--;
}
kfree(per_cpu(ida_bitmap, cpu));
per_cpu(ida_bitmap, cpu) = NULL;
return 0;
}

Expand Down
2 changes: 0 additions & 2 deletions tools/testing/radix-tree/linux/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,4 @@

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

#define xchg(ptr, x) uatomic_xchg(ptr, x)

#endif /* _KERNEL_H */
5 changes: 4 additions & 1 deletion tools/testing/radix-tree/linux/percpu.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@

#define DECLARE_PER_CPU(type, val) extern type val
#define DEFINE_PER_CPU(type, val) type val

#define __get_cpu_var(var) var
#define this_cpu_ptr(var) var
#define this_cpu_read(var) var
#define this_cpu_xchg(var, val) uatomic_xchg(&var, val)
#define this_cpu_cmpxchg(var, old, new) uatomic_cmpxchg(&var, old, new)
#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); (ptr); })
#define per_cpu(var, cpu) (*per_cpu_ptr(&(var), cpu))

0 comments on commit 7ad3d4d

Please sign in to comment.