Radix tree patches for 4.10
Subsystems (13)
v3 cover Radix tree patches for 4.10
From: Matthew Wilcox <[email protected]>
Hi Andrew,
Please include these patches in the -mm tree for 4.10. Mostly these are improvements; the only bug fixes in here relate to multiorder entries (which are unused in the 4.9 tree). The IDR rewrite has the highest potential for causing mayhem as the test suite is quite spartan. We have an Outreachy intern scheduled to work on the test suite for the 2016 winter season, so hopefully it will improve soon.
I did not include Konstantin's suggested change to the API for radix_tree_iter_resume(). Many of the callers do not currently care about the size of the entry they are consuming, and determining that information is not always trivial. Since this is not a performance critical API (it's called when we've paused iterating through a tree in order to schedule for a higher priority task), I think it's more important to have a simpler interface.
I'd like to thank Kiryl for all the testing he's been doing. He's found at least two bugs which weren't related to the API extensions that he really wanted from this patch set.
Noteworthy changes from v2:
- Rebased on latest mmots tree
- Various fixes to accommodate Johannes' exceptional entry accounting changes
- Renamed radix_tree_iter_next() to radix_tree_iter_resume()
- Add kerneldoc to radix_tree_iter_join(), radix_tree_iter_split(), and partially to struct radix_tree_node.
- Factor set_iter_tags() out of radix_tree_next_chunk() and use it in __radix_tree_next_slot().
- Rename __radix_tree_iter_next() to skip_siblings() and only compile it in when MULTIORDER is supported.
Test suite:
- Record item order
- Added an IDR replace test
- Improve iteration test to handle multiorder entries
- More tests to check exceptional entry accounting
- Handle exceptional entries in item_kill_tree()
Konstantin Khlebnikov (1):
radix tree test suite: benchmark for iterator
Matthew Wilcox (32):
radix tree test suite: Fix compilation
tools: Add WARN_ON_ONCE
radix tree test suite: Allow GFP_ATOMIC allocations to fail radix tree test suite: Track preempt_count
radix tree test suite: Free preallocated nodes radix tree test suite: Make runs more reproducible radix tree test suite: iteration test misuses RCU radix tree test suite: Use rcu_barrier
radix tree test suite: Handle exceptional entries radix tree test suite: record order in each item
tools: Add more bitmap functions
radix tree test suite: Use common find-bit code
radix-tree: Fix typo
radix-tree: Move rcu_head into a union with private_list
radix-tree: Create node_tag_set()
radix-tree: Make radix_tree_find_next_bit more useful
radix-tree: Improve dump output
btrfs: Fix race in btrfs_free_dummy_fs_info()
radix-tree: Improve multiorder iterators
radix-tree: Delete radix_tree_locate_item()
radix-tree: Delete radix_tree_range_tag_if_tagged()
radix-tree: Add radix_tree_join
radix-tree: Add radix_tree_split
radix-tree: Add radix_tree_split_preload()
radix-tree: Fix replacement for multiorder entries radix tree test suite: Check multiorder iteration
idr: Add ida_is_empty
tpm: Use idr_find(), not idr_find_slowpath()
rxrpc: Abstract away knowledge of IDR internals
idr: Reduce the number of bits per level from 8 to 6 radix tree test suite: Add some more functionality Reimplement IDR and IDA using the radix tree
drivers/char/tpm/tpm-chip.c | 4 +- drivers/usb/gadget/function/f_hid.c | 6 +- drivers/usb/gadget/function/f_printer.c | 6 +- fs/btrfs/tests/btrfs-tests.c | 1 + include/linux/idr.h | 156 +- include/linux/radix-tree.h | 179 +-- init/main.c | 3 +- lib/idr.c | 1078 -------------- lib/radix-tree.c | 1526 +++++++++++++++----- mm/khugepaged.c | 7 +- mm/page-writeback.c | 28 +- mm/shmem.c | 32 +- net/rxrpc/af_rxrpc.c | 11 +- net/rxrpc/conn_client.c | 4 +- tools/include/asm/bug.h | 11 + tools/include/linux/bitmap.h | 26 + tools/testing/radix-tree/Makefile | 18 +- tools/testing/radix-tree/benchmark.c | 98 ++ tools/testing/radix-tree/find_next_bit.c | 57 - tools/testing/radix-tree/idr.c | 160 ++ tools/testing/radix-tree/iteration_check.c | 123 +- tools/testing/radix-tree/linux.c | 23 +- tools/testing/radix-tree/linux/bitops.h | 40 +- tools/testing/radix-tree/linux/bitops/non-atomic.h | 13 +- tools/testing/radix-tree/linux/bug.h | 2 +- tools/testing/radix-tree/linux/cpu.h | 22 +- tools/testing/radix-tree/linux/gfp.h | 22 +- tools/testing/radix-tree/linux/idr.h | 1 + tools/testing/radix-tree/linux/kernel.h | 20 + tools/testing/radix-tree/linux/notifier.h | 8 - tools/testing/radix-tree/linux/preempt.h | 6 +- tools/testing/radix-tree/linux/slab.h | 6 +- tools/testing/radix-tree/linux/types.h | 2 - tools/testing/radix-tree/main.c | 83 +- tools/testing/radix-tree/multiorder.c | 289 +++- tools/testing/radix-tree/regression2.c | 3 +- tools/testing/radix-tree/regression3.c | 8 +- tools/testing/radix-tree/tag_check.c | 9 +- tools/testing/radix-tree/test.c | 92 +- tools/testing/radix-tree/test.h | 23 +- 40 files changed, 2311 insertions(+), 1895 deletions(-) create mode 100644 tools/testing/radix-tree/benchmark.c delete mode 100644 tools/testing/radix-tree/find_next_bit.c create mode 100644 tools/testing/radix-tree/idr.c create mode 100644 tools/testing/radix-tree/linux/idr.h delete mode 100644 tools/testing/radix-tree/linux/notifier.h
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 1/33 radix tree test suite: Fix compilation
From: Matthew Wilcox <[email protected]>
Patch "lib/radix-tree: Convert to hotplug state machine" breaks the test suite as it adds a call to cpuhp_setup_state_nocalls() which is not currently emulated in the test suite. Add it, and delete the emulation of the old CPU hotplug mechanism.
--- lib/radix-tree.c | 1 - tools/testing/radix-tree/linux/cpu.h | 22 +--------------------- tools/testing/radix-tree/linux/notifier.h | 8 -------- 3 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 tools/testing/radix-tree/linux/notifier.h
lib/radix-tree.c +0 -1
@@ -30,7 +30,6 @@#include <linux/percpu.h> #include <linux/slab.h> #include <linux/kmemleak.h> -#include <linux/notifier.h> #include <linux/cpu.h> #include <linux/string.h> #include <linux/bitops.h>
tools/testing/radix-tree/linux/cpu.h +1 -21
@@ -1,21 +1 @@ - -#define hotcpu_notifier (a, b) - -#define CPU_ONLINE 0x0002/* CPU (unsigned)v is up */ -#define CPU_UP_PREPARE 0x0003/* CPU (unsigned)v coming up */ -#define CPU_UP_CANCELED 0x0004/* CPU (unsigned)v NOT coming up */ -#define CPU_DOWN_PREPARE 0x0005/* CPU (unsigned)v going down */ -#define CPU_DOWN_FAILED 0x0006/* CPU (unsigned)v NOT going down */ -#define CPU_DEAD 0x0007/* CPU (unsigned)v dead */ -#define CPU_POST_DEAD 0x0009/* CPU (unsigned)v dead, cpu_hotplug -* lock is dropped */ -#define CPU_BROKEN 0x000C/* CPU (unsigned)v did not die properly, -* perhaps due to preemption. */ -#define CPU_TASKS_FROZEN 0x0010 - -#define CPU_ONLINE_FROZEN (CPU_ONLINE | CPU_TASKS_FROZEN) -#define CPU_UP_PREPARE_FROZEN (CPU_UP_PREPARE | CPU_TASKS_FROZEN) -#define CPU_UP_CANCELED_FROZEN (CPU_UP_CANCELED | CPU_TASKS_FROZEN) -#define CPU_DOWN_PREPARE_FROZEN (CPU_DOWN_PREPARE | CPU_TASKS_FROZEN) -#define CPU_DOWN_FAILED_FROZEN (CPU_DOWN_FAILED | CPU_TASKS_FROZEN) -#define CPU_DEAD_FROZEN (CPU_DEAD | CPU_TASKS_FROZEN) +#define cpuhp_setup_state_nocalls (a, b, c, d) (0)
tools/testing/radix-tree/linux/notifier.h deleted +0 -8
@@ -1,8 +0,0 @@ -#ifndef _NOTIFIER_H -#define _NOTIFIER_H - -struct notifier_block ; - -#define NOTIFY_OK 0x0001/* Suits me */ - -#endif
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 1/33 radix tree test suite: Fix compilation
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Patch "lib/radix-tree: Convert to hotplug state machine" breaks the test suite as it adds a call to cpuhp_setup_state_nocalls() which is not currently emulated in the test suite. Add it, and delete the emulation of the old CPU hotplug mechanism.
--- lib/radix-tree.c | 1 - tools/testing/radix-tree/linux/cpu.h | 22 +--------------------- tools/testing/radix-tree/linux/notifier.h | 8 -------- 3 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 tools/testing/radix-tree/linux/notifier.h
lib/radix-tree.c +0 -1
@@ -30,7 +30,6 @@#include <linux/percpu.h> #include <linux/slab.h> #include <linux/kmemleak.h> -#include <linux/notifier.h> #include <linux/cpu.h> #include <linux/string.h> #include <linux/bitops.h>
tools/testing/radix-tree/linux/cpu.h +1 -21
@@ -1,21 +1 @@ - -#define hotcpu_notifier (a, b) - -#define CPU_ONLINE 0x0002/* CPU (unsigned)v is up */ -#define CPU_UP_PREPARE 0x0003/* CPU (unsigned)v coming up */ -#define CPU_UP_CANCELED 0x0004/* CPU (unsigned)v NOT coming up */ -#define CPU_DOWN_PREPARE 0x0005/* CPU (unsigned)v going down */ -#define CPU_DOWN_FAILED 0x0006/* CPU (unsigned)v NOT going down */ -#define CPU_DEAD 0x0007/* CPU (unsigned)v dead */ -#define CPU_POST_DEAD 0x0009/* CPU (unsigned)v dead, cpu_hotplug -* lock is dropped */ -#define CPU_BROKEN 0x000C/* CPU (unsigned)v did not die properly, -* perhaps due to preemption. */ -#define CPU_TASKS_FROZEN 0x0010 - -#define CPU_ONLINE_FROZEN (CPU_ONLINE | CPU_TASKS_FROZEN) -#define CPU_UP_PREPARE_FROZEN (CPU_UP_PREPARE | CPU_TASKS_FROZEN) -#define CPU_UP_CANCELED_FROZEN (CPU_UP_CANCELED | CPU_TASKS_FROZEN) -#define CPU_DOWN_PREPARE_FROZEN (CPU_DOWN_PREPARE | CPU_TASKS_FROZEN) -#define CPU_DOWN_FAILED_FROZEN (CPU_DOWN_FAILED | CPU_TASKS_FROZEN) -#define CPU_DEAD_FROZEN (CPU_DEAD | CPU_TASKS_FROZEN) +#define cpuhp_setup_state_nocalls (a, b, c, d) (0)
tools/testing/radix-tree/linux/notifier.h deleted +0 -8
@@ -1,8 +0,0 @@ -#ifndef _NOTIFIER_H -#define _NOTIFIER_H - -struct notifier_block ; - -#define NOTIFY_OK 0x0001/* Suits me */ - -#endif
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 2/33 tools: Add WARN_ON_ONCE
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
The radix tree uses its own buggy WARN_ON_ONCE. Replace it with the definition from asm-generic/bug.h
--- tools/include/asm/bug.h | 11 +++++++++++ tools/testing/radix-tree/Makefile | 2 +- tools/testing/radix-tree/linux/bug.h | 2 +- tools/testing/radix-tree/linux/types.h | 2 -- 4 files changed, 13 insertions(+), 4 deletions(-)
tools/include/asm/bug.h +11 -0
@@ -12,6 +12,17 @@unlikely (__ret_warn_on); \ }) +#define WARN_ON_ONCE (condition) ({ \ + static int __warned; \ + int __ret_warn_once = !!(condition); \ + \ + if (unlikely(__ret_warn_once && !__warned)) { \ + __warned = true; \ + WARN_ON(1); \ + } \ + unlikely(__ret_warn_once); \ +}) + #define WARN_ONCE(condition, format...) ({ \ static int __warned; \ int __ret_warn_once = !!(condition); \
tools/testing/radix-tree/Makefile +1 -1
@@ -1,5 +1,5 @@ -CFLAGS += -I. -g -O2 -Wall -D_LGPL_SOURCE +CFLAGS += -I. -I../../include -g -O2 -Wall -D_LGPL_SOURCE LDFLAGS += -lpthread -lurcu TARGETS = main OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \
tools/testing/radix-tree/linux/bug.h +1 -1
@@ -1 +1 @@ -#define WARN_ON_ONCE (x) assert(x) +#include "asm/bug.h"
tools/testing/radix-tree/linux/types.h +0 -2
@@ -6,8 +6,6 @@#define __rcu#define __read_mostly -#define BITS_PER_LONG (sizeof(long) * 8) -static inline void INIT_LIST_HEAD (struct list_head *list) { list->next = list;
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 2/33 tools: Add WARN_ON_ONCE
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
The radix tree uses its own buggy WARN_ON_ONCE. Replace it with the definition from asm-generic/bug.h
--- tools/include/asm/bug.h | 11 +++++++++++ tools/testing/radix-tree/Makefile | 2 +- tools/testing/radix-tree/linux/bug.h | 2 +- tools/testing/radix-tree/linux/types.h | 2 -- 4 files changed, 13 insertions(+), 4 deletions(-)
tools/include/asm/bug.h +11 -0
@@ -12,6 +12,17 @@unlikely (__ret_warn_on); \ }) +#define WARN_ON_ONCE (condition) ({ \ + static int __warned; \ + int __ret_warn_once = !!(condition); \ + \ + if (unlikely(__ret_warn_once && !__warned)) { \ + __warned = true; \ + WARN_ON(1); \ + } \ + unlikely(__ret_warn_once); \ +}) + #define WARN_ONCE(condition, format...) ({ \ static int __warned; \ int __ret_warn_once = !!(condition); \
tools/testing/radix-tree/Makefile +1 -1
@@ -1,5 +1,5 @@ -CFLAGS += -I. -g -O2 -Wall -D_LGPL_SOURCE +CFLAGS += -I. -I../../include -g -O2 -Wall -D_LGPL_SOURCE LDFLAGS += -lpthread -lurcu TARGETS = main OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \
tools/testing/radix-tree/linux/bug.h +1 -1
@@ -1 +1 @@ -#define WARN_ON_ONCE (x) assert(x) +#include "asm/bug.h"
tools/testing/radix-tree/linux/types.h +0 -2
@@ -6,8 +6,6 @@#define __rcu#define __read_mostly -#define BITS_PER_LONG (sizeof(long) * 8) -static inline void INIT_LIST_HEAD (struct list_head *list) { list->next = list;
Signature · 1 line
2.10.2
PATCH v3 3/33 radix tree test suite: Allow GFP_ATOMIC allocations to fail
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
In order to test the preload code, it is necessary to fail GFP_ATOMIC allocations, which requires defining GFP_KERNEL and GFP_ATOMIC properly. Remove the obsolete __GFP_WAIT and copy the definitions of the __GFP flags which are used from the kernel include files. We also need the real definition of gfpflags_allow_blocking() to persuade the radix tree to actually use its preallocated nodes.
--- tools/testing/radix-tree/linux.c | 7 ++++++- tools/testing/radix-tree/linux/gfp.h | 22 +++++++++++++++++++--- tools/testing/radix-tree/linux/slab.h | 5 ----- 3 files changed, 25 insertions(+), 9 deletions(-)
tools/testing/radix-tree/linux.c +6 -1
@@ -33,7 +33,12 @@ mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,void *kmem_cache_alloc (struct kmem_cache *cachep,int flags) { -void *ret =malloc (cachep->size); +void *ret; + +if (flags & __GFP_NOWARN) +return NULL ; + + ret =malloc (cachep->size);if (cachep->ctor) cachep->ctor (ret);uatomic_inc (&nr_allocated);
tools/testing/radix-tree/linux/gfp.h +19 -3
@@ -3,8 +3,24 @@#define __GFP_BITS_SHIFT 26#define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) -#define __GFP_WAIT 1 -#define __GFP_ACCOUNT 0 -#define __GFP_NOWARN 0 + +#define __GFP_HIGH 0x20u +#define __GFP_IO 0x40u +#define __GFP_FS 0x80u +#define __GFP_NOWARN 0x200u +#define __GFP_ATOMIC 0x80000u +#define __GFP_ACCOUNT 0x100000u +#define __GFP_DIRECT_RECLAIM 0x400000u +#define __GFP_KSWAPD_RECLAIM 0x2000000u + +#define __GFP_RECLAIM (__GFP_DIRECT_RECLAIM|__GFP_KSWAPD_RECLAIM) + +#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM) +#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) + +static inline bool gfpflags_allow_blocking (const gfp_t gfp_flags) +{ +return !!(gfp_flags & __GFP_DIRECT_RECLAIM); +}#endif
tools/testing/radix-tree/linux/slab.h +0 -5
@@ -7,11 +7,6 @@#define SLAB_PANIC 2#define SLAB_RECLAIM_ACCOUNT 0x00020000UL/* Objects are reclaimable */ -static inline int gfpflags_allow_blocking (gfp_t mask) -{ -return 1 ; -} -struct kmem_cache {int size;void (*ctor)(void *);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 3/33 radix tree test suite: Allow GFP_ATOMIC allocations to fail
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
In order to test the preload code, it is necessary to fail GFP_ATOMIC allocations, which requires defining GFP_KERNEL and GFP_ATOMIC properly. Remove the obsolete __GFP_WAIT and copy the definitions of the __GFP flags which are used from the kernel include files. We also need the real definition of gfpflags_allow_blocking() to persuade the radix tree to actually use its preallocated nodes.
--- tools/testing/radix-tree/linux.c | 7 ++++++- tools/testing/radix-tree/linux/gfp.h | 22 +++++++++++++++++++--- tools/testing/radix-tree/linux/slab.h | 5 ----- 3 files changed, 25 insertions(+), 9 deletions(-)
tools/testing/radix-tree/linux.c +6 -1
@@ -33,7 +33,12 @@ mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,void *kmem_cache_alloc (struct kmem_cache *cachep,int flags) { -void *ret =malloc (cachep->size); +void *ret; + +if (flags & __GFP_NOWARN) +return NULL ; + + ret =malloc (cachep->size);if (cachep->ctor) cachep->ctor (ret);uatomic_inc (&nr_allocated);
tools/testing/radix-tree/linux/gfp.h +19 -3
@@ -3,8 +3,24 @@#define __GFP_BITS_SHIFT 26#define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) -#define __GFP_WAIT 1 -#define __GFP_ACCOUNT 0 -#define __GFP_NOWARN 0 + +#define __GFP_HIGH 0x20u +#define __GFP_IO 0x40u +#define __GFP_FS 0x80u +#define __GFP_NOWARN 0x200u +#define __GFP_ATOMIC 0x80000u +#define __GFP_ACCOUNT 0x100000u +#define __GFP_DIRECT_RECLAIM 0x400000u +#define __GFP_KSWAPD_RECLAIM 0x2000000u + +#define __GFP_RECLAIM (__GFP_DIRECT_RECLAIM|__GFP_KSWAPD_RECLAIM) + +#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM) +#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) + +static inline bool gfpflags_allow_blocking (const gfp_t gfp_flags) +{ +return !!(gfp_flags & __GFP_DIRECT_RECLAIM); +}#endif
tools/testing/radix-tree/linux/slab.h +0 -5
@@ -7,11 +7,6 @@#define SLAB_PANIC 2#define SLAB_RECLAIM_ACCOUNT 0x00020000UL/* Objects are reclaimable */ -static inline int gfpflags_allow_blocking (gfp_t mask) -{ -return 1 ; -} -struct kmem_cache {int size;void (*ctor)(void *);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 4/33 radix tree test suite: Track preempt_count
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Rather than simply NOP out preempt_enable() and preempt_disable(), keep track of preempt_count and display it regularly in case either the test suite or the code under test is forgetting to balance the enables & disables. Only found a test-case that was forgetting to re-enable preemption, but it's a possibility worth checking.
--- tools/testing/radix-tree/linux.c | 1 + tools/testing/radix-tree/linux/preempt.h | 6 +++--- tools/testing/radix-tree/main.c | 30 ++++++++++++++++++++---------- 3 files changed, 24 insertions(+), 13 deletions(-)
tools/testing/radix-tree/linux.c +1 -0
@@ -9,6 +9,7 @@#include <urcu/uatomic.h> int nr_allocated; +int preempt_count;void *mempool_alloc (mempool_t *pool,int gfp_mask) {
tools/testing/radix-tree/linux/preempt.h +3 -3
@@ -1,4 +1,4 @@ -/* */ +extern int preempt_count; -#define preempt_disable () do { } while (0) -#define preempt_enable () do { } while (0) +#define preempt_disable () uatomic_inc(&preempt_count) +#define preempt_enable () uatomic_dec(&preempt_count)
tools/testing/radix-tree/main.c +20 -10
@@ -293,27 +293,36 @@ static void single_thread_tests(bool long_run) {int i; -printf ("starting single_thread_tests: %d allocated\n" , nr_allocated); +printf ("starting single_thread_tests: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);multiorder_checks (); -printf ("after multiorder_check: %d allocated\n" , nr_allocated); +printf ("after multiorder_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);locate_check (); -printf ("after locate_check: %d allocated\n" , nr_allocated); +printf ("after locate_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);tag_check (); -printf ("after tag_check: %d allocated\n" , nr_allocated); +printf ("after tag_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);gang_check (); -printf ("after gang_check: %d allocated\n" , nr_allocated); +printf ("after gang_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);add_and_check (); -printf ("after add_and_check: %d allocated\n" , nr_allocated); +printf ("after add_and_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);dynamic_height_check (); -printf ("after dynamic_height_check: %d allocated\n" , nr_allocated); +printf ("after dynamic_height_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);big_gang_check (long_run); -printf ("after big_gang_check: %d allocated\n" , nr_allocated); +printf ("after big_gang_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);for (i =0 ; i < (long_run ?2000 :3 ); i++) {copy_tag_check ();printf ("%d " , i);fflush (stdout); } -printf ("after copy_tag_check: %d allocated\n" , nr_allocated); +printf ("after copy_tag_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count); }int main (int argc,char **argv)
@@ -336,7 +345,8 @@ int main(int argc, char **argv)single_thread_tests (long_run);sleep (1 ); -printf ("after sleep(1): %d allocated\n" , nr_allocated); +printf ("after sleep(1): %d allocated, preempt %d\n" , + nr_allocated, preempt_count);rcu_unregister_thread ();exit (0 );
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 4/33 radix tree test suite: Track preempt_count
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Rather than simply NOP out preempt_enable() and preempt_disable(), keep track of preempt_count and display it regularly in case either the test suite or the code under test is forgetting to balance the enables & disables. Only found a test-case that was forgetting to re-enable preemption, but it's a possibility worth checking.
--- tools/testing/radix-tree/linux.c | 1 + tools/testing/radix-tree/linux/preempt.h | 6 +++--- tools/testing/radix-tree/main.c | 30 ++++++++++++++++++++---------- 3 files changed, 24 insertions(+), 13 deletions(-)
tools/testing/radix-tree/linux.c +1 -0
@@ -9,6 +9,7 @@#include <urcu/uatomic.h> int nr_allocated; +int preempt_count;void *mempool_alloc (mempool_t *pool,int gfp_mask) {
tools/testing/radix-tree/linux/preempt.h +3 -3
@@ -1,4 +1,4 @@ -/* */ +extern int preempt_count; -#define preempt_disable () do { } while (0) -#define preempt_enable () do { } while (0) +#define preempt_disable () uatomic_inc(&preempt_count) +#define preempt_enable () uatomic_dec(&preempt_count)
tools/testing/radix-tree/main.c +20 -10
@@ -293,27 +293,36 @@ static void single_thread_tests(bool long_run) {int i; -printf ("starting single_thread_tests: %d allocated\n" , nr_allocated); +printf ("starting single_thread_tests: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);multiorder_checks (); -printf ("after multiorder_check: %d allocated\n" , nr_allocated); +printf ("after multiorder_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);locate_check (); -printf ("after locate_check: %d allocated\n" , nr_allocated); +printf ("after locate_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);tag_check (); -printf ("after tag_check: %d allocated\n" , nr_allocated); +printf ("after tag_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);gang_check (); -printf ("after gang_check: %d allocated\n" , nr_allocated); +printf ("after gang_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);add_and_check (); -printf ("after add_and_check: %d allocated\n" , nr_allocated); +printf ("after add_and_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);dynamic_height_check (); -printf ("after dynamic_height_check: %d allocated\n" , nr_allocated); +printf ("after dynamic_height_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);big_gang_check (long_run); -printf ("after big_gang_check: %d allocated\n" , nr_allocated); +printf ("after big_gang_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);for (i =0 ; i < (long_run ?2000 :3 ); i++) {copy_tag_check ();printf ("%d " , i);fflush (stdout); } -printf ("after copy_tag_check: %d allocated\n" , nr_allocated); +printf ("after copy_tag_check: %d allocated, preempt %d\n" , + nr_allocated, preempt_count); }int main (int argc,char **argv)
@@ -336,7 +345,8 @@ int main(int argc, char **argv)single_thread_tests (long_run);sleep (1 ); -printf ("after sleep(1): %d allocated\n" , nr_allocated); +printf ("after sleep(1): %d allocated, preempt %d\n" , + nr_allocated, preempt_count);rcu_unregister_thread ();exit (0 );
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 5/33 radix tree test suite: Free preallocated nodes
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
It can be a source of mild concern when the test suite shows that we're leaking nodes. While poring over the source code looking for leaks can lead to some fascinating bugs being discovered, sometimes the leak is simply that these nodes were preallocated and are sitting on the per-CPU list. Free them by calling the CPU dead callback.
--- tools/testing/radix-tree/main.c | 3 +++ tools/testing/radix-tree/test.h | 1 + 2 files changed, 4 insertions(+)
tools/testing/radix-tree/main.c +3 -0
@@ -344,6 +344,9 @@ int main(int argc, char **argv)iteration_test ();single_thread_tests (long_run); +/* Free any remaining preallocated nodes */ +radix_tree_cpu_dead (0 ); +sleep (1 );printf ("after sleep(1): %d allocated, preempt %d\n" , nr_allocated, preempt_count);
tools/testing/radix-tree/test.h +1 -0
@@ -44,3 +44,4 @@ void radix_tree_dump(struct radix_tree_root *root);int root_tag_get (struct radix_tree_root *root,unsigned int tag);unsigned long node_maxindex (struct radix_tree_node *);unsigned long shift_maxindex (unsigned int shift); +int radix_tree_cpu_dead (unsigned int cpu);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 5/33 radix tree test suite: Free preallocated nodes
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
It can be a source of mild concern when the test suite shows that we're leaking nodes. While poring over the source code looking for leaks can lead to some fascinating bugs being discovered, sometimes the leak is simply that these nodes were preallocated and are sitting on the per-CPU list. Free them by calling the CPU dead callback.
--- tools/testing/radix-tree/main.c | 3 +++ tools/testing/radix-tree/test.h | 1 + 2 files changed, 4 insertions(+)
tools/testing/radix-tree/main.c +3 -0
@@ -344,6 +344,9 @@ int main(int argc, char **argv)iteration_test ();single_thread_tests (long_run); +/* Free any remaining preallocated nodes */ +radix_tree_cpu_dead (0 ); +sleep (1 );printf ("after sleep(1): %d allocated, preempt %d\n" , nr_allocated, preempt_count);
tools/testing/radix-tree/test.h +1 -0
@@ -44,3 +44,4 @@ void radix_tree_dump(struct radix_tree_root *root);int root_tag_get (struct radix_tree_root *root,unsigned int tag);unsigned long node_maxindex (struct radix_tree_node *);unsigned long shift_maxindex (unsigned int shift); +int radix_tree_cpu_dead (unsigned int cpu);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 6/33 radix tree test suite: Make runs more reproducible
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Instead of reseeding the random number generator every time around the loop in big_gang_check(), seed it at the beginning of execution. Use rand_r() and an independent base seed for each thread in iteration_test() so they don't stomp all over each others state. Since this particular test depends on the kernel scheduler, the iteration test can't be reproduced based purely on the random seed, but at least it won't pollute the other tests.
Print the seed, and allow the seed to be specified so that a run which hits a problem can be reproduced.
--- tools/testing/radix-tree/iteration_check.c | 11 +++++++---- tools/testing/radix-tree/main.c | 9 +++++++-- 2 files changed, 14 insertions(+), 6 deletions(-)
tools/testing/radix-tree/iteration_check.c +7 -4
@@ -20,6 +20,7 @@#define TAG 0static pthread_mutex_t tree_lock =PTHREAD_MUTEX_INITIALIZER ;static pthread_t threads[NUM_THREADS ]; +static unsigned int seeds[3 ];RADIX_TREE (tree,GFP_KERNEL );bool test_complete;
@@ -71,7 +72,7 @@ static void *tagged_iteration_fn(void *arg)continue ; } -if (rand () %50 ==0 ) +if (rand_r (&seeds[0 ]) %50 ==0 ) slot =radix_tree_iter_next (&iter); }rcu_read_unlock ();
@@ -111,7 +112,7 @@ static void *untagged_iteration_fn(void *arg)continue ; } -if (rand () %50 ==0 ) +if (rand_r (&seeds[1 ]) %50 ==0 ) slot =radix_tree_iter_next (&iter); }rcu_read_unlock ();
@@ -129,7 +130,7 @@ static void *remove_entries_fn(void *arg)while (!test_complete) {int pgoff; - pgoff =rand () %100 ; + pgoff =rand_r (&seeds[2 ]) %100 ;pthread_mutex_lock (&tree_lock);item_delete (&tree, pgoff);
@@ -146,9 +147,11 @@ void iteration_test(void)printf ("Running iteration tests for 10 seconds\n" ); -srand (time (0 )); test_complete = false; +for (i =0 ; i <3 ; i++) + seeds[i] =rand (); +if (pthread_create (&threads[0 ],NULL , tagged_iteration_fn,NULL )) {perror ("pthread_create" );exit (1 );
tools/testing/radix-tree/main.c +7 -2
@@ -67,7 +67,6 @@ void big_gang_check(bool long_run)for (i =0 ; i < (long_run ?1000 :3 ); i++) {__big_gang_check (); -srand (time (0 ));printf ("%d " , i);fflush (stdout); }
@@ -329,12 +328,18 @@ int main(int argc, char **argv) {bool long_run = false;int opt; +unsigned int seed =time (NULL ); -while ((opt =getopt (argc, argv,"l" )) !=-1 ) { +while ((opt =getopt (argc, argv,"ls:" )) !=-1 ) {if (opt =='l' ) long_run = true; +else if (opt =='s' ) + seed =strtoul (optarg,NULL ,0 ); } +printf ("random seed %u\n" , seed); +srand (seed); +rcu_register_thread ();radix_tree_init ();
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 6/33 radix tree test suite: Make runs more reproducible
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Instead of reseeding the random number generator every time around the loop in big_gang_check(), seed it at the beginning of execution. Use rand_r() and an independent base seed for each thread in iteration_test() so they don't stomp all over each others state. Since this particular test depends on the kernel scheduler, the iteration test can't be reproduced based purely on the random seed, but at least it won't pollute the other tests.
Print the seed, and allow the seed to be specified so that a run which hits a problem can be reproduced.
--- tools/testing/radix-tree/iteration_check.c | 11 +++++++---- tools/testing/radix-tree/main.c | 9 +++++++-- 2 files changed, 14 insertions(+), 6 deletions(-)
tools/testing/radix-tree/iteration_check.c +7 -4
@@ -20,6 +20,7 @@#define TAG 0static pthread_mutex_t tree_lock =PTHREAD_MUTEX_INITIALIZER ;static pthread_t threads[NUM_THREADS ]; +static unsigned int seeds[3 ];RADIX_TREE (tree,GFP_KERNEL );bool test_complete;
@@ -71,7 +72,7 @@ static void *tagged_iteration_fn(void *arg)continue ; } -if (rand () %50 ==0 ) +if (rand_r (&seeds[0 ]) %50 ==0 ) slot =radix_tree_iter_next (&iter); }rcu_read_unlock ();
@@ -111,7 +112,7 @@ static void *untagged_iteration_fn(void *arg)continue ; } -if (rand () %50 ==0 ) +if (rand_r (&seeds[1 ]) %50 ==0 ) slot =radix_tree_iter_next (&iter); }rcu_read_unlock ();
@@ -129,7 +130,7 @@ static void *remove_entries_fn(void *arg)while (!test_complete) {int pgoff; - pgoff =rand () %100 ; + pgoff =rand_r (&seeds[2 ]) %100 ;pthread_mutex_lock (&tree_lock);item_delete (&tree, pgoff);
@@ -146,9 +147,11 @@ void iteration_test(void)printf ("Running iteration tests for 10 seconds\n" ); -srand (time (0 )); test_complete = false; +for (i =0 ; i <3 ; i++) + seeds[i] =rand (); +if (pthread_create (&threads[0 ],NULL , tagged_iteration_fn,NULL )) {perror ("pthread_create" );exit (1 );
tools/testing/radix-tree/main.c +7 -2
@@ -67,7 +67,6 @@ void big_gang_check(bool long_run)for (i =0 ; i < (long_run ?1000 :3 ); i++) {__big_gang_check (); -srand (time (0 ));printf ("%d " , i);fflush (stdout); }
@@ -329,12 +328,18 @@ int main(int argc, char **argv) {bool long_run = false;int opt; +unsigned int seed =time (NULL ); -while ((opt =getopt (argc, argv,"l" )) !=-1 ) { +while ((opt =getopt (argc, argv,"ls:" )) !=-1 ) {if (opt =='l' ) long_run = true; +else if (opt =='s' ) + seed =strtoul (optarg,NULL ,0 ); } +printf ("random seed %u\n" , seed); +srand (seed); +rcu_register_thread ();radix_tree_init ();
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
v3 cover Radix tree patches for 4.10
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Hi Andrew,
Please include these patches in the -mm tree for 4.10. Mostly these are improvements; the only bug fixes in here relate to multiorder entries (which are unused in the 4.9 tree). The IDR rewrite has the highest potential for causing mayhem as the test suite is quite spartan. We have an Outreachy intern scheduled to work on the test suite for the 2016 winter season, so hopefully it will improve soon.
I did not include Konstantin's suggested change to the API for radix_tree_iter_resume(). Many of the callers do not currently care about the size of the entry they are consuming, and determining that information is not always trivial. Since this is not a performance critical API (it's called when we've paused iterating through a tree in order to schedule for a higher priority task), I think it's more important to have a simpler interface.
I'd like to thank Kiryl for all the testing he's been doing. He's found at least two bugs which weren't related to the API extensions that he really wanted from this patch set.
Noteworthy changes from v2:
- Rebased on latest mmots tree
- Various fixes to accommodate Johannes' exceptional entry accounting changes
- Renamed radix_tree_iter_next() to radix_tree_iter_resume()
- Add kerneldoc to radix_tree_iter_join(), radix_tree_iter_split(), and partially to struct radix_tree_node.
- Factor set_iter_tags() out of radix_tree_next_chunk() and use it in __radix_tree_next_slot().
- Rename __radix_tree_iter_next() to skip_siblings() and only compile it in when MULTIORDER is supported.
Test suite:
- Record item order
- Added an IDR replace test
- Improve iteration test to handle multiorder entries
- More tests to check exceptional entry accounting
- Handle exceptional entries in item_kill_tree()
Konstantin Khlebnikov (1):
radix tree test suite: benchmark for iterator
Matthew Wilcox (32):
radix tree test suite: Fix compilation
tools: Add WARN_ON_ONCE
radix tree test suite: Allow GFP_ATOMIC allocations to fail radix tree test suite: Track preempt_count
radix tree test suite: Free preallocated nodes radix tree test suite: Make runs more reproducible radix tree test suite: iteration test misuses RCU radix tree test suite: Use rcu_barrier
radix tree test suite: Handle exceptional entries radix tree test suite: record order in each item
tools: Add more bitmap functions
radix tree test suite: Use common find-bit code
radix-tree: Fix typo
radix-tree: Move rcu_head into a union with private_list
radix-tree: Create node_tag_set()
radix-tree: Make radix_tree_find_next_bit more useful
radix-tree: Improve dump output
btrfs: Fix race in btrfs_free_dummy_fs_info()
radix-tree: Improve multiorder iterators
radix-tree: Delete radix_tree_locate_item()
radix-tree: Delete radix_tree_range_tag_if_tagged()
radix-tree: Add radix_tree_join
radix-tree: Add radix_tree_split
radix-tree: Add radix_tree_split_preload()
radix-tree: Fix replacement for multiorder entries radix tree test suite: Check multiorder iteration
idr: Add ida_is_empty
tpm: Use idr_find(), not idr_find_slowpath()
rxrpc: Abstract away knowledge of IDR internals
idr: Reduce the number of bits per level from 8 to 6 radix tree test suite: Add some more functionality Reimplement IDR and IDA using the radix tree
drivers/char/tpm/tpm-chip.c | 4 +- drivers/usb/gadget/function/f_hid.c | 6 +- drivers/usb/gadget/function/f_printer.c | 6 +- fs/btrfs/tests/btrfs-tests.c | 1 + include/linux/idr.h | 156 +- include/linux/radix-tree.h | 179 +-- init/main.c | 3 +- lib/idr.c | 1078 -------------- lib/radix-tree.c | 1526 +++++++++++++++----- mm/khugepaged.c | 7 +- mm/page-writeback.c | 28 +- mm/shmem.c | 32 +- net/rxrpc/af_rxrpc.c | 11 +- net/rxrpc/conn_client.c | 4 +- tools/include/asm/bug.h | 11 + tools/include/linux/bitmap.h | 26 + tools/testing/radix-tree/Makefile | 18 +- tools/testing/radix-tree/benchmark.c | 98 ++ tools/testing/radix-tree/find_next_bit.c | 57 - tools/testing/radix-tree/idr.c | 160 ++ tools/testing/radix-tree/iteration_check.c | 123 +- tools/testing/radix-tree/linux.c | 23 +- tools/testing/radix-tree/linux/bitops.h | 40 +- tools/testing/radix-tree/linux/bitops/non-atomic.h | 13 +- tools/testing/radix-tree/linux/bug.h | 2 +- tools/testing/radix-tree/linux/cpu.h | 22 +- tools/testing/radix-tree/linux/gfp.h | 22 +- tools/testing/radix-tree/linux/idr.h | 1 + tools/testing/radix-tree/linux/kernel.h | 20 + tools/testing/radix-tree/linux/notifier.h | 8 - tools/testing/radix-tree/linux/preempt.h | 6 +- tools/testing/radix-tree/linux/slab.h | 6 +- tools/testing/radix-tree/linux/types.h | 2 - tools/testing/radix-tree/main.c | 83 +- tools/testing/radix-tree/multiorder.c | 289 +++- tools/testing/radix-tree/regression2.c | 3 +- tools/testing/radix-tree/regression3.c | 8 +- tools/testing/radix-tree/tag_check.c | 9 +- tools/testing/radix-tree/test.c | 92 +- tools/testing/radix-tree/test.h | 23 +- 40 files changed, 2311 insertions(+), 1895 deletions(-) create mode 100644 tools/testing/radix-tree/benchmark.c delete mode 100644 tools/testing/radix-tree/find_next_bit.c create mode 100644 tools/testing/radix-tree/idr.c create mode 100644 tools/testing/radix-tree/linux/idr.h delete mode 100644 tools/testing/radix-tree/linux/notifier.h
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 7/33 radix tree test suite: iteration test misuses RCU
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Each thread needs to register itself with RCU, otherwise the reading thread's read lock has no effect and the freeing thread will free the memory in the tree without waiting for the read lock to be dropped.
--- tools/testing/radix-tree/iteration_check.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-)
tools/testing/radix-tree/iteration_check.c +26 -2
@@ -29,6 +29,8 @@ static void *add_entries_fn(void *arg) {int pgoff; +rcu_register_thread (); +while (!test_complete) {for (pgoff =0 ; pgoff <100 ; pgoff++) {pthread_mutex_lock (&tree_lock);
@@ -38,6 +40,8 @@ static void *add_entries_fn(void *arg) } } +rcu_unregister_thread (); +return NULL ; }
@@ -53,6 +57,8 @@ static void *tagged_iteration_fn(void *arg)struct radix_tree_iter iter;void **slot; +rcu_register_thread (); +while (!test_complete) {rcu_read_lock ();radix_tree_for_each_tagged (slot, &tree, &iter,0 ,TAG ) {
@@ -72,12 +78,18 @@ static void *tagged_iteration_fn(void *arg)continue ; } -if (rand_r (&seeds[0 ]) %50 ==0 ) +if (rand_r (&seeds[0 ]) %50 ==0 ) { slot =radix_tree_iter_next (&iter); +rcu_read_unlock (); +rcu_barrier (); +rcu_read_lock (); + } }rcu_read_unlock (); } +rcu_unregister_thread (); +return NULL ; }
@@ -93,6 +105,8 @@ static void *untagged_iteration_fn(void *arg)struct radix_tree_iter iter;void **slot; +rcu_register_thread (); +while (!test_complete) {rcu_read_lock ();radix_tree_for_each_slot (slot, &tree, &iter,0 ) {
@@ -112,12 +126,18 @@ static void *untagged_iteration_fn(void *arg)continue ; } -if (rand_r (&seeds[1 ]) %50 ==0 ) +if (rand_r (&seeds[1 ]) %50 ==0 ) { slot =radix_tree_iter_next (&iter); +rcu_read_unlock (); +rcu_barrier (); +rcu_read_lock (); + } }rcu_read_unlock (); } +rcu_unregister_thread (); +return NULL ; }
@@ -127,6 +147,8 @@ static void *untagged_iteration_fn(void *arg)*/ static void *remove_entries_fn (void *arg) { +rcu_register_thread (); +while (!test_complete) {int pgoff;
@@ -137,6 +159,8 @@ static void *remove_entries_fn(void *arg)pthread_mutex_unlock (&tree_lock); } +rcu_unregister_thread (); +return NULL ; }
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 7/33 radix tree test suite: iteration test misuses RCU
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Each thread needs to register itself with RCU, otherwise the reading thread's read lock has no effect and the freeing thread will free the memory in the tree without waiting for the read lock to be dropped.
--- tools/testing/radix-tree/iteration_check.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-)
tools/testing/radix-tree/iteration_check.c +26 -2
@@ -29,6 +29,8 @@ static void *add_entries_fn(void *arg) {int pgoff; +rcu_register_thread (); +while (!test_complete) {for (pgoff =0 ; pgoff <100 ; pgoff++) {pthread_mutex_lock (&tree_lock);
@@ -38,6 +40,8 @@ static void *add_entries_fn(void *arg) } } +rcu_unregister_thread (); +return NULL ; }
@@ -53,6 +57,8 @@ static void *tagged_iteration_fn(void *arg)struct radix_tree_iter iter;void **slot; +rcu_register_thread (); +while (!test_complete) {rcu_read_lock ();radix_tree_for_each_tagged (slot, &tree, &iter,0 ,TAG ) {
@@ -72,12 +78,18 @@ static void *tagged_iteration_fn(void *arg)continue ; } -if (rand_r (&seeds[0 ]) %50 ==0 ) +if (rand_r (&seeds[0 ]) %50 ==0 ) { slot =radix_tree_iter_next (&iter); +rcu_read_unlock (); +rcu_barrier (); +rcu_read_lock (); + } }rcu_read_unlock (); } +rcu_unregister_thread (); +return NULL ; }
@@ -93,6 +105,8 @@ static void *untagged_iteration_fn(void *arg)struct radix_tree_iter iter;void **slot; +rcu_register_thread (); +while (!test_complete) {rcu_read_lock ();radix_tree_for_each_slot (slot, &tree, &iter,0 ) {
@@ -112,12 +126,18 @@ static void *untagged_iteration_fn(void *arg)continue ; } -if (rand_r (&seeds[1 ]) %50 ==0 ) +if (rand_r (&seeds[1 ]) %50 ==0 ) { slot =radix_tree_iter_next (&iter); +rcu_read_unlock (); +rcu_barrier (); +rcu_read_lock (); + } }rcu_read_unlock (); } +rcu_unregister_thread (); +return NULL ; }
@@ -127,6 +147,8 @@ static void *untagged_iteration_fn(void *arg)*/ static void *remove_entries_fn (void *arg) { +rcu_register_thread (); +while (!test_complete) {int pgoff;
@@ -137,6 +159,8 @@ static void *remove_entries_fn(void *arg)pthread_mutex_unlock (&tree_lock); } +rcu_unregister_thread (); +return NULL ; }
Signature · 1 line
2.10.2
PATCH v3 8/33 radix tree test suite: benchmark for iterator
In reply to Matthew WilcoxFrom: Konstantin Khlebnikov <[email protected]>
This adds simple benchmark for iterator similar to one I've used for commit 78c1d78 ("radix-tree: introduce bit-optimized iterator")
Building with make BENCHMARK=1 set radix tree order to 6, this allows to get performance comparable to in kernel performance.
--- tools/testing/radix-tree/Makefile | 6 +- tools/testing/radix-tree/benchmark.c | 98 +++++++++++++++++++++++++++++++++ tools/testing/radix-tree/linux/kernel.h | 4 ++ tools/testing/radix-tree/main.c | 2 + tools/testing/radix-tree/test.h | 1 + 5 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 tools/testing/radix-tree/benchmark.c
tools/testing/radix-tree/Makefile +5 -1
@@ -4,7 +4,11 @@ LDFLAGS += -lpthread -lurcu TARGETS = main OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \ regression1.o regression2.o regression3.o multiorder.o \ - iteration_check.o + iteration_check.o benchmark.o + +ifdef BENCHMARK + CFLAGS += -DBENCHMARK=1 +endif targets: $(TARGETS)
tools/testing/radix-tree/benchmark.c new file +98 -0
98 lines not on this page · raw message
tools/testing/radix-tree/linux/kernel.h +4 -0
@@ -10,7 +10,11 @@#include "../../include/linux/compiler.h" #include "../../../include/linux/kconfig.h" +#ifdef BENCHMARK +#define RADIX_TREE_MAP_SHIFT 6 +#else #define RADIX_TREE_MAP_SHIFT 3 +#endif #ifndef NULL #define NULL 0
tools/testing/radix-tree/main.c +2 -0
@@ -352,6 +352,8 @@ int main(int argc, char **argv)/* Free any remaining preallocated nodes */ radix_tree_cpu_dead (0 ); +benchmark (); +sleep (1 );printf ("after sleep(1): %d allocated, preempt %d\n" , nr_allocated, preempt_count);
tools/testing/radix-tree/test.h +1 -0
@@ -28,6 +28,7 @@ void item_kill_tree(struct radix_tree_root *root);void tag_check (void );void multiorder_checks (void );void iteration_test (void ); +void benchmark (void );struct item *item_tag_set (struct radix_tree_root *root,unsigned long index,int tag);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 8/33 radix tree test suite: benchmark for iterator
In reply to Matthew WilcoxFrom: Konstantin Khlebnikov <[email protected]>
This adds simple benchmark for iterator similar to one I've used for commit 78c1d78 ("radix-tree: introduce bit-optimized iterator")
Building with make BENCHMARK=1 set radix tree order to 6, this allows to get performance comparable to in kernel performance.
--- tools/testing/radix-tree/Makefile | 6 +- tools/testing/radix-tree/benchmark.c | 98 +++++++++++++++++++++++++++++++++ tools/testing/radix-tree/linux/kernel.h | 4 ++ tools/testing/radix-tree/main.c | 2 + tools/testing/radix-tree/test.h | 1 + 5 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 tools/testing/radix-tree/benchmark.c
tools/testing/radix-tree/Makefile +5 -1
@@ -4,7 +4,11 @@ LDFLAGS += -lpthread -lurcu TARGETS = main OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \ regression1.o regression2.o regression3.o multiorder.o \ - iteration_check.o + iteration_check.o benchmark.o + +ifdef BENCHMARK + CFLAGS += -DBENCHMARK=1 +endif targets: $(TARGETS)
tools/testing/radix-tree/benchmark.c new file +98 -0
98 lines not on this page · raw message
tools/testing/radix-tree/linux/kernel.h +4 -0
@@ -10,7 +10,11 @@#include "../../include/linux/compiler.h" #include "../../../include/linux/kconfig.h" +#ifdef BENCHMARK +#define RADIX_TREE_MAP_SHIFT 6 +#else #define RADIX_TREE_MAP_SHIFT 3 +#endif #ifndef NULL #define NULL 0
tools/testing/radix-tree/main.c +2 -0
@@ -352,6 +352,8 @@ int main(int argc, char **argv)/* Free any remaining preallocated nodes */ radix_tree_cpu_dead (0 ); +benchmark (); +sleep (1 );printf ("after sleep(1): %d allocated, preempt %d\n" , nr_allocated, preempt_count);
tools/testing/radix-tree/test.h +1 -0
@@ -28,6 +28,7 @@ void item_kill_tree(struct radix_tree_root *root);void tag_check (void );void multiorder_checks (void );void iteration_test (void ); +void benchmark (void );struct item *item_tag_set (struct radix_tree_root *root,unsigned long index,int tag);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 9/33 radix tree test suite: Use rcu_barrier
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Calling rcu_barrier() allows all of the rcu-freed memory to be actually returned to the pool, and allows nr_allocated to return to 0. As well as allowing diffs between runs to be more useful, it also lets us pinpoint leaks more effectively.
--- tools/testing/radix-tree/main.c | 12 ++++++++++-- tools/testing/radix-tree/tag_check.c | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-)
tools/testing/radix-tree/main.c +10 -2
@@ -295,24 +295,31 @@ static void single_thread_tests(bool long_run)printf ("starting single_thread_tests: %d allocated, preempt %d\n" , nr_allocated, preempt_count);multiorder_checks (); +rcu_barrier ();printf ("after multiorder_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);locate_check (); +rcu_barrier ();printf ("after locate_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);tag_check (); +rcu_barrier ();printf ("after tag_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);gang_check (); +rcu_barrier ();printf ("after gang_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);add_and_check (); +rcu_barrier ();printf ("after add_and_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);dynamic_height_check (); +rcu_barrier ();printf ("after dynamic_height_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);big_gang_check (long_run); +rcu_barrier ();printf ("after big_gang_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);for (i =0 ; i < (long_run ?2000 :3 ); i++) {
@@ -320,6 +327,7 @@ static void single_thread_tests(bool long_run)printf ("%d " , i);fflush (stdout); } +rcu_barrier ();printf ("after copy_tag_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count); }
@@ -354,8 +362,8 @@ int main(int argc, char **argv)benchmark (); -sleep (1 ); -printf ("after sleep(1): %d allocated, preempt %d\n" , +rcu_barrier (); +printf ("after rcu_barrier: %d allocated, preempt %d\n" , nr_allocated, preempt_count);rcu_unregister_thread ();
tools/testing/radix-tree/tag_check.c +5 -0
@@ -51,6 +51,7 @@ void simple_checks(void)verify_tag_consistency (&tree,1 );printf ("before item_kill_tree: %d allocated\n" , nr_allocated);item_kill_tree (&tree); +rcu_barrier ();printf ("after item_kill_tree: %d allocated\n" , nr_allocated); }
@@ -331,12 +332,16 @@ void tag_check(void)single_check ();extend_checks ();contract_checks (); +rcu_barrier ();printf ("after extend_checks: %d allocated\n" , nr_allocated);__leak_check ();leak_check (); +rcu_barrier ();printf ("after leak_check: %d allocated\n" , nr_allocated);simple_checks (); +rcu_barrier ();printf ("after simple_checks: %d allocated\n" , nr_allocated);thrash_tags (); +rcu_barrier ();printf ("after thrash_tags: %d allocated\n" , nr_allocated); }
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 9/33 radix tree test suite: Use rcu_barrier
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Calling rcu_barrier() allows all of the rcu-freed memory to be actually returned to the pool, and allows nr_allocated to return to 0. As well as allowing diffs between runs to be more useful, it also lets us pinpoint leaks more effectively.
--- tools/testing/radix-tree/main.c | 12 ++++++++++-- tools/testing/radix-tree/tag_check.c | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-)
tools/testing/radix-tree/main.c +10 -2
@@ -295,24 +295,31 @@ static void single_thread_tests(bool long_run)printf ("starting single_thread_tests: %d allocated, preempt %d\n" , nr_allocated, preempt_count);multiorder_checks (); +rcu_barrier ();printf ("after multiorder_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);locate_check (); +rcu_barrier ();printf ("after locate_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);tag_check (); +rcu_barrier ();printf ("after tag_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);gang_check (); +rcu_barrier ();printf ("after gang_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);add_and_check (); +rcu_barrier ();printf ("after add_and_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);dynamic_height_check (); +rcu_barrier ();printf ("after dynamic_height_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);big_gang_check (long_run); +rcu_barrier ();printf ("after big_gang_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count);for (i =0 ; i < (long_run ?2000 :3 ); i++) {
@@ -320,6 +327,7 @@ static void single_thread_tests(bool long_run)printf ("%d " , i);fflush (stdout); } +rcu_barrier ();printf ("after copy_tag_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count); }
@@ -354,8 +362,8 @@ int main(int argc, char **argv)benchmark (); -sleep (1 ); -printf ("after sleep(1): %d allocated, preempt %d\n" , +rcu_barrier (); +printf ("after rcu_barrier: %d allocated, preempt %d\n" , nr_allocated, preempt_count);rcu_unregister_thread ();
tools/testing/radix-tree/tag_check.c +5 -0
@@ -51,6 +51,7 @@ void simple_checks(void)verify_tag_consistency (&tree,1 );printf ("before item_kill_tree: %d allocated\n" , nr_allocated);item_kill_tree (&tree); +rcu_barrier ();printf ("after item_kill_tree: %d allocated\n" , nr_allocated); }
@@ -331,12 +332,16 @@ void tag_check(void)single_check ();extend_checks ();contract_checks (); +rcu_barrier ();printf ("after extend_checks: %d allocated\n" , nr_allocated);__leak_check ();leak_check (); +rcu_barrier ();printf ("after leak_check: %d allocated\n" , nr_allocated);simple_checks (); +rcu_barrier ();printf ("after simple_checks: %d allocated\n" , nr_allocated);thrash_tags (); +rcu_barrier ();printf ("after thrash_tags: %d allocated\n" , nr_allocated); }
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 10/33 radix tree test suite: Handle exceptional entries
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
item_kill_tree() assumes that everything in the tree is a pointer to a struct item, which is annoying when testing the behaviour of exceptional entries. Fix it to delete exceptional entries on the assumption they don't need to be freed.
--- tools/testing/radix-tree/test.c | 7 +++++++ 1 file changed, 7 insertions(+)
tools/testing/radix-tree/test.c +7 -0
@@ -200,9 +200,16 @@ void verify_tag_consistency(struct radix_tree_root *root, unsigned int tag)void item_kill_tree (struct radix_tree_root *root) { +struct radix_tree_iter iter; +void **slot;struct item *items[32 ];int nfound; +radix_tree_for_each_slot (slot, root, &iter,0 ) { +if (radix_tree_exceptional_entry (*slot)) +radix_tree_delete (root, iter.index); + } +while ((nfound =radix_tree_gang_lookup (root, (void **)items,0 ,32 ))) {int i;
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 10/33 radix tree test suite: Handle exceptional entries
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
item_kill_tree() assumes that everything in the tree is a pointer to a struct item, which is annoying when testing the behaviour of exceptional entries. Fix it to delete exceptional entries on the assumption they don't need to be freed.
--- tools/testing/radix-tree/test.c | 7 +++++++ 1 file changed, 7 insertions(+)
tools/testing/radix-tree/test.c +7 -0
@@ -200,9 +200,16 @@ void verify_tag_consistency(struct radix_tree_root *root, unsigned int tag)void item_kill_tree (struct radix_tree_root *root) { +struct radix_tree_iter iter; +void **slot;struct item *items[32 ];int nfound; +radix_tree_for_each_slot (slot, root, &iter,0 ) { +if (radix_tree_exceptional_entry (*slot)) +radix_tree_delete (root, iter.index); + } +while ((nfound =radix_tree_gang_lookup (root, (void **)items,0 ,32 ))) {int i;
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 11/33 radix tree test suite: record order in each item
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This probably doubles the size of each item allocated by the test suite but it lets us check a few more things, and may be needed for upcoming API changes that require the caller pass in the order of the entry.
--- tools/testing/radix-tree/multiorder.c | 2 +- tools/testing/radix-tree/test.c | 29 +++++++++++++++++++---------- tools/testing/radix-tree/test.h | 6 +++--- 3 files changed, 23 insertions(+), 14 deletions(-)
tools/testing/radix-tree/multiorder.c +1 -1
@@ -125,7 +125,7 @@ static void multiorder_check(unsigned long index, int order)unsigned long min = index & ~((1UL << order) -1 );unsigned long max = min + (1UL << order);void **slot; -struct item *item2 =item_create (min); +struct item *item2 =item_create (min, order);RADIX_TREE (tree,GFP_KERNEL );printf ("Multiorder index %ld, order %d\n" , index, order);
tools/testing/radix-tree/test.c +19 -10
@@ -24,21 +24,29 @@ int item_tag_get(struct radix_tree_root *root, unsigned long index, int tag)return radix_tree_tag_get (root, index, tag); } -int __item_insert (struct radix_tree_root *root,struct item *item, -unsigned order) +int __item_insert (struct radix_tree_root *root,struct item *item) { -return __radix_tree_insert (root, item->index, order, item); +return __radix_tree_insert (root, item->index, item->order, item); }int item_insert (struct radix_tree_root *root,unsigned long index) { -return __item_insert (root,item_create (index),0 ); +return __item_insert (root,item_create (index,0 )); }int item_insert_order (struct radix_tree_root *root,unsigned long index,unsigned order) { -return __item_insert (root,item_create (index), order); +return __item_insert (root,item_create (index, order)); +} + +void item_sanity (struct item *item,unsigned long index) +{ +unsigned long mask; +assert (!radix_tree_is_internal_node (item)); +assert (item->order <BITS_PER_LONG ); + mask = (1UL << item->order) -1 ; +assert ((item->index | mask) == (index | mask)); }int item_delete (struct radix_tree_root *root,unsigned long index)
@@ -46,18 +54,19 @@ int item_delete(struct radix_tree_root *root, unsigned long index)struct item *item =radix_tree_delete (root, index);if (item) { -assert (item->index == index); +item_sanity (item, index);free (item);return 1 ; }return 0 ; } -struct item *item_create (unsigned long index) +struct item *item_create (unsigned long index,unsigned int order) {struct item *ret =malloc (sizeof (*ret)); ret->index = index; + ret->order = order;return ret; }
@@ -66,8 +75,8 @@ void item_check_present(struct radix_tree_root *root, unsigned long index)struct item *item; item =radix_tree_lookup (root, index); -assert (item !=0 ); -assert (item->index == index); +assert (item !=NULL ); +item_sanity (item, index); }struct item *item_lookup (struct radix_tree_root *root,unsigned long index)
@@ -80,7 +89,7 @@ void item_check_absent(struct radix_tree_root *root, unsigned long index)struct item *item; item =radix_tree_lookup (root, index); -assert (item ==0 ); +assert (item ==NULL ); }/*
tools/testing/radix-tree/test.h +3 -3
@@ -5,11 +5,11 @@struct item {unsigned long index; +unsigned int order; }; -struct item *item_create (unsigned long index); -int __item_insert (struct radix_tree_root *root,struct item *item, -unsigned order); +struct item *item_create (unsigned long index,unsigned int order); +int __item_insert (struct radix_tree_root *root,struct item *item);int item_insert (struct radix_tree_root *root,unsigned long index);int item_insert_order (struct radix_tree_root *root,unsigned long index,unsigned order);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 11/33 radix tree test suite: record order in each item
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This probably doubles the size of each item allocated by the test suite but it lets us check a few more things, and may be needed for upcoming API changes that require the caller pass in the order of the entry.
--- tools/testing/radix-tree/multiorder.c | 2 +- tools/testing/radix-tree/test.c | 29 +++++++++++++++++++---------- tools/testing/radix-tree/test.h | 6 +++--- 3 files changed, 23 insertions(+), 14 deletions(-)
tools/testing/radix-tree/multiorder.c +1 -1
@@ -125,7 +125,7 @@ static void multiorder_check(unsigned long index, int order)unsigned long min = index & ~((1UL << order) -1 );unsigned long max = min + (1UL << order);void **slot; -struct item *item2 =item_create (min); +struct item *item2 =item_create (min, order);RADIX_TREE (tree,GFP_KERNEL );printf ("Multiorder index %ld, order %d\n" , index, order);
tools/testing/radix-tree/test.c +19 -10
@@ -24,21 +24,29 @@ int item_tag_get(struct radix_tree_root *root, unsigned long index, int tag)return radix_tree_tag_get (root, index, tag); } -int __item_insert (struct radix_tree_root *root,struct item *item, -unsigned order) +int __item_insert (struct radix_tree_root *root,struct item *item) { -return __radix_tree_insert (root, item->index, order, item); +return __radix_tree_insert (root, item->index, item->order, item); }int item_insert (struct radix_tree_root *root,unsigned long index) { -return __item_insert (root,item_create (index),0 ); +return __item_insert (root,item_create (index,0 )); }int item_insert_order (struct radix_tree_root *root,unsigned long index,unsigned order) { -return __item_insert (root,item_create (index), order); +return __item_insert (root,item_create (index, order)); +} + +void item_sanity (struct item *item,unsigned long index) +{ +unsigned long mask; +assert (!radix_tree_is_internal_node (item)); +assert (item->order <BITS_PER_LONG ); + mask = (1UL << item->order) -1 ; +assert ((item->index | mask) == (index | mask)); }int item_delete (struct radix_tree_root *root,unsigned long index)
@@ -46,18 +54,19 @@ int item_delete(struct radix_tree_root *root, unsigned long index)struct item *item =radix_tree_delete (root, index);if (item) { -assert (item->index == index); +item_sanity (item, index);free (item);return 1 ; }return 0 ; } -struct item *item_create (unsigned long index) +struct item *item_create (unsigned long index,unsigned int order) {struct item *ret =malloc (sizeof (*ret)); ret->index = index; + ret->order = order;return ret; }
@@ -66,8 +75,8 @@ void item_check_present(struct radix_tree_root *root, unsigned long index)struct item *item; item =radix_tree_lookup (root, index); -assert (item !=0 ); -assert (item->index == index); +assert (item !=NULL ); +item_sanity (item, index); }struct item *item_lookup (struct radix_tree_root *root,unsigned long index)
@@ -80,7 +89,7 @@ void item_check_absent(struct radix_tree_root *root, unsigned long index)struct item *item; item =radix_tree_lookup (root, index); -assert (item ==0 ); +assert (item ==NULL ); }/*
tools/testing/radix-tree/test.h +3 -3
@@ -5,11 +5,11 @@struct item {unsigned long index; +unsigned int order; }; -struct item *item_create (unsigned long index); -int __item_insert (struct radix_tree_root *root,struct item *item, -unsigned order); +struct item *item_create (unsigned long index,unsigned int order); +int __item_insert (struct radix_tree_root *root,struct item *item);int item_insert (struct radix_tree_root *root,unsigned long index);int item_insert_order (struct radix_tree_root *root,unsigned long index,unsigned order);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 12/33 tools: Add more bitmap functions
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
I need the following functions for the radix tree:
bitmap_fillbitmap_emptybitmap_full
Copy the implementations from include/linux/bitmap.h
--- tools/include/linux/bitmap.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
tools/include/linux/bitmap.h +26 -0
@@ -35,6 +35,32 @@ static inline void bitmap_zero(unsigned long *dst, int nbits) } } +static inline void bitmap_fill (unsigned long *dst,unsigned int nbits) +{ +unsigned int nlongs =BITS_TO_LONGS (nbits); +if (!small_const_nbits (nbits)) { +unsigned int len = (nlongs -1 ) *sizeof (unsigned long ); +memset (dst,0xff , len); + } + dst[nlongs -1 ] =BITMAP_LAST_WORD_MASK (nbits); +} + +static inline int bitmap_empty (const unsigned long *src,unsigned nbits) +{ +if (small_const_nbits (nbits)) +return ! (*src &BITMAP_LAST_WORD_MASK (nbits)); + +return find_first_bit (src, nbits) == nbits; +} + +static inline int bitmap_full (const unsigned long *src,unsigned int nbits) +{ +if (small_const_nbits (nbits)) +return ! (~(*src) &BITMAP_LAST_WORD_MASK (nbits)); + +return find_first_zero_bit (src, nbits) == nbits; +} +static inline int bitmap_weight (const unsigned long *src,int nbits) {if (small_const_nbits (nbits))
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 12/33 tools: Add more bitmap functions
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
I need the following functions for the radix tree:
bitmap_fillbitmap_emptybitmap_full
Copy the implementations from include/linux/bitmap.h
--- tools/include/linux/bitmap.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
tools/include/linux/bitmap.h +26 -0
@@ -35,6 +35,32 @@ static inline void bitmap_zero(unsigned long *dst, int nbits) } } +static inline void bitmap_fill (unsigned long *dst,unsigned int nbits) +{ +unsigned int nlongs =BITS_TO_LONGS (nbits); +if (!small_const_nbits (nbits)) { +unsigned int len = (nlongs -1 ) *sizeof (unsigned long ); +memset (dst,0xff , len); + } + dst[nlongs -1 ] =BITMAP_LAST_WORD_MASK (nbits); +} + +static inline int bitmap_empty (const unsigned long *src,unsigned nbits) +{ +if (small_const_nbits (nbits)) +return ! (*src &BITMAP_LAST_WORD_MASK (nbits)); + +return find_first_bit (src, nbits) == nbits; +} + +static inline int bitmap_full (const unsigned long *src,unsigned int nbits) +{ +if (small_const_nbits (nbits)) +return ! (~(*src) &BITMAP_LAST_WORD_MASK (nbits)); + +return find_first_zero_bit (src, nbits) == nbits; +} +static inline int bitmap_weight (const unsigned long *src,int nbits) {if (small_const_nbits (nbits))
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 13/33 radix tree test suite: Use common find-bit code
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Remove the old find_next_bit code in favour of linking in the find_bit code from tools/lib.
--- tools/testing/radix-tree/Makefile | 7 ++- tools/testing/radix-tree/find_next_bit.c | 57 ---------------------- tools/testing/radix-tree/linux/bitops.h | 40 +++++++++------ tools/testing/radix-tree/linux/bitops/non-atomic.h | 13 +++-- tools/testing/radix-tree/linux/kernel.h | 11 +++++ 5 files changed, 48 insertions(+), 80 deletions(-) delete mode 100644 tools/testing/radix-tree/find_next_bit.c
tools/testing/radix-tree/Makefile +6 -1
@@ -18,7 +18,12 @@ main: $(OFILES) clean: $(RM) -f $(TARGETS) *.o radix-tree.c -$(OFILES): *.h */*.h ../../../include/linux/radix-tree.h ../../include/linux/*.h +find_next_bit.o: ../../lib/find_bit.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(OFILES): *.h */*.h \ + ../../include/linux/*.h \ + ../../../include/linux/radix-tree.h radix-tree.c: ../../../lib/radix-tree.c sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@
tools/testing/radix-tree/find_next_bit.c deleted +0 -57
57 lines not on this page · raw message
tools/testing/radix-tree/linux/bitops.h +25 -15
@@ -2,9 +2,14 @@#define _ASM_GENERIC_BITOPS_NON_ATOMIC_H_#include <linux/types.h> +#include <linux/bitops/find.h> +#include <linux/bitops/hweight.h> +#include <linux/kernel.h> -#define BITOP_MASK (nr) (1UL << ((nr) % BITS_PER_LONG)) -#define BITOP_WORD (nr) ((nr) / BITS_PER_LONG) +#define BIT_MASK (nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD (nr) ((nr) / BITS_PER_LONG) +#define BITS_PER_BYTE 8 +#define BITS_TO_LONGS (nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))/** * __set_bit - Set a bit in memory
@@ -17,16 +22,16 @@*/ static inline void __set_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr); *p |= mask; }static inline void __clear_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr); *p &= ~mask; }
@@ -42,8 +47,8 @@ static inline void __clear_bit(int nr, volatile unsigned long *addr)*/ static inline void __change_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr); *p ^= mask; }
@@ -59,8 +64,8 @@ static inline void __change_bit(int nr, volatile unsigned long *addr)*/ static inline int __test_and_set_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr);unsigned long old = *p; *p = old | mask;
@@ -78,8 +83,8 @@ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)*/ static inline int __test_and_clear_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr);unsigned long old = *p; *p = old & ~mask;
@@ -90,8 +95,8 @@ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)static inline int __test_and_change_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr);unsigned long old = *p; *p = old ^ mask;
@@ -105,7 +110,7 @@ static inline int __test_and_change_bit(int nr,*/ static inline int test_bit (int nr,const volatile unsigned long *addr) { -return 1UL & (addr[BITOP_WORD (nr)] >> (nr & (BITS_PER_LONG -1 ))); +return 1UL & (addr[BIT_WORD (nr)] >> (nr & (BITS_PER_LONG -1 ))); }/**
@@ -147,4 +152,9 @@ unsigned long find_next_bit(const unsigned long *addr,unsigned long size, unsigned long offset); +static inline unsigned long hweight_long (unsigned long w) +{ +return sizeof (w) ==4 ?hweight32 (w) :hweight64 (w); +} +#endif /* _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ */
tools/testing/radix-tree/linux/bitops/non-atomic.h +6 -7
@@ -3,7 +3,6 @@#include <asm/types.h> -#define BITOP_MASK (nr) (1UL << ((nr) % BITS_PER_LONG))#define BITOP_WORD (nr) ((nr) / BITS_PER_LONG)/**
@@ -17,7 +16,7 @@*/ static inline void __set_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); *p |= mask;
@@ -25,7 +24,7 @@ static inline void __set_bit(int nr, volatile unsigned long *addr)static inline void __clear_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); *p &= ~mask;
@@ -42,7 +41,7 @@ static inline void __clear_bit(int nr, volatile unsigned long *addr)*/ static inline void __change_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); *p ^= mask;
@@ -59,7 +58,7 @@ static inline void __change_bit(int nr, volatile unsigned long *addr)*/ static inline int __test_and_set_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr);unsigned long old = *p;
@@ -78,7 +77,7 @@ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)*/ static inline int __test_and_clear_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr);unsigned long old = *p;
@@ -90,7 +89,7 @@ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)static inline int __test_and_change_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr);unsigned long old = *p;
tools/testing/radix-tree/linux/kernel.h +11 -0
@@ -47,4 +47,15 @@ static inline int in_interrupt(void) {return 0 ; } + +/* +* This looks more complex than it should be. But we need to +* get the type for the ~ right in round_down (it needs to be +* as wide as the result!), and we want to evaluate the macro +* arguments just once each. +*/ +#define __round_mask (x, y) ((__typeof__(x))((y)-1)) +#define round_up (x, y) ((((x)-1) | __round_mask(x, y))+1) +#define round_down (x, y) ((x) & ~__round_mask(x, y)) +#endif /* _KERNEL_H */
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 13/33 radix tree test suite: Use common find-bit code
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Remove the old find_next_bit code in favour of linking in the find_bit code from tools/lib.
--- tools/testing/radix-tree/Makefile | 7 ++- tools/testing/radix-tree/find_next_bit.c | 57 ---------------------- tools/testing/radix-tree/linux/bitops.h | 40 +++++++++------ tools/testing/radix-tree/linux/bitops/non-atomic.h | 13 +++-- tools/testing/radix-tree/linux/kernel.h | 11 +++++ 5 files changed, 48 insertions(+), 80 deletions(-) delete mode 100644 tools/testing/radix-tree/find_next_bit.c
tools/testing/radix-tree/Makefile +6 -1
@@ -18,7 +18,12 @@ main: $(OFILES) clean: $(RM) -f $(TARGETS) *.o radix-tree.c -$(OFILES): *.h */*.h ../../../include/linux/radix-tree.h ../../include/linux/*.h +find_next_bit.o: ../../lib/find_bit.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(OFILES): *.h */*.h \ + ../../include/linux/*.h \ + ../../../include/linux/radix-tree.h radix-tree.c: ../../../lib/radix-tree.c sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@
tools/testing/radix-tree/find_next_bit.c deleted +0 -57
57 lines not on this page · raw message
tools/testing/radix-tree/linux/bitops.h +25 -15
@@ -2,9 +2,14 @@#define _ASM_GENERIC_BITOPS_NON_ATOMIC_H_#include <linux/types.h> +#include <linux/bitops/find.h> +#include <linux/bitops/hweight.h> +#include <linux/kernel.h> -#define BITOP_MASK (nr) (1UL << ((nr) % BITS_PER_LONG)) -#define BITOP_WORD (nr) ((nr) / BITS_PER_LONG) +#define BIT_MASK (nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD (nr) ((nr) / BITS_PER_LONG) +#define BITS_PER_BYTE 8 +#define BITS_TO_LONGS (nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))/** * __set_bit - Set a bit in memory
@@ -17,16 +22,16 @@*/ static inline void __set_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr); *p |= mask; }static inline void __clear_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr); *p &= ~mask; }
@@ -42,8 +47,8 @@ static inline void __clear_bit(int nr, volatile unsigned long *addr)*/ static inline void __change_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr); *p ^= mask; }
@@ -59,8 +64,8 @@ static inline void __change_bit(int nr, volatile unsigned long *addr)*/ static inline int __test_and_set_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr);unsigned long old = *p; *p = old | mask;
@@ -78,8 +83,8 @@ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)*/ static inline int __test_and_clear_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr);unsigned long old = *p; *p = old & ~mask;
@@ -90,8 +95,8 @@ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)static inline int __test_and_change_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); -unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr);unsigned long old = *p; *p = old ^ mask;
@@ -105,7 +110,7 @@ static inline int __test_and_change_bit(int nr,*/ static inline int test_bit (int nr,const volatile unsigned long *addr) { -return 1UL & (addr[BITOP_WORD (nr)] >> (nr & (BITS_PER_LONG -1 ))); +return 1UL & (addr[BIT_WORD (nr)] >> (nr & (BITS_PER_LONG -1 ))); }/**
@@ -147,4 +152,9 @@ unsigned long find_next_bit(const unsigned long *addr,unsigned long size, unsigned long offset); +static inline unsigned long hweight_long (unsigned long w) +{ +return sizeof (w) ==4 ?hweight32 (w) :hweight64 (w); +} +#endif /* _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ */
tools/testing/radix-tree/linux/bitops/non-atomic.h +6 -7
@@ -3,7 +3,6 @@#include <asm/types.h> -#define BITOP_MASK (nr) (1UL << ((nr) % BITS_PER_LONG))#define BITOP_WORD (nr) ((nr) / BITS_PER_LONG)/**
@@ -17,7 +16,7 @@*/ static inline void __set_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); *p |= mask;
@@ -25,7 +24,7 @@ static inline void __set_bit(int nr, volatile unsigned long *addr)static inline void __clear_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); *p &= ~mask;
@@ -42,7 +41,7 @@ static inline void __clear_bit(int nr, volatile unsigned long *addr)*/ static inline void __change_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr); *p ^= mask;
@@ -59,7 +58,7 @@ static inline void __change_bit(int nr, volatile unsigned long *addr)*/ static inline int __test_and_set_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr);unsigned long old = *p;
@@ -78,7 +77,7 @@ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)*/ static inline int __test_and_clear_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr);unsigned long old = *p;
@@ -90,7 +89,7 @@ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)static inline int __test_and_change_bit (int nr,volatile unsigned long *addr) { -unsigned long mask =BITOP_MASK (nr); +unsigned long mask =BIT_MASK (nr);unsigned long *p = ((unsigned long *)addr) +BITOP_WORD (nr);unsigned long old = *p;
tools/testing/radix-tree/linux/kernel.h +11 -0
@@ -47,4 +47,15 @@ static inline int in_interrupt(void) {return 0 ; } + +/* +* This looks more complex than it should be. But we need to +* get the type for the ~ right in round_down (it needs to be +* as wide as the result!), and we want to evaluate the macro +* arguments just once each. +*/ +#define __round_mask (x, y) ((__typeof__(x))((y)-1)) +#define round_up (x, y) ((((x)-1) | __round_mask(x, y))+1) +#define round_down (x, y) ((x) & ~__round_mask(x, y)) +#endif /* _KERNEL_H */
Signature · 1 line
2.10.2
PATCH v3 14/33 radix-tree: Fix typo
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
--- lib/radix-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/radix-tree.c +1 -1
@@ -1109,7 +1109,7 @@ void **radix_tree_next_chunk(struct radix_tree_root *root,* because RADIX_TREE_MAP_SHIFT < BITS_PER_LONG. * * This condition also used by radix_tree_next_slot() to stop -* contiguous iterating, and forbid swithing to the next chunk. +* contiguous iterating, and forbid switching to the next chunk. */ index = iter->next_index;if (!index && iter->index)
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 14/33 radix-tree: Fix typo
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
--- lib/radix-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/radix-tree.c +1 -1
@@ -1109,7 +1109,7 @@ void **radix_tree_next_chunk(struct radix_tree_root *root,* because RADIX_TREE_MAP_SHIFT < BITS_PER_LONG. * * This condition also used by radix_tree_next_slot() to stop -* contiguous iterating, and forbid swithing to the next chunk. +* contiguous iterating, and forbid switching to the next chunk. */ index = iter->next_index;if (!index && iter->index)
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 15/33 radix-tree: Move rcu_head into a union with private_list
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
I want to be able to reference node->parent after freeing node. Currently node->parent is in a union with rcu_head, so it is overwritten when the node is put on the RCU list. We know that private_list is not referenced after the node is freed, so it is safe for these two members to share space.
--- include/linux/radix-tree.h | 14 ++++---------- lib/radix-tree.c | 1 + 2 files changed, 5 insertions(+), 10 deletions(-)
include/linux/radix-tree.h +4 -10
@@ -85,18 +85,12 @@ struct radix_tree_node {unsigned char offset;/* Slot offset in parent */ unsigned char count;/* Total entry count */ unsigned char exceptional;/* Exceptional entry count */ +struct radix_tree_node *parent;/* Used when ascending tree */ +void *private_data;/* For tree user */ union { -struct { -/* Used when ascending tree */ -struct radix_tree_node *parent; -/* For tree user */ -void *private_data; - }; -/* Used when freeing node */ -struct rcu_head rcu_head; +struct list_head private_list;/* For tree user */ +struct rcu_head rcu_head;/* Used when freeing node */ }; -/* For tree user */ -struct list_head private_list;void __rcu *slots[RADIX_TREE_MAP_SIZE ];unsigned long tags[RADIX_TREE_MAX_TAGS ][RADIX_TREE_TAG_LONGS ]; };
lib/radix-tree.c +1 -0
@@ -324,6 +324,7 @@ static void radix_tree_node_rcu_free(struct rcu_head *head)tag_clear (node, i,0 ); node->slots[0 ] =NULL ; +INIT_LIST_HEAD (&node->private_list);kmem_cache_free (radix_tree_node_cachep, node); }
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 15/33 radix-tree: Move rcu_head into a union with private_list
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
I want to be able to reference node->parent after freeing node. Currently node->parent is in a union with rcu_head, so it is overwritten when the node is put on the RCU list. We know that private_list is not referenced after the node is freed, so it is safe for these two members to share space.
--- include/linux/radix-tree.h | 14 ++++---------- lib/radix-tree.c | 1 + 2 files changed, 5 insertions(+), 10 deletions(-)
include/linux/radix-tree.h +4 -10
@@ -85,18 +85,12 @@ struct radix_tree_node {unsigned char offset;/* Slot offset in parent */ unsigned char count;/* Total entry count */ unsigned char exceptional;/* Exceptional entry count */ +struct radix_tree_node *parent;/* Used when ascending tree */ +void *private_data;/* For tree user */ union { -struct { -/* Used when ascending tree */ -struct radix_tree_node *parent; -/* For tree user */ -void *private_data; - }; -/* Used when freeing node */ -struct rcu_head rcu_head; +struct list_head private_list;/* For tree user */ +struct rcu_head rcu_head;/* Used when freeing node */ }; -/* For tree user */ -struct list_head private_list;void __rcu *slots[RADIX_TREE_MAP_SIZE ];unsigned long tags[RADIX_TREE_MAX_TAGS ][RADIX_TREE_TAG_LONGS ]; };
lib/radix-tree.c +1 -0
@@ -324,6 +324,7 @@ static void radix_tree_node_rcu_free(struct rcu_head *head)tag_clear (node, i,0 ); node->slots[0 ] =NULL ; +INIT_LIST_HEAD (&node->private_list);kmem_cache_free (radix_tree_node_cachep, node); }
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 16/33 radix-tree: Create node_tag_set()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Similar to node_tag_clear(), factor node_tag_set() out of radix_tree_range_tag_if_tagged().
--- lib/radix-tree.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-)
lib/radix-tree.c +19 -22
@@ -990,6 +990,22 @@ static void node_tag_clear(struct radix_tree_root *root,root_tag_clear (root, tag); } +static void node_tag_set (struct radix_tree_root *root, +struct radix_tree_node *node, +unsigned int tag,unsigned int offset) +{ +while (node) { +if (tag_get (node, tag, offset)) +return ; +tag_set (node, tag, offset); + offset = node->offset; + node = node->parent; + } + +if (!root_tag_get (root, tag)) +root_tag_set (root, tag); +} +/** * radix_tree_tag_clear - clear a tag on a radix tree node * @root: radix tree root
@@ -1228,7 +1244,7 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,unsigned long nr_to_tag, unsigned int iftag, unsigned int settag) { - struct radix_tree_node *parent, *node, *child; + struct radix_tree_node *node, *child;unsigned long maxindex;unsigned long tagged =0 ;unsigned long index = *first_indexp;
@@ -1263,22 +1279,8 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,continue ; } -/* tag the leaf */ tagged++; -tag_set (node, settag, offset); - -/* walk back up the path tagging interior nodes */ - parent = node; -for (;;) { - offset = parent->offset; - parent = parent->parent; -if (!parent) -break ; -/* stop if we find a node with the tag already set */ -if (tag_get (parent, settag, offset)) -break ; -tag_set (parent, settag, offset); - } +node_tag_set (root, node, settag, offset); next:/* Go to next entry in node */ index = ((index >> node->shift) +1 ) << node->shift;
@@ -1300,12 +1302,7 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,if (tagged >= nr_to_tag)break ; } -/* -* We need not to tag the root tag if there is no tag which is set with -* settag within the range from *first_indexp to last_index. -*/ -if (tagged >0 ) -root_tag_set (root, settag); + *first_indexp = index;return tagged;
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 16/33 radix-tree: Create node_tag_set()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Similar to node_tag_clear(), factor node_tag_set() out of radix_tree_range_tag_if_tagged().
--- lib/radix-tree.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-)
lib/radix-tree.c +19 -22
@@ -990,6 +990,22 @@ static void node_tag_clear(struct radix_tree_root *root,root_tag_clear (root, tag); } +static void node_tag_set (struct radix_tree_root *root, +struct radix_tree_node *node, +unsigned int tag,unsigned int offset) +{ +while (node) { +if (tag_get (node, tag, offset)) +return ; +tag_set (node, tag, offset); + offset = node->offset; + node = node->parent; + } + +if (!root_tag_get (root, tag)) +root_tag_set (root, tag); +} +/** * radix_tree_tag_clear - clear a tag on a radix tree node * @root: radix tree root
@@ -1228,7 +1244,7 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,unsigned long nr_to_tag, unsigned int iftag, unsigned int settag) { - struct radix_tree_node *parent, *node, *child; + struct radix_tree_node *node, *child;unsigned long maxindex;unsigned long tagged =0 ;unsigned long index = *first_indexp;
@@ -1263,22 +1279,8 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,continue ; } -/* tag the leaf */ tagged++; -tag_set (node, settag, offset); - -/* walk back up the path tagging interior nodes */ - parent = node; -for (;;) { - offset = parent->offset; - parent = parent->parent; -if (!parent) -break ; -/* stop if we find a node with the tag already set */ -if (tag_get (parent, settag, offset)) -break ; -tag_set (parent, settag, offset); - } +node_tag_set (root, node, settag, offset); next:/* Go to next entry in node */ index = ((index >> node->shift) +1 ) << node->shift;
@@ -1300,12 +1302,7 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,if (tagged >= nr_to_tag)break ; } -/* -* We need not to tag the root tag if there is no tag which is set with -* settag within the range from *first_indexp to last_index. -*/ -if (tagged >0 ) -root_tag_set (root, settag); + *first_indexp = index;return tagged;
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 17/33 radix-tree: Make radix_tree_find_next_bit more useful
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Since this function is specialised to the radix tree, pass in the node and tag to calculate the address of the bitmap in radix_tree_find_next_bit() instead of the caller. Likewise, there is no need to pass in the size of the bitmap.
--- lib/radix-tree.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
lib/radix-tree.c +7 -10
@@ -190,13 +190,12 @@ static inline int any_tag_set(struct radix_tree_node *node, unsigned int tag)* Returns next bit offset, or size if nothing found. */ static __always_inline unsigned long -radix_tree_find_next_bit (const unsigned long *addr, -unsigned long size,unsigned long offset) +radix_tree_find_next_bit (struct radix_tree_node *node,unsigned int tag, +unsigned long offset) { -if (!__builtin_constant_p (size)) -return find_next_bit (addr, size, offset); +const unsigned long *addr = node->tags[tag]; -if (offset < size) { +if (offset <RADIX_TREE_MAP_SIZE ) {unsigned long tmp; addr += offset /BITS_PER_LONG ;
@@ -204,14 +203,14 @@ radix_tree_find_next_bit(const unsigned long *addr,if (tmp)return __ffs (tmp) + offset; offset = (offset +BITS_PER_LONG ) & ~(BITS_PER_LONG -1 ); -while (offset < size) { +while (offset <RADIX_TREE_MAP_SIZE ) { tmp = *++addr;if (tmp)return __ffs (tmp) + offset; offset +=BITS_PER_LONG ; } } -return size; +return RADIX_TREE_MAP_SIZE ; }#ifndef __KERNEL__
@@ -1159,9 +1158,7 @@ void **radix_tree_next_chunk(struct radix_tree_root *root,return NULL ;if (flags &RADIX_TREE_ITER_TAGGED ) - offset =radix_tree_find_next_bit ( - node->tags[tag], -RADIX_TREE_MAP_SIZE , + offset =radix_tree_find_next_bit (node, tag, offset +1 );else while (++offset <RADIX_TREE_MAP_SIZE ) {
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 17/33 radix-tree: Make radix_tree_find_next_bit more useful
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Since this function is specialised to the radix tree, pass in the node and tag to calculate the address of the bitmap in radix_tree_find_next_bit() instead of the caller. Likewise, there is no need to pass in the size of the bitmap.
--- lib/radix-tree.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
lib/radix-tree.c +7 -10
@@ -190,13 +190,12 @@ static inline int any_tag_set(struct radix_tree_node *node, unsigned int tag)* Returns next bit offset, or size if nothing found. */ static __always_inline unsigned long -radix_tree_find_next_bit (const unsigned long *addr, -unsigned long size,unsigned long offset) +radix_tree_find_next_bit (struct radix_tree_node *node,unsigned int tag, +unsigned long offset) { -if (!__builtin_constant_p (size)) -return find_next_bit (addr, size, offset); +const unsigned long *addr = node->tags[tag]; -if (offset < size) { +if (offset <RADIX_TREE_MAP_SIZE ) {unsigned long tmp; addr += offset /BITS_PER_LONG ;
@@ -204,14 +203,14 @@ radix_tree_find_next_bit(const unsigned long *addr,if (tmp)return __ffs (tmp) + offset; offset = (offset +BITS_PER_LONG ) & ~(BITS_PER_LONG -1 ); -while (offset < size) { +while (offset <RADIX_TREE_MAP_SIZE ) { tmp = *++addr;if (tmp)return __ffs (tmp) + offset; offset +=BITS_PER_LONG ; } } -return size; +return RADIX_TREE_MAP_SIZE ; }#ifndef __KERNEL__
@@ -1159,9 +1158,7 @@ void **radix_tree_next_chunk(struct radix_tree_root *root,return NULL ;if (flags &RADIX_TREE_ITER_TAGGED ) - offset =radix_tree_find_next_bit ( - node->tags[tag], -RADIX_TREE_MAP_SIZE , + offset =radix_tree_find_next_bit (node, tag, offset +1 );else while (++offset <RADIX_TREE_MAP_SIZE ) {
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 18/33 radix-tree: Improve dump output
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Print the indices of the entries as unsigned (instead of signed) integers and print the parent node of each entry to help navigate around larger trees where the layout is not quite so obvious. Print the indices covered by a node. Rearrange the order of fields printed so the indices and parents line up for each type of entry.
--- lib/radix-tree.c | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-)
lib/radix-tree.c +26 -23
@@ -213,15 +213,29 @@ radix_tree_find_next_bit(struct radix_tree_node *node, unsigned int tag,return RADIX_TREE_MAP_SIZE ; } +/* +* The maximum index which can be stored in a radix tree +*/ +static inline unsigned long shift_maxindex (unsigned int shift) +{ +return (RADIX_TREE_MAP_SIZE << shift) -1 ; +} + +static inline unsigned long node_maxindex (struct radix_tree_node *node) +{ +return shift_maxindex (node->shift); +} +#ifndef __KERNEL__static void dump_node (struct radix_tree_node *node,unsigned long index) {unsigned long i; -pr_debug ("radix node: %p offset %d tags %lx %lx %lx shift %d count %d exceptional %d parent %p\n" , - node, node->offset, +pr_debug ("radix node: %p offset %d indices %lu-%lu parent %p tags %lx %lx %lx shift %d count %d exceptional %d\n" , + node, node->offset, index, index |node_maxindex (node), + node->parent, node->tags[0 ][0 ], node->tags[1 ][0 ], node->tags[2 ][0 ], - node->shift, node->count, node->exceptional, node->parent); + node->shift, node->count, node->exceptional);for (i =0 ; i <RADIX_TREE_MAP_SIZE ; i++) {unsigned long first = index | (i << node->shift);
@@ -229,14 +243,16 @@ static void dump_node(struct radix_tree_node *node, unsigned long index)void *entry = node->slots[i];if (!entry)continue ; -if (is_sibling_entry (node, entry)) { -pr_debug ("radix sblng %p offset %ld val %p indices %ld-%ld\n" , - entry, i, - *(void **)entry_to_node (entry), - first, last); +if (entry ==RADIX_TREE_RETRY ) { +pr_debug ("radix retry offset %ld indices %lu-%lu parent %p\n" , + i, first, last, node); }else if (!radix_tree_is_internal_node (entry)) { -pr_debug ("radix entry %p offset %ld indices %ld-%ld\n" , - entry, i, first, last); +pr_debug ("radix entry %p offset %ld indices %lu-%lu parent %p\n" , + entry, i, first, last, node); + }else if (is_sibling_entry (node, entry)) { +pr_debug ("radix sblng %p offset %ld indices %lu-%lu parent %p val %p\n" , + entry, i, first, last, node, + *(void **)entry_to_node (entry)); }else {dump_node (entry_to_node (entry), first); }
@@ -454,19 +470,6 @@ int radix_tree_maybe_preload_order(gfp_t gfp_mask, int order)return __radix_tree_preload (gfp_mask, nr_nodes); } -/* -* The maximum index which can be stored in a radix tree -*/ -static inline unsigned long shift_maxindex (unsigned int shift) -{ -return (RADIX_TREE_MAP_SIZE << shift) -1 ; -} - -static inline unsigned long node_maxindex (struct radix_tree_node *node) -{ -return shift_maxindex (node->shift); -} -static unsigned radix_tree_load_root (struct radix_tree_root *root,struct radix_tree_node **nodep,unsigned long *maxindex) {
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 18/33 radix-tree: Improve dump output
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Print the indices of the entries as unsigned (instead of signed) integers and print the parent node of each entry to help navigate around larger trees where the layout is not quite so obvious. Print the indices covered by a node. Rearrange the order of fields printed so the indices and parents line up for each type of entry.
--- lib/radix-tree.c | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-)
lib/radix-tree.c +26 -23
@@ -213,15 +213,29 @@ radix_tree_find_next_bit(struct radix_tree_node *node, unsigned int tag,return RADIX_TREE_MAP_SIZE ; } +/* +* The maximum index which can be stored in a radix tree +*/ +static inline unsigned long shift_maxindex (unsigned int shift) +{ +return (RADIX_TREE_MAP_SIZE << shift) -1 ; +} + +static inline unsigned long node_maxindex (struct radix_tree_node *node) +{ +return shift_maxindex (node->shift); +} +#ifndef __KERNEL__static void dump_node (struct radix_tree_node *node,unsigned long index) {unsigned long i; -pr_debug ("radix node: %p offset %d tags %lx %lx %lx shift %d count %d exceptional %d parent %p\n" , - node, node->offset, +pr_debug ("radix node: %p offset %d indices %lu-%lu parent %p tags %lx %lx %lx shift %d count %d exceptional %d\n" , + node, node->offset, index, index |node_maxindex (node), + node->parent, node->tags[0 ][0 ], node->tags[1 ][0 ], node->tags[2 ][0 ], - node->shift, node->count, node->exceptional, node->parent); + node->shift, node->count, node->exceptional);for (i =0 ; i <RADIX_TREE_MAP_SIZE ; i++) {unsigned long first = index | (i << node->shift);
@@ -229,14 +243,16 @@ static void dump_node(struct radix_tree_node *node, unsigned long index)void *entry = node->slots[i];if (!entry)continue ; -if (is_sibling_entry (node, entry)) { -pr_debug ("radix sblng %p offset %ld val %p indices %ld-%ld\n" , - entry, i, - *(void **)entry_to_node (entry), - first, last); +if (entry ==RADIX_TREE_RETRY ) { +pr_debug ("radix retry offset %ld indices %lu-%lu parent %p\n" , + i, first, last, node); }else if (!radix_tree_is_internal_node (entry)) { -pr_debug ("radix entry %p offset %ld indices %ld-%ld\n" , - entry, i, first, last); +pr_debug ("radix entry %p offset %ld indices %lu-%lu parent %p\n" , + entry, i, first, last, node); + }else if (is_sibling_entry (node, entry)) { +pr_debug ("radix sblng %p offset %ld indices %lu-%lu parent %p val %p\n" , + entry, i, first, last, node, + *(void **)entry_to_node (entry)); }else {dump_node (entry_to_node (entry), first); }
@@ -454,19 +470,6 @@ int radix_tree_maybe_preload_order(gfp_t gfp_mask, int order)return __radix_tree_preload (gfp_mask, nr_nodes); } -/* -* The maximum index which can be stored in a radix tree -*/ -static inline unsigned long shift_maxindex (unsigned int shift) -{ -return (RADIX_TREE_MAP_SIZE << shift) -1 ; -} - -static inline unsigned long node_maxindex (struct radix_tree_node *node) -{ -return shift_maxindex (node->shift); -} -static unsigned radix_tree_load_root (struct radix_tree_root *root,struct radix_tree_node **nodep,unsigned long *maxindex) {
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 19/33 btrfs: Fix race in btrfs_free_dummy_fs_info()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
We drop the lock which protects the radix tree, so we must call radix_tree_iter_next() in order to avoid a modification to the tree invalidating the iterator state.
--- fs/btrfs/tests/btrfs-tests.c | 1 + 1 file changed, 1 insertion(+)
fs/btrfs/tests/btrfs-tests.c +1 -0
@@ -162,6 +162,7 @@ void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info) slot =radix_tree_iter_retry (&iter);continue ; } + slot =radix_tree_iter_next (&iter);spin_unlock (&fs_info->buffer_lock);free_extent_buffer_stale (eb);spin_lock (&fs_info->buffer_lock);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 19/33 btrfs: Fix race in btrfs_free_dummy_fs_info()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
We drop the lock which protects the radix tree, so we must call radix_tree_iter_next() in order to avoid a modification to the tree invalidating the iterator state.
--- fs/btrfs/tests/btrfs-tests.c | 1 + 1 file changed, 1 insertion(+)
fs/btrfs/tests/btrfs-tests.c +1 -0
@@ -162,6 +162,7 @@ void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info) slot =radix_tree_iter_retry (&iter);continue ; } + slot =radix_tree_iter_next (&iter);spin_unlock (&fs_info->buffer_lock);free_extent_buffer_stale (eb);spin_lock (&fs_info->buffer_lock);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 20/33 radix-tree: Improve multiorder iterators
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This fixes several interlinked problems with the iterators in the presence of multiorder entries.
1. radix_tree_iter_next() would only advance by one slot, which would result in the iterators returning the same entry more than once if there were sibling entries.
2. radix_tree_next_slot() could return an internal pointer instead of a user pointer if a tagged multiorder entry was immediately followed by an entry of lower order.
3. radix_tree_next_slot() expanded to a lot more code than it used to when multiorder support was compiled in. And I wasn't comfortable with entry_to_node() being in a header file.
Fixing radix_tree_iter_next() for the presence of sibling entries necessarily involves examining the contents of the radix tree, so we now need to pass 'slot' to radix_tree_iter_next(), and we need to change the calling convention so it is called *before* dropping the lock which protects the tree. Also rename it to radix_tree_iter_resume(), as some people thought it was necessary to call radix_tree_iter_next() each time around the loop.
radix_tree_next_slot() becomes closer to how it looked before multiorder support was introduced. It only checks to see if the next entry in the chunk is a sibling entry or a pointer to a node; this should be rare enough that handling this case out of line is not a performance impact (and such impact is amortised by the fact that the entry we just processed was a multiorder entry). Also, radix_tree_next_slot() used to force a new chunk lookup for untagged entries, which is more expensive than the out of line sibling entry skipping.
--- fs/btrfs/tests/btrfs-tests.c | 2 +- include/linux/radix-tree.h | 71 +++++++-------- lib/radix-tree.c | 138 +++++++++++++++++++++++++---- mm/khugepaged.c | 7 +- mm/shmem.c | 6 +- tools/testing/radix-tree/iteration_check.c | 12 +-- tools/testing/radix-tree/multiorder.c | 28 ++++-- tools/testing/radix-tree/regression3.c | 8 +- tools/testing/radix-tree/test.h | 1 + 9 files changed, 188 insertions(+), 85 deletions(-)
fs/btrfs/tests/btrfs-tests.c +1 -1
@@ -162,7 +162,7 @@ void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info) slot =radix_tree_iter_retry (&iter);continue ; } - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter);spin_unlock (&fs_info->buffer_lock);free_extent_buffer_stale (eb);spin_lock (&fs_info->buffer_lock);
include/linux/radix-tree.h +30 -41
130 lines not on this page · raw message
lib/radix-tree.c +121 -17
156 lines not on this page · raw message
mm/khugepaged.c +3 -4
@@ -1446,7 +1446,7 @@ static void collapse_shmem(struct mm_struct *mm,radix_tree_replace_slot (&mapping->page_tree, slot, new_page + (index %HPAGE_PMD_NR )); - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter); index++;continue ; out_lru:
@@ -1546,7 +1546,6 @@ static void collapse_shmem(struct mm_struct *mm,/* Put holes back where they were */ radix_tree_delete (&mapping->page_tree, iter.index); - slot =radix_tree_iter_next (&iter);continue ; }
@@ -1557,11 +1556,11 @@ static void collapse_shmem(struct mm_struct *mm,page_ref_unfreeze (page,2 );radix_tree_replace_slot (&mapping->page_tree, slot, page); + slot =radix_tree_iter_resume (slot, &iter);spin_unlock_irq (&mapping->tree_lock);putback_lru_page (page);unlock_page (page);spin_lock_irq (&mapping->tree_lock); - slot =radix_tree_iter_next (&iter); }VM_BUG_ON (nr_none);spin_unlock_irq (&mapping->tree_lock);
@@ -1641,8 +1640,8 @@ static void khugepaged_scan_shmem(struct mm_struct *mm, present++;if (need_resched ()) { + slot =radix_tree_iter_resume (slot, &iter);cond_resched_rcu (); - slot =radix_tree_iter_next (&iter); } }rcu_read_unlock ();
mm/shmem.c +3 -3
@@ -661,8 +661,8 @@ unsigned long shmem_partial_swap_usage(struct address_space *mapping, swapped++;if (need_resched ()) { + slot =radix_tree_iter_resume (slot, &iter);cond_resched_rcu (); - slot =radix_tree_iter_next (&iter); } }
@@ -2435,8 +2435,8 @@ static void shmem_tag_pins(struct address_space *mapping) }if (need_resched ()) { + slot =radix_tree_iter_resume (slot, &iter);cond_resched_rcu (); - slot =radix_tree_iter_next (&iter); } }rcu_read_unlock ();
@@ -2505,8 +2505,8 @@ static int shmem_wait_for_pins(struct address_space *mapping)spin_unlock_irq (&mapping->tree_lock); continue_resched:if (need_resched ()) { + slot =radix_tree_iter_resume (slot, &iter);cond_resched_rcu (); - slot =radix_tree_iter_next (&iter); } }rcu_read_unlock ();
tools/testing/radix-tree/iteration_check.c +6 -6
@@ -48,8 +48,8 @@ static void *add_entries_fn(void *arg)/* * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find * things that have been removed and randomly resetting our iteration to the -* next chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and -* radix_tree_iter_next() cause radix_tree_next_slot() to be called with a +* next chunk with radix_tree_iter_resume(). Both radix_tree_iter_retry() and +* radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a * NULL 'slot' variable. */ static void *tagged_iteration_fn (void *arg)
@@ -79,7 +79,7 @@ static void *tagged_iteration_fn(void *arg) }if (rand_r (&seeds[0 ]) %50 ==0 ) { - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter);rcu_read_unlock ();rcu_barrier ();rcu_read_lock ();
@@ -96,8 +96,8 @@ static void *tagged_iteration_fn(void *arg)/* * Iterate over the entries, doing a radix_tree_iter_retry() as we find things * that have been removed and randomly resetting our iteration to the next -* chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and -* radix_tree_iter_next() cause radix_tree_next_slot() to be called with a +* chunk with radix_tree_iter_resume(). Both radix_tree_iter_retry() and +* radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a * NULL 'slot' variable. */ static void *untagged_iteration_fn (void *arg)
@@ -127,7 +127,7 @@ static void *untagged_iteration_fn(void *arg) }if (rand_r (&seeds[1 ]) %50 ==0 ) { - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter);rcu_read_unlock ();rcu_barrier ();rcu_read_lock ();
tools/testing/radix-tree/multiorder.c +19 -9
@@ -231,11 +231,14 @@ void multiorder_iteration(void)radix_tree_for_each_slot (slot, &tree, &iter, j) {int height = order[i] /RADIX_TREE_MAP_SHIFT ;int shift = height *RADIX_TREE_MAP_SHIFT ; -int mask = (1 << order[i]) -1 ; +unsigned long mask = (1UL << order[i]) -1 ; +struct item *item = *slot; -assert (iter.index >= (index[i] &~ mask)); -assert (iter.index <= (index[i] | mask)); +assert ((iter.index | mask) == (index[i] | mask));assert (iter.shift == shift); +assert (!radix_tree_is_internal_node (item)); +assert ((item->index | mask) == (index[i] | mask)); +assert (item->order == order[i]); i++; } }
@@ -269,7 +272,7 @@ void multiorder_tagged_iteration(void)assert (radix_tree_tag_set (&tree, tag_index[i],1 ));for (j =0 ; j <256 ; j++) { -int mask, k; +int k;for (i =0 ; i <TAG_ENTRIES ; i++) {for (k = i; index[k] < tag_index[i]; k++)
@@ -279,12 +282,16 @@ void multiorder_tagged_iteration(void) }radix_tree_for_each_tagged (slot, &tree, &iter, j,1 ) { +unsigned long mask; +struct item *item = *slot;for (k = i; index[k] < tag_index[i]; k++) ; - mask = (1 << order[k]) -1 ; + mask = (1UL << order[k]) -1 ; -assert (iter.index >= (tag_index[i] &~ mask)); -assert (iter.index <= (tag_index[i] | mask)); +assert ((iter.index | mask) == (tag_index[i] | mask)); +assert (!radix_tree_is_internal_node (item)); +assert ((item->index | mask) == (tag_index[i] | mask)); +assert (item->order == order[k]); i++; } }
@@ -303,12 +310,15 @@ void multiorder_tagged_iteration(void) }radix_tree_for_each_tagged (slot, &tree, &iter, j,2 ) { +struct item *item = *slot;for (k = i; index[k] < tag_index[i]; k++) ; mask = (1 << order[k]) -1 ; -assert (iter.index >= (tag_index[i] &~ mask)); -assert (iter.index <= (tag_index[i] | mask)); +assert ((iter.index | mask) == (tag_index[i] | mask)); +assert (!radix_tree_is_internal_node (item)); +assert ((item->index | mask) == (tag_index[i] | mask)); +assert (item->order == order[k]); i++; } }
tools/testing/radix-tree/regression3.c +4 -4
@@ -5,7 +5,7 @@* In following radix_tree_next_slot current chunk size becomes zero. * This isn't checked and it tries to dereference null pointer in slot. * -* Helper radix_tree_iter_next reset slot to NULL and next_index to index + 1, +* Helper radix_tree_iter_resume reset slot to NULL and next_index to index + 1, * for tagger iteraction it also must reset cached tags in iterator to abort * next radix_tree_next_slot and go to slow-path into radix_tree_next_chunk. *
@@ -88,7 +88,7 @@ void regression3_test(void)printf ("slot %ld %p\n" , iter.index, *slot);if (!iter.index) {printf ("next at %ld\n" , iter.index); - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter); } }
@@ -96,7 +96,7 @@ void regression3_test(void)printf ("contig %ld %p\n" , iter.index, *slot);if (!iter.index) {printf ("next at %ld\n" , iter.index); - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter); } }
@@ -106,7 +106,7 @@ void regression3_test(void)printf ("tagged %ld %p\n" , iter.index, *slot);if (!iter.index) {printf ("next at %ld\n" , iter.index); - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter); } }
tools/testing/radix-tree/test.h +1 -0
@@ -41,6 +41,7 @@ void verify_tag_consistency(struct radix_tree_root *root, unsigned int tag);extern int nr_allocated;/* Normally private parts of lib/radix-tree.c */ +struct radix_tree_node *entry_to_node (void *ptr);void radix_tree_dump (struct radix_tree_root *root);int root_tag_get (struct radix_tree_root *root,unsigned int tag);unsigned long node_maxindex (struct radix_tree_node *);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 20/33 radix-tree: Improve multiorder iterators
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This fixes several interlinked problems with the iterators in the presence of multiorder entries.
1. radix_tree_iter_next() would only advance by one slot, which would result in the iterators returning the same entry more than once if there were sibling entries.
2. radix_tree_next_slot() could return an internal pointer instead of a user pointer if a tagged multiorder entry was immediately followed by an entry of lower order.
3. radix_tree_next_slot() expanded to a lot more code than it used to when multiorder support was compiled in. And I wasn't comfortable with entry_to_node() being in a header file.
Fixing radix_tree_iter_next() for the presence of sibling entries necessarily involves examining the contents of the radix tree, so we now need to pass 'slot' to radix_tree_iter_next(), and we need to change the calling convention so it is called *before* dropping the lock which protects the tree. Also rename it to radix_tree_iter_resume(), as some people thought it was necessary to call radix_tree_iter_next() each time around the loop.
radix_tree_next_slot() becomes closer to how it looked before multiorder support was introduced. It only checks to see if the next entry in the chunk is a sibling entry or a pointer to a node; this should be rare enough that handling this case out of line is not a performance impact (and such impact is amortised by the fact that the entry we just processed was a multiorder entry). Also, radix_tree_next_slot() used to force a new chunk lookup for untagged entries, which is more expensive than the out of line sibling entry skipping.
--- fs/btrfs/tests/btrfs-tests.c | 2 +- include/linux/radix-tree.h | 71 +++++++-------- lib/radix-tree.c | 138 +++++++++++++++++++++++++---- mm/khugepaged.c | 7 +- mm/shmem.c | 6 +- tools/testing/radix-tree/iteration_check.c | 12 +-- tools/testing/radix-tree/multiorder.c | 28 ++++-- tools/testing/radix-tree/regression3.c | 8 +- tools/testing/radix-tree/test.h | 1 + 9 files changed, 188 insertions(+), 85 deletions(-)
fs/btrfs/tests/btrfs-tests.c +1 -1
@@ -162,7 +162,7 @@ void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info) slot =radix_tree_iter_retry (&iter);continue ; } - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter);spin_unlock (&fs_info->buffer_lock);free_extent_buffer_stale (eb);spin_lock (&fs_info->buffer_lock);
include/linux/radix-tree.h +30 -41
130 lines not on this page · raw message
lib/radix-tree.c +121 -17
156 lines not on this page · raw message
mm/khugepaged.c +3 -4
@@ -1446,7 +1446,7 @@ static void collapse_shmem(struct mm_struct *mm,radix_tree_replace_slot (&mapping->page_tree, slot, new_page + (index %HPAGE_PMD_NR )); - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter); index++;continue ; out_lru:
@@ -1546,7 +1546,6 @@ static void collapse_shmem(struct mm_struct *mm,/* Put holes back where they were */ radix_tree_delete (&mapping->page_tree, iter.index); - slot =radix_tree_iter_next (&iter);continue ; }
@@ -1557,11 +1556,11 @@ static void collapse_shmem(struct mm_struct *mm,page_ref_unfreeze (page,2 );radix_tree_replace_slot (&mapping->page_tree, slot, page); + slot =radix_tree_iter_resume (slot, &iter);spin_unlock_irq (&mapping->tree_lock);putback_lru_page (page);unlock_page (page);spin_lock_irq (&mapping->tree_lock); - slot =radix_tree_iter_next (&iter); }VM_BUG_ON (nr_none);spin_unlock_irq (&mapping->tree_lock);
@@ -1641,8 +1640,8 @@ static void khugepaged_scan_shmem(struct mm_struct *mm, present++;if (need_resched ()) { + slot =radix_tree_iter_resume (slot, &iter);cond_resched_rcu (); - slot =radix_tree_iter_next (&iter); } }rcu_read_unlock ();
mm/shmem.c +3 -3
@@ -661,8 +661,8 @@ unsigned long shmem_partial_swap_usage(struct address_space *mapping, swapped++;if (need_resched ()) { + slot =radix_tree_iter_resume (slot, &iter);cond_resched_rcu (); - slot =radix_tree_iter_next (&iter); } }
@@ -2435,8 +2435,8 @@ static void shmem_tag_pins(struct address_space *mapping) }if (need_resched ()) { + slot =radix_tree_iter_resume (slot, &iter);cond_resched_rcu (); - slot =radix_tree_iter_next (&iter); } }rcu_read_unlock ();
@@ -2505,8 +2505,8 @@ static int shmem_wait_for_pins(struct address_space *mapping)spin_unlock_irq (&mapping->tree_lock); continue_resched:if (need_resched ()) { + slot =radix_tree_iter_resume (slot, &iter);cond_resched_rcu (); - slot =radix_tree_iter_next (&iter); } }rcu_read_unlock ();
tools/testing/radix-tree/iteration_check.c +6 -6
@@ -48,8 +48,8 @@ static void *add_entries_fn(void *arg)/* * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find * things that have been removed and randomly resetting our iteration to the -* next chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and -* radix_tree_iter_next() cause radix_tree_next_slot() to be called with a +* next chunk with radix_tree_iter_resume(). Both radix_tree_iter_retry() and +* radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a * NULL 'slot' variable. */ static void *tagged_iteration_fn (void *arg)
@@ -79,7 +79,7 @@ static void *tagged_iteration_fn(void *arg) }if (rand_r (&seeds[0 ]) %50 ==0 ) { - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter);rcu_read_unlock ();rcu_barrier ();rcu_read_lock ();
@@ -96,8 +96,8 @@ static void *tagged_iteration_fn(void *arg)/* * Iterate over the entries, doing a radix_tree_iter_retry() as we find things * that have been removed and randomly resetting our iteration to the next -* chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and -* radix_tree_iter_next() cause radix_tree_next_slot() to be called with a +* chunk with radix_tree_iter_resume(). Both radix_tree_iter_retry() and +* radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a * NULL 'slot' variable. */ static void *untagged_iteration_fn (void *arg)
@@ -127,7 +127,7 @@ static void *untagged_iteration_fn(void *arg) }if (rand_r (&seeds[1 ]) %50 ==0 ) { - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter);rcu_read_unlock ();rcu_barrier ();rcu_read_lock ();
tools/testing/radix-tree/multiorder.c +19 -9
@@ -231,11 +231,14 @@ void multiorder_iteration(void)radix_tree_for_each_slot (slot, &tree, &iter, j) {int height = order[i] /RADIX_TREE_MAP_SHIFT ;int shift = height *RADIX_TREE_MAP_SHIFT ; -int mask = (1 << order[i]) -1 ; +unsigned long mask = (1UL << order[i]) -1 ; +struct item *item = *slot; -assert (iter.index >= (index[i] &~ mask)); -assert (iter.index <= (index[i] | mask)); +assert ((iter.index | mask) == (index[i] | mask));assert (iter.shift == shift); +assert (!radix_tree_is_internal_node (item)); +assert ((item->index | mask) == (index[i] | mask)); +assert (item->order == order[i]); i++; } }
@@ -269,7 +272,7 @@ void multiorder_tagged_iteration(void)assert (radix_tree_tag_set (&tree, tag_index[i],1 ));for (j =0 ; j <256 ; j++) { -int mask, k; +int k;for (i =0 ; i <TAG_ENTRIES ; i++) {for (k = i; index[k] < tag_index[i]; k++)
@@ -279,12 +282,16 @@ void multiorder_tagged_iteration(void) }radix_tree_for_each_tagged (slot, &tree, &iter, j,1 ) { +unsigned long mask; +struct item *item = *slot;for (k = i; index[k] < tag_index[i]; k++) ; - mask = (1 << order[k]) -1 ; + mask = (1UL << order[k]) -1 ; -assert (iter.index >= (tag_index[i] &~ mask)); -assert (iter.index <= (tag_index[i] | mask)); +assert ((iter.index | mask) == (tag_index[i] | mask)); +assert (!radix_tree_is_internal_node (item)); +assert ((item->index | mask) == (tag_index[i] | mask)); +assert (item->order == order[k]); i++; } }
@@ -303,12 +310,15 @@ void multiorder_tagged_iteration(void) }radix_tree_for_each_tagged (slot, &tree, &iter, j,2 ) { +struct item *item = *slot;for (k = i; index[k] < tag_index[i]; k++) ; mask = (1 << order[k]) -1 ; -assert (iter.index >= (tag_index[i] &~ mask)); -assert (iter.index <= (tag_index[i] | mask)); +assert ((iter.index | mask) == (tag_index[i] | mask)); +assert (!radix_tree_is_internal_node (item)); +assert ((item->index | mask) == (tag_index[i] | mask)); +assert (item->order == order[k]); i++; } }
tools/testing/radix-tree/regression3.c +4 -4
@@ -5,7 +5,7 @@* In following radix_tree_next_slot current chunk size becomes zero. * This isn't checked and it tries to dereference null pointer in slot. * -* Helper radix_tree_iter_next reset slot to NULL and next_index to index + 1, +* Helper radix_tree_iter_resume reset slot to NULL and next_index to index + 1, * for tagger iteraction it also must reset cached tags in iterator to abort * next radix_tree_next_slot and go to slow-path into radix_tree_next_chunk. *
@@ -88,7 +88,7 @@ void regression3_test(void)printf ("slot %ld %p\n" , iter.index, *slot);if (!iter.index) {printf ("next at %ld\n" , iter.index); - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter); } }
@@ -96,7 +96,7 @@ void regression3_test(void)printf ("contig %ld %p\n" , iter.index, *slot);if (!iter.index) {printf ("next at %ld\n" , iter.index); - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter); } }
@@ -106,7 +106,7 @@ void regression3_test(void)printf ("tagged %ld %p\n" , iter.index, *slot);if (!iter.index) {printf ("next at %ld\n" , iter.index); - slot =radix_tree_iter_next (&iter); + slot =radix_tree_iter_resume (slot, &iter); } }
tools/testing/radix-tree/test.h +1 -0
@@ -41,6 +41,7 @@ void verify_tag_consistency(struct radix_tree_root *root, unsigned int tag);extern int nr_allocated;/* Normally private parts of lib/radix-tree.c */ +struct radix_tree_node *entry_to_node (void *ptr);void radix_tree_dump (struct radix_tree_root *root);int root_tag_get (struct radix_tree_root *root,unsigned int tag);unsigned long node_maxindex (struct radix_tree_node *);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 21/33 radix-tree: Delete radix_tree_locate_item()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This rather complicated function can be better implemented as an iterator. It has only one caller, so move the functionality to the only place that needs it. Update the test suite to follow the same pattern.
--- include/linux/radix-tree.h | 1 - lib/radix-tree.c | 99 ----------------------------------------- mm/shmem.c | 26 ++++++++++- tools/testing/radix-tree/main.c | 8 ++-- tools/testing/radix-tree/test.c | 22 +++++++++ tools/testing/radix-tree/test.h | 2 + 6 files changed, 53 insertions(+), 105 deletions(-)
include/linux/radix-tree.h +0 -1
@@ -296,7 +296,6 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,unsigned long nr_to_tag, unsigned int fromtag, unsigned int totag);int radix_tree_tagged (struct radix_tree_root *root,unsigned int tag); -unsigned long radix_tree_locate_item (struct radix_tree_root *root,void *item);static inline void radix_tree_preload_end (void ) {
lib/radix-tree.c +0 -99
105 lines not on this page · raw message
mm/shmem.c +25 -1
@@ -1049,6 +1049,30 @@ static void shmem_evict_inode(struct inode *inode)clear_inode (inode); } +static unsigned long find_swap_entry (struct radix_tree_root *root,void *item) +{ +struct radix_tree_iter iter; +void **slot; +unsigned long found =-1 ; +unsigned int checked =0 ; + +rcu_read_lock (); +radix_tree_for_each_slot (slot, root, &iter,0 ) { +if (*slot == item) { + found = iter.index; +break ; + } + checked++; +if ((checked %4096 ) !=0 ) +continue ; + slot =radix_tree_iter_resume (slot, &iter); +cond_resched_rcu (); + } + +rcu_read_unlock (); +return found; +} +/* * If swap found in inode, free it and move page from swapcache to filecache. */
@@ -1062,7 +1086,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info,int error =0 ; radswap =swp_to_radix_entry (swap); - index =radix_tree_locate_item (&mapping->page_tree, radswap); + index =find_swap_entry (&mapping->page_tree, radswap);if (index ==-1 )return -EAGAIN ;/* tell shmem_unuse we found nothing */
tools/testing/radix-tree/main.c +4 -4
@@ -239,7 +239,7 @@ static void __locate_check(struct radix_tree_root *tree, unsigned long index,item_insert_order (tree, index, order); item =item_lookup (tree, index); - index2 =radix_tree_locate_item (tree, item); + index2 =find_item (tree, item);if (index != index2) {printf ("index %ld order %d inserted; found %ld\n" , index, order, index2);
@@ -273,17 +273,17 @@ static void locate_check(void) index += (1UL << order)) {__locate_check (&tree, index + offset, order); } -if (radix_tree_locate_item (&tree, &tree) !=-1 ) +if (find_item (&tree, &tree) !=-1 )abort ();item_kill_tree (&tree); } } -if (radix_tree_locate_item (&tree, &tree) !=-1 ) +if (find_item (&tree, &tree) !=-1 )abort ();__locate_check (&tree,-1 ,0 ); -if (radix_tree_locate_item (&tree, &tree) !=-1 ) +if (find_item (&tree, &tree) !=-1 )abort ();item_kill_tree (&tree); }
tools/testing/radix-tree/test.c +22 -0
@@ -151,6 +151,28 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start,assert (nfound ==0 ); } +/* Use the same pattern as find_swap_entry() in mm/shmem.c */ +unsigned long find_item (struct radix_tree_root *root,void *item) +{ +struct radix_tree_iter iter; +void **slot; +unsigned long found =-1 ; +unsigned long checked =0 ; + +radix_tree_for_each_slot (slot, root, &iter,0 ) { +if (*slot == item) { + found = iter.index; +break ; + } + checked++; +if ((checked %4 ) !=0 ) +continue ; + slot =radix_tree_iter_resume (slot, &iter); + } + +return found; +} +static int verify_node (struct radix_tree_node *slot,unsigned int tag,int tagged) {
tools/testing/radix-tree/test.h +2 -0
@@ -25,6 +25,8 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start, unsigned long nr, int chunk);void item_kill_tree (struct radix_tree_root *root); +unsigned long find_item (struct radix_tree_root *,void *item); +void tag_check (void );void multiorder_checks (void );void iteration_test (void );
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 21/33 radix-tree: Delete radix_tree_locate_item()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This rather complicated function can be better implemented as an iterator. It has only one caller, so move the functionality to the only place that needs it. Update the test suite to follow the same pattern.
--- include/linux/radix-tree.h | 1 - lib/radix-tree.c | 99 ----------------------------------------- mm/shmem.c | 26 ++++++++++- tools/testing/radix-tree/main.c | 8 ++-- tools/testing/radix-tree/test.c | 22 +++++++++ tools/testing/radix-tree/test.h | 2 + 6 files changed, 53 insertions(+), 105 deletions(-)
include/linux/radix-tree.h +0 -1
@@ -296,7 +296,6 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,unsigned long nr_to_tag, unsigned int fromtag, unsigned int totag);int radix_tree_tagged (struct radix_tree_root *root,unsigned int tag); -unsigned long radix_tree_locate_item (struct radix_tree_root *root,void *item);static inline void radix_tree_preload_end (void ) {
lib/radix-tree.c +0 -99
105 lines not on this page · raw message
mm/shmem.c +25 -1
@@ -1049,6 +1049,30 @@ static void shmem_evict_inode(struct inode *inode)clear_inode (inode); } +static unsigned long find_swap_entry (struct radix_tree_root *root,void *item) +{ +struct radix_tree_iter iter; +void **slot; +unsigned long found =-1 ; +unsigned int checked =0 ; + +rcu_read_lock (); +radix_tree_for_each_slot (slot, root, &iter,0 ) { +if (*slot == item) { + found = iter.index; +break ; + } + checked++; +if ((checked %4096 ) !=0 ) +continue ; + slot =radix_tree_iter_resume (slot, &iter); +cond_resched_rcu (); + } + +rcu_read_unlock (); +return found; +} +/* * If swap found in inode, free it and move page from swapcache to filecache. */
@@ -1062,7 +1086,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info,int error =0 ; radswap =swp_to_radix_entry (swap); - index =radix_tree_locate_item (&mapping->page_tree, radswap); + index =find_swap_entry (&mapping->page_tree, radswap);if (index ==-1 )return -EAGAIN ;/* tell shmem_unuse we found nothing */
tools/testing/radix-tree/main.c +4 -4
@@ -239,7 +239,7 @@ static void __locate_check(struct radix_tree_root *tree, unsigned long index,item_insert_order (tree, index, order); item =item_lookup (tree, index); - index2 =radix_tree_locate_item (tree, item); + index2 =find_item (tree, item);if (index != index2) {printf ("index %ld order %d inserted; found %ld\n" , index, order, index2);
@@ -273,17 +273,17 @@ static void locate_check(void) index += (1UL << order)) {__locate_check (&tree, index + offset, order); } -if (radix_tree_locate_item (&tree, &tree) !=-1 ) +if (find_item (&tree, &tree) !=-1 )abort ();item_kill_tree (&tree); } } -if (radix_tree_locate_item (&tree, &tree) !=-1 ) +if (find_item (&tree, &tree) !=-1 )abort ();__locate_check (&tree,-1 ,0 ); -if (radix_tree_locate_item (&tree, &tree) !=-1 ) +if (find_item (&tree, &tree) !=-1 )abort ();item_kill_tree (&tree); }
tools/testing/radix-tree/test.c +22 -0
@@ -151,6 +151,28 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start,assert (nfound ==0 ); } +/* Use the same pattern as find_swap_entry() in mm/shmem.c */ +unsigned long find_item (struct radix_tree_root *root,void *item) +{ +struct radix_tree_iter iter; +void **slot; +unsigned long found =-1 ; +unsigned long checked =0 ; + +radix_tree_for_each_slot (slot, root, &iter,0 ) { +if (*slot == item) { + found = iter.index; +break ; + } + checked++; +if ((checked %4 ) !=0 ) +continue ; + slot =radix_tree_iter_resume (slot, &iter); + } + +return found; +} +static int verify_node (struct radix_tree_node *slot,unsigned int tag,int tagged) {
tools/testing/radix-tree/test.h +2 -0
@@ -25,6 +25,8 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start, unsigned long nr, int chunk);void item_kill_tree (struct radix_tree_root *root); +unsigned long find_item (struct radix_tree_root *,void *item); +void tag_check (void );void multiorder_checks (void );void iteration_test (void );
Signature · 1 line
2.10.2
PATCH v3 22/33 radix-tree: Delete radix_tree_range_tag_if_tagged()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This is an exceptionally complicated function with just one caller (tag_pages_for_writeback). We devote a large portion of the runtime of the test suite to testing this one function which has one caller. By introducing the new function radix_tree_iter_tag_set(), we can eliminate all of the complexity while keeping the performance. The caller can now use a fairly standard radix_tree_for_each() loop, and it doesn't need to worry about tricksy things like 'start' wrapping.
The test suite continues to spend a large amount of time investigating this function, but now it's testing the underlying primitives such as radix_tree_iter_resume() and the radix_tree_for_each_tagged() iterator which are also used by other parts of the kernel.
--- include/linux/radix-tree.h | 74 ++++++++++----------- lib/radix-tree.c | 117 ++++++--------------------------- mm/page-writeback.c | 28 +++++--- tools/testing/radix-tree/main.c | 12 ++-- tools/testing/radix-tree/multiorder.c | 13 ++-- tools/testing/radix-tree/regression2.c | 3 +- tools/testing/radix-tree/tag_check.c | 4 +- tools/testing/radix-tree/test.c | 34 ++++++++++ tools/testing/radix-tree/test.h | 3 + 9 files changed, 125 insertions(+), 163 deletions(-)
include/linux/radix-tree.h +37 -37
@@ -121,6 +121,41 @@ static inline bool radix_tree_empty(struct radix_tree_root *root) }/** +* struct radix_tree_iter - radix tree iterator state +* +* @index: index of current slot +* @next_index: one beyond the last index for this chunk +* @tags: bit-mask for tag-iterating +* @node: node that contains current slot +* @shift: shift for the node that holds our slots +* +* This radix tree iterator works in terms of "chunks" of slots. A chunk is a +* subinterval of slots contained within one radix tree leaf node. It is +* described by a pointer to its first slot and a struct radix_tree_iter +* which holds the chunk's position in the tree and its size. For tagged +* iteration radix_tree_iter also holds the slots' bit-mask for one chosen +* radix tree tag. +*/ +struct radix_tree_iter { +unsigned long index; +unsigned long next_index; +unsigned long tags; +struct radix_tree_node *node; +#ifdef CONFIG_RADIX_TREE_MULTIORDER +unsigned int shift; +#endif +}; + +static inline unsigned int iter_shift (const struct radix_tree_iter *iter) +{ +#ifdef CONFIG_RADIX_TREE_MULTIORDER +return iter->shift; +#else +return 0 ; +#endif +} + +/** * Radix-tree synchronization * * The radix-tree API requires that users provide all synchronisation (with
@@ -283,6 +318,8 @@ void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag);int radix_tree_tag_get (struct radix_tree_root *root,unsigned long index,unsigned int tag); +void radix_tree_iter_tag_set (struct radix_tree_root *root, +const struct radix_tree_iter *iter,unsigned int tag); unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items,
@@ -291,10 +328,6 @@ unsigned int radix_tree_gang_lookup_tag_slot(struct radix_tree_root *root, void ***results, unsigned long first_index, unsigned int max_items, unsigned int tag); -unsigned long radix_tree_range_tag_if_tagged (struct radix_tree_root *root, -unsigned long *first_indexp,unsigned long last_index, -unsigned long nr_to_tag, -unsigned int fromtag,unsigned int totag);int radix_tree_tagged (struct radix_tree_root *root,unsigned int tag);static inline void radix_tree_preload_end (void )
@@ -302,39 +335,6 @@ static inline void radix_tree_preload_end(void)preempt_enable (); } -/** -* struct radix_tree_iter - radix tree iterator state -* -* @index: index of current slot -* @next_index: one beyond the last index for this chunk -* @tags: bit-mask for tag-iterating -* @shift: shift for the node that holds our slots -* -* This radix tree iterator works in terms of "chunks" of slots. A chunk is a -* subinterval of slots contained within one radix tree leaf node. It is -* described by a pointer to its first slot and a struct radix_tree_iter -* which holds the chunk's position in the tree and its size. For tagged -* iteration radix_tree_iter also holds the slots' bit-mask for one chosen -* radix tree tag. -*/ -struct radix_tree_iter { -unsigned long index; -unsigned long next_index; -unsigned long tags; -#ifdef CONFIG_RADIX_TREE_MULTIORDER -unsigned int shift; -#endif -}; - -static inline unsigned int iter_shift (struct radix_tree_iter *iter) -{ -#ifdef CONFIG_RADIX_TREE_MULTIORDER -return iter->shift; -#else -return 0 ; -#endif -} -#define RADIX_TREE_ITER_TAG_MASK 0x00FF/* tag index in lower byte */ #define RADIX_TREE_ITER_TAGGED 0x0100/* lookup tagged slots */ #define RADIX_TREE_ITER_CONTIG 0x0200/* stop at first hole */
lib/radix-tree.c +20 -97
153 lines not on this page · raw message
mm/page-writeback.c +18 -10
@@ -2106,18 +2106,26 @@ void tag_pages_for_writeback(struct address_space *mapping, pgoff_t start, pgoff_t end) {#define WRITEBACK_TAG_BATCH 4096 -unsigned long tagged; - -do { -spin_lock_irq (&mapping->tree_lock); - tagged =radix_tree_range_tag_if_tagged (&mapping->page_tree, - &start, end,WRITEBACK_TAG_BATCH , -PAGECACHE_TAG_DIRTY ,PAGECACHE_TAG_TOWRITE ); +unsigned long tagged =0 ; +struct radix_tree_iter iter; +void **slot; + +spin_lock_irq (&mapping->tree_lock); +radix_tree_for_each_tagged (slot, &mapping->page_tree, &iter, start, +PAGECACHE_TAG_DIRTY ) { +if (iter.index > end) +break ; +radix_tree_iter_tag_set (&mapping->page_tree, &iter, +PAGECACHE_TAG_TOWRITE ); + tagged++; +if ((tagged %WRITEBACK_TAG_BATCH ) !=0 ) +continue ; + slot =radix_tree_iter_resume (slot, &iter);spin_unlock_irq (&mapping->tree_lock); -WARN_ON_ONCE (tagged >WRITEBACK_TAG_BATCH );cond_resched (); -/* We check 'start' to handle wrapping when end == ~0UL */ - }while (tagged >=WRITEBACK_TAG_BATCH && start); +spin_lock_irq (&mapping->tree_lock); + } +spin_unlock_irq (&mapping->tree_lock); }EXPORT_SYMBOL (tag_pages_for_writeback);
tools/testing/radix-tree/main.c +4 -8
@@ -205,8 +205,7 @@ void copy_tag_check(void) }// printf("\ncopying tags...\n"); - cur = start; - tagged =radix_tree_range_tag_if_tagged (&tree, &cur, end,ITEMS ,0 ,1 ); + tagged =tag_tagged_items (&tree,NULL , start, end,ITEMS ,0 ,1 );// printf("checking copied tags\n"); assert (tagged == count);
@@ -214,16 +213,13 @@ void copy_tag_check(void)/* Copy tags in several rounds */ // printf("\ncopying tags...\n"); - cur = start; -do { - tmp =rand () % (count/10 +2 ); - tagged =radix_tree_range_tag_if_tagged (&tree, &cur, end, tmp,0 ,2 ); - }while (tmp == tagged); + tmp =rand () % (count /10 +2 ); + tagged =tag_tagged_items (&tree,NULL , start, end, tmp,0 ,2 ); +assert (tagged == count);// printf("%lu %lu %lu\n", tagged, tmp, count); // printf("checking copied tags\n"); check_copied_tags (&tree, start, end, idx,ITEMS ,0 ,2 ); -assert (tagged < tmp);verify_tag_consistency (&tree,0 );verify_tag_consistency (&tree,1 );verify_tag_consistency (&tree,2 );
tools/testing/radix-tree/multiorder.c +5 -8
@@ -26,7 +26,6 @@ static void __multiorder_tag_test(int index, int order) {RADIX_TREE (tree,GFP_KERNEL );int base, err, i; -unsigned long first =0 ;/* our canonical entry */ base = index & ~((1 << order) -1 );
@@ -60,7 +59,7 @@ static void __multiorder_tag_test(int index, int order)assert (!radix_tree_tag_get (&tree, i,1 )); } -assert (radix_tree_range_tag_if_tagged (&tree, &first, ~0UL ,10 ,0 ,1 ) ==1 ); +assert (tag_tagged_items (&tree,NULL ,0 , ~0UL ,10 ,0 ,1 ) ==1 );assert (radix_tree_tag_clear (&tree, index,0 ));for_each_index (i, base, order) {
@@ -251,7 +250,6 @@ void multiorder_tagged_iteration(void)RADIX_TREE (tree,GFP_KERNEL );struct radix_tree_iter iter;void **slot; -unsigned long first =0 ;int i, j;printf ("Multiorder tagged iteration test\n" );
@@ -296,8 +294,8 @@ void multiorder_tagged_iteration(void) } } -radix_tree_range_tag_if_tagged (&tree, &first, ~0UL , -MT_NUM_ENTRIES ,1 ,2 ); +assert (tag_tagged_items (&tree,NULL ,0 , ~0UL ,TAG_ENTRIES ,1 ,2 ) == +TAG_ENTRIES );for (j =0 ; j <256 ; j++) {int mask, k;
@@ -323,9 +321,8 @@ void multiorder_tagged_iteration(void) } } - first =1 ; -radix_tree_range_tag_if_tagged (&tree, &first, ~0UL , -MT_NUM_ENTRIES ,1 ,0 ); +assert (tag_tagged_items (&tree,NULL ,1 , ~0UL ,MT_NUM_ENTRIES *2 ,1 ,0 ) + ==TAG_ENTRIES ); i =0 ;radix_tree_for_each_tagged (slot, &tree, &iter,0 ,0 ) {assert (iter.index == tag_index[i]);
tools/testing/radix-tree/regression2.c +2 -1
@@ -50,6 +50,7 @@#include <stdio.h> #include "regression.h" +#include "test.h" #define PAGECACHE_TAG_DIRTY 0#define PAGECACHE_TAG_WRITEBACK 1
@@ -90,7 +91,7 @@ void regression2_test(void)/* 1. */ start =0 ; end = max_slots -2 ; -radix_tree_range_tag_if_tagged (&mt_tree, &start, end,1 , +tag_tagged_items (&mt_tree,NULL , start, end,1 ,PAGECACHE_TAG_DIRTY ,PAGECACHE_TAG_TOWRITE );/* 2. */
tools/testing/radix-tree/tag_check.c +2 -2
@@ -23,7 +23,7 @@ __simple_checks(struct radix_tree_root *tree, unsigned long index, int tag)item_tag_set (tree, index, tag); ret =item_tag_get (tree, index, tag);assert (ret !=0 ); - ret =radix_tree_range_tag_if_tagged (tree, &first, ~0UL ,10 , tag, !tag); + ret =tag_tagged_items (tree,NULL , first, ~0UL ,10 , tag, !tag);assert (ret ==1 ); ret =item_tag_get (tree, index, !tag);assert (ret !=0 );
@@ -320,7 +320,7 @@ static void single_check(void)assert (ret ==0 );verify_tag_consistency (&tree,0 );verify_tag_consistency (&tree,1 ); - ret =radix_tree_range_tag_if_tagged (&tree, &first,10 ,10 ,0 ,1 ); + ret =tag_tagged_items (&tree,NULL , first,10 ,10 ,0 ,1 );assert (ret ==1 ); ret =radix_tree_gang_lookup_tag (&tree, (void **)items,0 ,BATCH ,1 );assert (ret ==1 );
tools/testing/radix-tree/test.c +34 -0
@@ -151,6 +151,40 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start,assert (nfound ==0 ); } +/* Use the same pattern as tag_pages_for_writeback() in mm/page-writeback.c */ +int tag_tagged_items (struct radix_tree_root *root,pthread_mutex_t *lock, +unsigned long start,unsigned long end,unsigned batch, +unsigned iftag,unsigned thentag) +{ +unsigned long tagged =0 ; +struct radix_tree_iter iter; +void **slot; + +if (batch ==0 ) + batch =1 ; + +if (lock) +pthread_mutex_lock (lock); +radix_tree_for_each_tagged (slot, root, &iter, start, iftag) { +if (iter.index > end) +break ; +radix_tree_iter_tag_set (root, &iter, thentag); + tagged++; +if ((tagged % batch) !=0 ) +continue ; + slot =radix_tree_iter_resume (slot, &iter); +if (lock) { +pthread_mutex_unlock (lock); +rcu_barrier (); +pthread_mutex_lock (lock); + } + } +if (lock) +pthread_mutex_unlock (lock); + +return tagged; +} +/* Use the same pattern as find_swap_entry() in mm/shmem.c */ unsigned long find_item (struct radix_tree_root *root,void *item) {
tools/testing/radix-tree/test.h +3 -0
@@ -25,6 +25,9 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start, unsigned long nr, int chunk);void item_kill_tree (struct radix_tree_root *root); +int tag_tagged_items (struct radix_tree_root *,pthread_mutex_t *, +unsigned long start,unsigned long end,unsigned batch, +unsigned iftag,unsigned thentag);unsigned long find_item (struct radix_tree_root *,void *item);void tag_check (void );
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 22/33 radix-tree: Delete radix_tree_range_tag_if_tagged()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This is an exceptionally complicated function with just one caller (tag_pages_for_writeback). We devote a large portion of the runtime of the test suite to testing this one function which has one caller. By introducing the new function radix_tree_iter_tag_set(), we can eliminate all of the complexity while keeping the performance. The caller can now use a fairly standard radix_tree_for_each() loop, and it doesn't need to worry about tricksy things like 'start' wrapping.
The test suite continues to spend a large amount of time investigating this function, but now it's testing the underlying primitives such as radix_tree_iter_resume() and the radix_tree_for_each_tagged() iterator which are also used by other parts of the kernel.
--- include/linux/radix-tree.h | 74 ++++++++++----------- lib/radix-tree.c | 117 ++++++--------------------------- mm/page-writeback.c | 28 +++++--- tools/testing/radix-tree/main.c | 12 ++-- tools/testing/radix-tree/multiorder.c | 13 ++-- tools/testing/radix-tree/regression2.c | 3 +- tools/testing/radix-tree/tag_check.c | 4 +- tools/testing/radix-tree/test.c | 34 ++++++++++ tools/testing/radix-tree/test.h | 3 + 9 files changed, 125 insertions(+), 163 deletions(-)
include/linux/radix-tree.h +37 -37
@@ -121,6 +121,41 @@ static inline bool radix_tree_empty(struct radix_tree_root *root) }/** +* struct radix_tree_iter - radix tree iterator state +* +* @index: index of current slot +* @next_index: one beyond the last index for this chunk +* @tags: bit-mask for tag-iterating +* @node: node that contains current slot +* @shift: shift for the node that holds our slots +* +* This radix tree iterator works in terms of "chunks" of slots. A chunk is a +* subinterval of slots contained within one radix tree leaf node. It is +* described by a pointer to its first slot and a struct radix_tree_iter +* which holds the chunk's position in the tree and its size. For tagged +* iteration radix_tree_iter also holds the slots' bit-mask for one chosen +* radix tree tag. +*/ +struct radix_tree_iter { +unsigned long index; +unsigned long next_index; +unsigned long tags; +struct radix_tree_node *node; +#ifdef CONFIG_RADIX_TREE_MULTIORDER +unsigned int shift; +#endif +}; + +static inline unsigned int iter_shift (const struct radix_tree_iter *iter) +{ +#ifdef CONFIG_RADIX_TREE_MULTIORDER +return iter->shift; +#else +return 0 ; +#endif +} + +/** * Radix-tree synchronization * * The radix-tree API requires that users provide all synchronisation (with
@@ -283,6 +318,8 @@ void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag);int radix_tree_tag_get (struct radix_tree_root *root,unsigned long index,unsigned int tag); +void radix_tree_iter_tag_set (struct radix_tree_root *root, +const struct radix_tree_iter *iter,unsigned int tag); unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items,
@@ -291,10 +328,6 @@ unsigned int radix_tree_gang_lookup_tag_slot(struct radix_tree_root *root, void ***results, unsigned long first_index, unsigned int max_items, unsigned int tag); -unsigned long radix_tree_range_tag_if_tagged (struct radix_tree_root *root, -unsigned long *first_indexp,unsigned long last_index, -unsigned long nr_to_tag, -unsigned int fromtag,unsigned int totag);int radix_tree_tagged (struct radix_tree_root *root,unsigned int tag);static inline void radix_tree_preload_end (void )
@@ -302,39 +335,6 @@ static inline void radix_tree_preload_end(void)preempt_enable (); } -/** -* struct radix_tree_iter - radix tree iterator state -* -* @index: index of current slot -* @next_index: one beyond the last index for this chunk -* @tags: bit-mask for tag-iterating -* @shift: shift for the node that holds our slots -* -* This radix tree iterator works in terms of "chunks" of slots. A chunk is a -* subinterval of slots contained within one radix tree leaf node. It is -* described by a pointer to its first slot and a struct radix_tree_iter -* which holds the chunk's position in the tree and its size. For tagged -* iteration radix_tree_iter also holds the slots' bit-mask for one chosen -* radix tree tag. -*/ -struct radix_tree_iter { -unsigned long index; -unsigned long next_index; -unsigned long tags; -#ifdef CONFIG_RADIX_TREE_MULTIORDER -unsigned int shift; -#endif -}; - -static inline unsigned int iter_shift (struct radix_tree_iter *iter) -{ -#ifdef CONFIG_RADIX_TREE_MULTIORDER -return iter->shift; -#else -return 0 ; -#endif -} -#define RADIX_TREE_ITER_TAG_MASK 0x00FF/* tag index in lower byte */ #define RADIX_TREE_ITER_TAGGED 0x0100/* lookup tagged slots */ #define RADIX_TREE_ITER_CONTIG 0x0200/* stop at first hole */
lib/radix-tree.c +20 -97
153 lines not on this page · raw message
mm/page-writeback.c +18 -10
@@ -2106,18 +2106,26 @@ void tag_pages_for_writeback(struct address_space *mapping, pgoff_t start, pgoff_t end) {#define WRITEBACK_TAG_BATCH 4096 -unsigned long tagged; - -do { -spin_lock_irq (&mapping->tree_lock); - tagged =radix_tree_range_tag_if_tagged (&mapping->page_tree, - &start, end,WRITEBACK_TAG_BATCH , -PAGECACHE_TAG_DIRTY ,PAGECACHE_TAG_TOWRITE ); +unsigned long tagged =0 ; +struct radix_tree_iter iter; +void **slot; + +spin_lock_irq (&mapping->tree_lock); +radix_tree_for_each_tagged (slot, &mapping->page_tree, &iter, start, +PAGECACHE_TAG_DIRTY ) { +if (iter.index > end) +break ; +radix_tree_iter_tag_set (&mapping->page_tree, &iter, +PAGECACHE_TAG_TOWRITE ); + tagged++; +if ((tagged %WRITEBACK_TAG_BATCH ) !=0 ) +continue ; + slot =radix_tree_iter_resume (slot, &iter);spin_unlock_irq (&mapping->tree_lock); -WARN_ON_ONCE (tagged >WRITEBACK_TAG_BATCH );cond_resched (); -/* We check 'start' to handle wrapping when end == ~0UL */ - }while (tagged >=WRITEBACK_TAG_BATCH && start); +spin_lock_irq (&mapping->tree_lock); + } +spin_unlock_irq (&mapping->tree_lock); }EXPORT_SYMBOL (tag_pages_for_writeback);
tools/testing/radix-tree/main.c +4 -8
@@ -205,8 +205,7 @@ void copy_tag_check(void) }// printf("\ncopying tags...\n"); - cur = start; - tagged =radix_tree_range_tag_if_tagged (&tree, &cur, end,ITEMS ,0 ,1 ); + tagged =tag_tagged_items (&tree,NULL , start, end,ITEMS ,0 ,1 );// printf("checking copied tags\n"); assert (tagged == count);
@@ -214,16 +213,13 @@ void copy_tag_check(void)/* Copy tags in several rounds */ // printf("\ncopying tags...\n"); - cur = start; -do { - tmp =rand () % (count/10 +2 ); - tagged =radix_tree_range_tag_if_tagged (&tree, &cur, end, tmp,0 ,2 ); - }while (tmp == tagged); + tmp =rand () % (count /10 +2 ); + tagged =tag_tagged_items (&tree,NULL , start, end, tmp,0 ,2 ); +assert (tagged == count);// printf("%lu %lu %lu\n", tagged, tmp, count); // printf("checking copied tags\n"); check_copied_tags (&tree, start, end, idx,ITEMS ,0 ,2 ); -assert (tagged < tmp);verify_tag_consistency (&tree,0 );verify_tag_consistency (&tree,1 );verify_tag_consistency (&tree,2 );
tools/testing/radix-tree/multiorder.c +5 -8
@@ -26,7 +26,6 @@ static void __multiorder_tag_test(int index, int order) {RADIX_TREE (tree,GFP_KERNEL );int base, err, i; -unsigned long first =0 ;/* our canonical entry */ base = index & ~((1 << order) -1 );
@@ -60,7 +59,7 @@ static void __multiorder_tag_test(int index, int order)assert (!radix_tree_tag_get (&tree, i,1 )); } -assert (radix_tree_range_tag_if_tagged (&tree, &first, ~0UL ,10 ,0 ,1 ) ==1 ); +assert (tag_tagged_items (&tree,NULL ,0 , ~0UL ,10 ,0 ,1 ) ==1 );assert (radix_tree_tag_clear (&tree, index,0 ));for_each_index (i, base, order) {
@@ -251,7 +250,6 @@ void multiorder_tagged_iteration(void)RADIX_TREE (tree,GFP_KERNEL );struct radix_tree_iter iter;void **slot; -unsigned long first =0 ;int i, j;printf ("Multiorder tagged iteration test\n" );
@@ -296,8 +294,8 @@ void multiorder_tagged_iteration(void) } } -radix_tree_range_tag_if_tagged (&tree, &first, ~0UL , -MT_NUM_ENTRIES ,1 ,2 ); +assert (tag_tagged_items (&tree,NULL ,0 , ~0UL ,TAG_ENTRIES ,1 ,2 ) == +TAG_ENTRIES );for (j =0 ; j <256 ; j++) {int mask, k;
@@ -323,9 +321,8 @@ void multiorder_tagged_iteration(void) } } - first =1 ; -radix_tree_range_tag_if_tagged (&tree, &first, ~0UL , -MT_NUM_ENTRIES ,1 ,0 ); +assert (tag_tagged_items (&tree,NULL ,1 , ~0UL ,MT_NUM_ENTRIES *2 ,1 ,0 ) + ==TAG_ENTRIES ); i =0 ;radix_tree_for_each_tagged (slot, &tree, &iter,0 ,0 ) {assert (iter.index == tag_index[i]);
tools/testing/radix-tree/regression2.c +2 -1
@@ -50,6 +50,7 @@#include <stdio.h> #include "regression.h" +#include "test.h" #define PAGECACHE_TAG_DIRTY 0#define PAGECACHE_TAG_WRITEBACK 1
@@ -90,7 +91,7 @@ void regression2_test(void)/* 1. */ start =0 ; end = max_slots -2 ; -radix_tree_range_tag_if_tagged (&mt_tree, &start, end,1 , +tag_tagged_items (&mt_tree,NULL , start, end,1 ,PAGECACHE_TAG_DIRTY ,PAGECACHE_TAG_TOWRITE );/* 2. */
tools/testing/radix-tree/tag_check.c +2 -2
@@ -23,7 +23,7 @@ __simple_checks(struct radix_tree_root *tree, unsigned long index, int tag)item_tag_set (tree, index, tag); ret =item_tag_get (tree, index, tag);assert (ret !=0 ); - ret =radix_tree_range_tag_if_tagged (tree, &first, ~0UL ,10 , tag, !tag); + ret =tag_tagged_items (tree,NULL , first, ~0UL ,10 , tag, !tag);assert (ret ==1 ); ret =item_tag_get (tree, index, !tag);assert (ret !=0 );
@@ -320,7 +320,7 @@ static void single_check(void)assert (ret ==0 );verify_tag_consistency (&tree,0 );verify_tag_consistency (&tree,1 ); - ret =radix_tree_range_tag_if_tagged (&tree, &first,10 ,10 ,0 ,1 ); + ret =tag_tagged_items (&tree,NULL , first,10 ,10 ,0 ,1 );assert (ret ==1 ); ret =radix_tree_gang_lookup_tag (&tree, (void **)items,0 ,BATCH ,1 );assert (ret ==1 );
tools/testing/radix-tree/test.c +34 -0
@@ -151,6 +151,40 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start,assert (nfound ==0 ); } +/* Use the same pattern as tag_pages_for_writeback() in mm/page-writeback.c */ +int tag_tagged_items (struct radix_tree_root *root,pthread_mutex_t *lock, +unsigned long start,unsigned long end,unsigned batch, +unsigned iftag,unsigned thentag) +{ +unsigned long tagged =0 ; +struct radix_tree_iter iter; +void **slot; + +if (batch ==0 ) + batch =1 ; + +if (lock) +pthread_mutex_lock (lock); +radix_tree_for_each_tagged (slot, root, &iter, start, iftag) { +if (iter.index > end) +break ; +radix_tree_iter_tag_set (root, &iter, thentag); + tagged++; +if ((tagged % batch) !=0 ) +continue ; + slot =radix_tree_iter_resume (slot, &iter); +if (lock) { +pthread_mutex_unlock (lock); +rcu_barrier (); +pthread_mutex_lock (lock); + } + } +if (lock) +pthread_mutex_unlock (lock); + +return tagged; +} +/* Use the same pattern as find_swap_entry() in mm/shmem.c */ unsigned long find_item (struct radix_tree_root *root,void *item) {
tools/testing/radix-tree/test.h +3 -0
@@ -25,6 +25,9 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start, unsigned long nr, int chunk);void item_kill_tree (struct radix_tree_root *root); +int tag_tagged_items (struct radix_tree_root *,pthread_mutex_t *, +unsigned long start,unsigned long end,unsigned batch, +unsigned iftag,unsigned thentag);unsigned long find_item (struct radix_tree_root *,void *item);void tag_check (void );
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 23/33 radix-tree: Add radix_tree_join
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This new function allows for the replacement of many smaller entries in the radix tree with one larger multiorder entry. From the point of view of an RCU walker, they may see a mixture of the smaller entries and the large entry during the same walk, but they will never see NULL for an index which was populated before the join.
--- include/linux/radix-tree.h | 3 + lib/radix-tree.c | 183 ++++++++++++++++++++++++++++------ tools/testing/radix-tree/multiorder.c | 58 +++++++++++ 3 files changed, 213 insertions(+), 31 deletions(-)
include/linux/radix-tree.h +3 -0
@@ -335,6 +335,9 @@ static inline void radix_tree_preload_end(void)preempt_enable (); } +int radix_tree_join (struct radix_tree_root *,unsigned long index, +unsigned new_order,void *); +#define RADIX_TREE_ITER_TAG_MASK 0x00FF/* tag index in lower byte */ #define RADIX_TREE_ITER_TAGGED 0x0100/* lookup tagged slots */ #define RADIX_TREE_ITER_CONTIG 0x0200/* stop at first hole */
lib/radix-tree.c +152 -31
241 lines not on this page · raw message
tools/testing/radix-tree/multiorder.c +58 -0
68 lines not on this page · raw message
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 23/33 radix-tree: Add radix_tree_join
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This new function allows for the replacement of many smaller entries in the radix tree with one larger multiorder entry. From the point of view of an RCU walker, they may see a mixture of the smaller entries and the large entry during the same walk, but they will never see NULL for an index which was populated before the join.
--- include/linux/radix-tree.h | 3 + lib/radix-tree.c | 183 ++++++++++++++++++++++++++++------ tools/testing/radix-tree/multiorder.c | 58 +++++++++++ 3 files changed, 213 insertions(+), 31 deletions(-)
include/linux/radix-tree.h +3 -0
@@ -335,6 +335,9 @@ static inline void radix_tree_preload_end(void)preempt_enable (); } +int radix_tree_join (struct radix_tree_root *,unsigned long index, +unsigned new_order,void *); +#define RADIX_TREE_ITER_TAG_MASK 0x00FF/* tag index in lower byte */ #define RADIX_TREE_ITER_TAGGED 0x0100/* lookup tagged slots */ #define RADIX_TREE_ITER_CONTIG 0x0200/* stop at first hole */
lib/radix-tree.c +152 -31
241 lines not on this page · raw message
tools/testing/radix-tree/multiorder.c +58 -0
68 lines not on this page · raw message
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 24/33 radix-tree: Add radix_tree_split
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This new function splits a larger multiorder entry into smaller entries (potentially multi-order entries). These entries are initialised to RADIX_TREE_RETRY to ensure that RCU walkers who see this state aren't confused. The caller should then call radix_tree_for_each_slot() and radix_tree_replace_slot() in order to turn these retry entries into the intended new entries. Tags are replicated from the original multiorder entry into each new entry.
--- include/linux/radix-tree.h | 12 +++ lib/radix-tree.c | 142 +++++++++++++++++++++++++++++++++- tools/testing/radix-tree/multiorder.c | 64 +++++++++++++++ 3 files changed, 214 insertions(+), 4 deletions(-)
include/linux/radix-tree.h +12 -0
@@ -80,6 +80,14 @@ static inline bool radix_tree_is_internal_node(void *ptr)#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \ RADIX_TREE_MAP_SHIFT)) +/* +* @count is the count of every non-NULL element in the ->slots array +* whether that is an exceptional entry, a retry entry, a user pointer, +* a sibling entry or a pointer to the next level of the tree. +* @exceptional is the count of every element in ->slots which is +* either radix_tree_exceptional_entry() or is a sibling entry for an +* exceptional entry. +*/ struct radix_tree_node {unsigned char shift;/* Bits remaining in each slot */ unsigned char offset;/* Slot offset in parent */
@@ -293,6 +301,8 @@ void __radix_tree_replace(struct radix_tree_root *root,struct radix_tree_node *node, void **slot, void *item, radix_tree_update_node_t update_node, void *private); +void radix_tree_iter_replace (struct radix_tree_root *, +const struct radix_tree_iter *,void **slot,void *item);void radix_tree_replace_slot (struct radix_tree_root *root,void **slot,void *item); void __radix_tree_delete_node(struct radix_tree_root *root,
@@ -335,6 +345,8 @@ static inline void radix_tree_preload_end(void)preempt_enable (); } +int radix_tree_split (struct radix_tree_root *,unsigned long index, +unsigned new_order);int radix_tree_join (struct radix_tree_root *,unsigned long index,unsigned new_order,void *);
lib/radix-tree.c +138 -4
185 lines not on this page · raw message
tools/testing/radix-tree/multiorder.c +64 -0
74 lines not on this page · raw message
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 24/33 radix-tree: Add radix_tree_split
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
This new function splits a larger multiorder entry into smaller entries (potentially multi-order entries). These entries are initialised to RADIX_TREE_RETRY to ensure that RCU walkers who see this state aren't confused. The caller should then call radix_tree_for_each_slot() and radix_tree_replace_slot() in order to turn these retry entries into the intended new entries. Tags are replicated from the original multiorder entry into each new entry.
--- include/linux/radix-tree.h | 12 +++ lib/radix-tree.c | 142 +++++++++++++++++++++++++++++++++- tools/testing/radix-tree/multiorder.c | 64 +++++++++++++++ 3 files changed, 214 insertions(+), 4 deletions(-)
include/linux/radix-tree.h +12 -0
@@ -80,6 +80,14 @@ static inline bool radix_tree_is_internal_node(void *ptr)#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \ RADIX_TREE_MAP_SHIFT)) +/* +* @count is the count of every non-NULL element in the ->slots array +* whether that is an exceptional entry, a retry entry, a user pointer, +* a sibling entry or a pointer to the next level of the tree. +* @exceptional is the count of every element in ->slots which is +* either radix_tree_exceptional_entry() or is a sibling entry for an +* exceptional entry. +*/ struct radix_tree_node {unsigned char shift;/* Bits remaining in each slot */ unsigned char offset;/* Slot offset in parent */
@@ -293,6 +301,8 @@ void __radix_tree_replace(struct radix_tree_root *root,struct radix_tree_node *node, void **slot, void *item, radix_tree_update_node_t update_node, void *private); +void radix_tree_iter_replace (struct radix_tree_root *, +const struct radix_tree_iter *,void **slot,void *item);void radix_tree_replace_slot (struct radix_tree_root *root,void **slot,void *item); void __radix_tree_delete_node(struct radix_tree_root *root,
@@ -335,6 +345,8 @@ static inline void radix_tree_preload_end(void)preempt_enable (); } +int radix_tree_split (struct radix_tree_root *,unsigned long index, +unsigned new_order);int radix_tree_join (struct radix_tree_root *,unsigned long index,unsigned new_order,void *);
lib/radix-tree.c +138 -4
185 lines not on this page · raw message
tools/testing/radix-tree/multiorder.c +64 -0
74 lines not on this page · raw message
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v3 24/33] radix-tree: Add radix_tree_split
Quoted 22 lines · Matthew Wilcox
From: Matthew Wilcox <[email protected]>
This new function splits a larger multiorder entry into smaller entries (potentially multi-order entries). These entries are initialised to
RADIX_TREE_RETRYto ensure that RCU walkers who see this state aren't confused. The caller should then callradix_tree_for_each_slot()andradix_tree_replace_slot()in order to turn these retry entries into the intended new entries. Tags are replicated from the original multiorder entry into each new entry.Signed-off-by Matthew Wilcox <[email protected]>--- include/linux/radix-tree.h | 12 +++ lib/radix-tree.c | 142 +++++++++++++++++++++++++++++++++- tools/testing/radix-tree/multiorder.c | 64 +++++++++++++++ 3 files changed, 214 insertions(+), 4 deletions(-)include/linux/radix-tree.h +0
-0
include/linux/radix-tree.h void __radix_tree_replace(struct radix_tree_root *root, · line 305
Quoted 5 lines
include/linux/radix-tree.h +2
-0@@ -293,6 +301,8 @@ void __radix_tree_replace(struct radix_tree_root *root,struct radix_tree_node *node, void **slot, void *item, radix_tree_update_node_t update_node, void *private); +void radix_tree_iter_replace (struct radix_tree_root *, +const struct radix_tree_iter *,void **slot,void *item);
above
include/linux/radix-tree.h static inline void radix_tree_preload_end(void) · line 349
Quoted 9 lines
include/linux/radix-tree.h +2
-0void radix_tree_replace_slot (struct radix_tree_root *root,void **slot,void *item); void __radix_tree_delete_node(struct radix_tree_root *root,@@ -335,6 +345,8 @@ static inline void radix_tree_preload_end(void)preempt_enable (); } +int radix_tree_split (struct radix_tree_root *,unsigned long index, +unsigned new_order);
and above:
and in patch 25/33: Add radix_tree_split_preload()
As indicated in CodingStyle:
In function prototypes, include parameter names with their data types. Although this is not required by the C language, it is preferred in Linux because it is a simple way to add valuable information for the reader.
include/linux/radix-tree.h static inline void radix_tree_preload_end(void) · line 352
Quoted 3 lines
include/linux/radix-tree.h +0
-0int radix_tree_join (struct radix_tree_root *,unsigned long index,unsigned new_order,void *);
Yes, the source file already omits some function prototype parameter names, so these patches just follow that tradition. It's weird (to me) though that the existing code even mixes this style in one function prototype (see immed. above).
Thanks.
Signature · 7 lines
~Randy -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
RE: [PATCH v3 24/33] radix-tree: Add radix_tree_split
RnJvbTogUmFuZHkgRHVubGFwIFttYWlsdG86cmR1bmxhcEBpbmZyYWRlYWQub3JnXQ0KPiA+ICt2 b2lkIHJhZGl4X3RyZWVfaXRlcl9yZXBsYWNlKHN0cnVjdCByYWRpeF90cmVlX3Jvb3QgKiwNCj4g PiArCQljb25zdCBzdHJ1Y3QgcmFkaXhfdHJlZV9pdGVyICosIHZvaWQgKipzbG90LCB2b2lkICpp dGVtKTsNCj4gDQo+ID4gK2ludCByYWRpeF90cmVlX3NwbGl0KHN0cnVjdCByYWRpeF90cmVlX3Jv b3QgKiwgdW5zaWduZWQgbG9uZyBpbmRleCwNCj4gPiArCQkJdW5zaWduZWQgbmV3X29yZGVyKTsN Cj4gDQo+IGFuZCBhYm92ZToNCj4gDQo+IEFzIGluZGljYXRlZCBpbiBDb2RpbmdTdHlsZToNCj4g SW4gZnVuY3Rpb24gcHJvdG90eXBlcywgaW5jbHVkZSBwYXJhbWV0ZXIgbmFtZXMgd2l0aCB0aGVp ciBkYXRhIHR5cGVzLg0KPiBBbHRob3VnaCB0aGlzIGlzIG5vdCByZXF1aXJlZCBieSB0aGUgQyBs YW5ndWFnZSwgaXQgaXMgcHJlZmVycmVkIGluIExpbnV4DQo+IGJlY2F1c2UgaXQgaXMgYSBzaW1w bGUgd2F5IHRvIGFkZCB2YWx1YWJsZSBpbmZvcm1hdGlvbiBmb3IgdGhlIHJlYWRlci4NCg0KSSB0 aGluayB0aGUgcnVsZSBoZXJlIHNob3VsZCBiZSBhIGJpdCBtb3JlIG51YW5jZWQuICBJIHRoaW5r IGl0IGlzIHBvc2l0aXZlbHkgY3JpbWluYWwgdG8gaGF2ZSBhbiB1bm5hbWVkICd1bnNpZ25lZCBs b25nJyBvciAnYm9vbCcgaW4gYSBmdW5jdGlvbiBwcm90b3R5cGUuICBCdXQgd2hhdCBleHRyYSBp bmZvcm1hdGlvbiBpcyBjb21tdW5pY2F0ZWQgYnkgYWRkaW5nICdyb290JyBhZnRlciAnc3RydWN0 IHJhZGl4X3RyZWVfcm9vdCAqJz8gIEkga25vdyBpdCdzIGEgcm9vdCwgeW91IHRvbGQgbWUgdGhh dCB3aXRoIHRoZSBzdHJ1Y3R1cmUgbmFtZSENCg0KT2J2aW91c2x5IHNvbWV0aW1lcyBpdCB3b3Vs ZCBiZSB1c2VmdWwsIGZvciBleGFtcGxlIGlmIHdlIGhhZCBhIGZ1bmN0aW9uIHRvIG1vdmUgYW4g ZW50cnkgZnJvbSBvbmUgcmFkaXggdHJlZSB0byBhbm90aGVyLCB5b3UgbWlnaHQgd2FudCB0byBo YXZlICdzdHJ1Y3QgcmFkaXhfdHJlZV9yb290ICpvbGQsIHN0cnVjdCByYWRpeF90cmVlX3Jvb3Qg Km5ldycgYXMgdHdvIG9mIHlvdXIgcGFyYW1ldGVycy4NCg0KPiA+ICBpbnQgcmFkaXhfdHJlZV9q b2luKHN0cnVjdCByYWRpeF90cmVlX3Jvb3QgKiwgdW5zaWduZWQgbG9uZyBpbmRleCwNCj4gPiAg CQkJdW5zaWduZWQgbmV3X29yZGVyLCB2b2lkICopOw0KPiANCj4gWWVzLCB0aGUgc291cmNlIGZp bGUgYWxyZWFkeSBvbWl0cyBzb21lIGZ1bmN0aW9uIHByb3RvdHlwZSBwYXJhbWV0ZXIgbmFtZXMs DQo+IHNvIHRoZXNlIHBhdGNoZXMganVzdCBmb2xsb3cgdGhhdCB0cmFkaXRpb24uICBJdCdzIHdl aXJkICh0byBtZSkgdGhvdWdoIHRoYXQNCj4gdGhlIGV4aXN0aW5nIGNvZGUgZXZlbiBtaXhlcyB0 aGlzIHN0eWxlIGluIG9uZSBmdW5jdGlvbiBwcm90b3R5cGUgKHNlZQ0KPiBpbW1lZC4gYWJvdmUp Lg0KDQpJIGFncmVlIHRoYXQgdm9pZCAqIHNob3VsZCBwcm9iYWJseSBiZSBuYW1lZCAoYXMgbmV3 X2VudHJ5KS4gIE5hbWluZyBpbmRleCBhbmQgbmV3X29yZGVyIGlzIGNvcnJlY3QuICBCdXQgYWdh aW4sIHRoZXJlJ3Mgbm8gdXNlZnVsIGluZm9ybWF0aW9uIGdpdmVuIGJ5IG5hbWluZyByb290Lg0K
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 25/33 radix-tree: Add radix_tree_split_preload()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Calculate how many nodes we need to allocate to split an old_order entry into multiple entries, each of size new_order. The test suite checks that we allocated exactly the right number of nodes; neither too many (checked by rtp->nr == 0), nor too few (checked by comparing nr_allocated before and after the call to radix_tree_split()).
--- include/linux/radix-tree.h | 1 + lib/radix-tree.c | 24 +++++++++++++++++++- tools/testing/radix-tree/multiorder.c | 42 +++++++++++++++++++++++++++++++++-- tools/testing/radix-tree/test.h | 5 +++++ 4 files changed, 69 insertions(+), 3 deletions(-)
include/linux/radix-tree.h +1 -0
@@ -345,6 +345,7 @@ static inline void radix_tree_preload_end(void)preempt_enable (); } +int radix_tree_split_preload (unsigned old_order,unsigned new_order,gfp_t );int radix_tree_split (struct radix_tree_root *,unsigned long index,unsigned new_order); int radix_tree_join(struct radix_tree_root *, unsigned long index,
lib/radix-tree.c +23 -1
@@ -367,7 +367,7 @@ radix_tree_node_free(struct radix_tree_node *node)* To make use of this facility, the radix tree must be initialised without * __GFP_DIRECT_RECLAIM being passed to INIT_RADIX_TREE(). */ -static int __radix_tree_preload (gfp_t gfp_mask,int nr) +static int __radix_tree_preload (gfp_t gfp_mask,unsigned nr) {struct radix_tree_preload *rtp;struct radix_tree_node *node;
@@ -433,6 +433,28 @@ int radix_tree_maybe_preload(gfp_t gfp_mask) }EXPORT_SYMBOL (radix_tree_maybe_preload); +#ifdef CONFIG_RADIX_TREE_MULTIORDER +/* +* Preload with enough objects to ensure that we can split a single entry +* of order @old_order into many entries of size @new_order +*/ +int radix_tree_split_preload (unsigned int old_order,unsigned int new_order, +gfp_t gfp_mask) +{ +unsigned top =1 << (old_order %RADIX_TREE_MAP_SHIFT ); +unsigned layers = (old_order /RADIX_TREE_MAP_SHIFT ) - + (new_order /RADIX_TREE_MAP_SHIFT ); +unsigned nr =0 ; + +WARN_ON_ONCE (!gfpflags_allow_blocking (gfp_mask)); +BUG_ON (new_order >= old_order); + +while (layers--) + nr = nr *RADIX_TREE_MAP_SIZE +1 ; +return __radix_tree_preload (gfp_mask, top * nr); +} +#endif +/* * The same as function above, but preload number of nodes required to insert * (1 << order) continuous naturally-aligned elements.
tools/testing/radix-tree/multiorder.c +40 -2
95 lines not on this page · raw message
tools/testing/radix-tree/test.h +5 -0
@@ -52,3 +52,8 @@ int root_tag_get(struct radix_tree_root *root, unsigned int tag);unsigned long node_maxindex (struct radix_tree_node *);unsigned long shift_maxindex (unsigned int shift);int radix_tree_cpu_dead (unsigned int cpu); +struct radix_tree_preload { +unsigned nr; +struct radix_tree_node *nodes; +}; +extern struct radix_tree_preload radix_tree_preloads;
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 25/33 radix-tree: Add radix_tree_split_preload()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Calculate how many nodes we need to allocate to split an old_order entry into multiple entries, each of size new_order. The test suite checks that we allocated exactly the right number of nodes; neither too many (checked by rtp->nr == 0), nor too few (checked by comparing nr_allocated before and after the call to radix_tree_split()).
--- include/linux/radix-tree.h | 1 + lib/radix-tree.c | 24 +++++++++++++++++++- tools/testing/radix-tree/multiorder.c | 42 +++++++++++++++++++++++++++++++++-- tools/testing/radix-tree/test.h | 5 +++++ 4 files changed, 69 insertions(+), 3 deletions(-)
include/linux/radix-tree.h +1 -0
@@ -345,6 +345,7 @@ static inline void radix_tree_preload_end(void)preempt_enable (); } +int radix_tree_split_preload (unsigned old_order,unsigned new_order,gfp_t );int radix_tree_split (struct radix_tree_root *,unsigned long index,unsigned new_order); int radix_tree_join(struct radix_tree_root *, unsigned long index,
lib/radix-tree.c +23 -1
@@ -367,7 +367,7 @@ radix_tree_node_free(struct radix_tree_node *node)* To make use of this facility, the radix tree must be initialised without * __GFP_DIRECT_RECLAIM being passed to INIT_RADIX_TREE(). */ -static int __radix_tree_preload (gfp_t gfp_mask,int nr) +static int __radix_tree_preload (gfp_t gfp_mask,unsigned nr) {struct radix_tree_preload *rtp;struct radix_tree_node *node;
@@ -433,6 +433,28 @@ int radix_tree_maybe_preload(gfp_t gfp_mask) }EXPORT_SYMBOL (radix_tree_maybe_preload); +#ifdef CONFIG_RADIX_TREE_MULTIORDER +/* +* Preload with enough objects to ensure that we can split a single entry +* of order @old_order into many entries of size @new_order +*/ +int radix_tree_split_preload (unsigned int old_order,unsigned int new_order, +gfp_t gfp_mask) +{ +unsigned top =1 << (old_order %RADIX_TREE_MAP_SHIFT ); +unsigned layers = (old_order /RADIX_TREE_MAP_SHIFT ) - + (new_order /RADIX_TREE_MAP_SHIFT ); +unsigned nr =0 ; + +WARN_ON_ONCE (!gfpflags_allow_blocking (gfp_mask)); +BUG_ON (new_order >= old_order); + +while (layers--) + nr = nr *RADIX_TREE_MAP_SIZE +1 ; +return __radix_tree_preload (gfp_mask, top * nr); +} +#endif +/* * The same as function above, but preload number of nodes required to insert * (1 << order) continuous naturally-aligned elements.
tools/testing/radix-tree/multiorder.c +40 -2
95 lines not on this page · raw message
tools/testing/radix-tree/test.h +5 -0
@@ -52,3 +52,8 @@ int root_tag_get(struct radix_tree_root *root, unsigned int tag);unsigned long node_maxindex (struct radix_tree_node *);unsigned long shift_maxindex (unsigned int shift);int radix_tree_cpu_dead (unsigned int cpu); +struct radix_tree_preload { +unsigned nr; +struct radix_tree_node *nodes; +}; +extern struct radix_tree_preload radix_tree_preloads;
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 26/33 radix-tree: Fix replacement for multiorder entries
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
When replacing an entry with NULL, we need to delete any sibling entries. Also account deleting exceptional entries properly.
Also fix a bug with radix_tree_iter_replace() where we would fail to remove entirely freed nodes.
Also fix accounting bug when switching between normal and exceptional entries with replace_slot.
Also add testcases for all these bugs.
--- lib/radix-tree.c | 60 +++++++++++++++++------- tools/testing/radix-tree/multiorder.c | 87 ++++++++++++++++++++++++++++++----- 2 files changed, 119 insertions(+), 28 deletions(-)
lib/radix-tree.c +44 -16
@@ -976,6 +976,24 @@ void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) }EXPORT_SYMBOL (radix_tree_lookup); +static inline int slot_count (struct radix_tree_node *node, +void **slot) +{ +int n =1 ; +#ifdef CONFIG_RADIX_TREE_MULTIORDER +void *ptr =node_to_entry (slot); +unsigned offset =get_slot_offset (node, slot); +int i; + +for (i =1 ; offset + i <RADIX_TREE_MAP_SIZE ; i++) { +if (node->slots[offset + i] != ptr) +break ; + n++; + } +#endif +return n; +} + static void replace_slot(struct radix_tree_root *root, struct radix_tree_node *node, void **slot, void *item,
@@ -994,12 +1012,35 @@ static void replace_slot(struct radix_tree_root *root,if (node) { node->count += count; - node->exceptional += exceptional; +if (exceptional) { + exceptional *=slot_count (node, slot); + node->exceptional += exceptional; + } }rcu_assign_pointer (*slot, item); } +static inline void delete_sibling_entries (struct radix_tree_node *node, +void **slot) +{ +#ifdef CONFIG_RADIX_TREE_MULTIORDER +bool exceptional =radix_tree_exceptional_entry (*slot); +void *ptr =node_to_entry (slot); +unsigned offset =get_slot_offset (node, slot); +int i; + +for (i =1 ; offset + i <RADIX_TREE_MAP_SIZE ; i++) { +if (node->slots[offset + i] != ptr) +break ; + node->slots[offset + i] =NULL ; + node->count--; +if (exceptional) + node->exceptional--; + } +#endif +} +/** * __radix_tree_replace - replace item in a slot * @root: radix tree root
@@ -1017,6 +1058,8 @@ void __radix_tree_replace(struct radix_tree_root *root, void **slot, void *item, radix_tree_update_node_t update_node, void *private) { + if (!item) + delete_sibling_entries(node, slot);/* * This function supports replacing exceptional entries and * deleting entries, but that needs accounting against the
@@ -1793,20 +1836,6 @@ void __radix_tree_delete_node(struct radix_tree_root *root,delete_node (root, node,NULL ,NULL ); } -static inline void delete_sibling_entries (struct radix_tree_node *node, -void *ptr,unsigned offset) -{ -#ifdef CONFIG_RADIX_TREE_MULTIORDER -int i; -for (i =1 ; offset + i <RADIX_TREE_MAP_SIZE ; i++) { -if (node->slots[offset + i] != ptr) -break ; - node->slots[offset + i] =NULL ; - node->count--; - } -#endif -} -/** * radix_tree_delete_item - delete an item from a radix tree * @root: radix tree root
@@ -1846,7 +1875,6 @@ void *radix_tree_delete_item(struct radix_tree_root *root,for (tag =0 ; tag <RADIX_TREE_MAX_TAGS ; tag++)node_tag_clear (root, node, tag, offset); -delete_sibling_entries (node,node_to_entry (slot), offset);__radix_tree_replace (root, node, slot,NULL ,NULL ,NULL );return entry;
tools/testing/radix-tree/multiorder.c +75 -12
147 lines not on this page · raw message
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 26/33 radix-tree: Fix replacement for multiorder entries
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
When replacing an entry with NULL, we need to delete any sibling entries. Also account deleting exceptional entries properly.
Also fix a bug with radix_tree_iter_replace() where we would fail to remove entirely freed nodes.
Also fix accounting bug when switching between normal and exceptional entries with replace_slot.
Also add testcases for all these bugs.
--- lib/radix-tree.c | 60 +++++++++++++++++------- tools/testing/radix-tree/multiorder.c | 87 ++++++++++++++++++++++++++++++----- 2 files changed, 119 insertions(+), 28 deletions(-)
lib/radix-tree.c +44 -16
@@ -976,6 +976,24 @@ void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) }EXPORT_SYMBOL (radix_tree_lookup); +static inline int slot_count (struct radix_tree_node *node, +void **slot) +{ +int n =1 ; +#ifdef CONFIG_RADIX_TREE_MULTIORDER +void *ptr =node_to_entry (slot); +unsigned offset =get_slot_offset (node, slot); +int i; + +for (i =1 ; offset + i <RADIX_TREE_MAP_SIZE ; i++) { +if (node->slots[offset + i] != ptr) +break ; + n++; + } +#endif +return n; +} + static void replace_slot(struct radix_tree_root *root, struct radix_tree_node *node, void **slot, void *item,
@@ -994,12 +1012,35 @@ static void replace_slot(struct radix_tree_root *root,if (node) { node->count += count; - node->exceptional += exceptional; +if (exceptional) { + exceptional *=slot_count (node, slot); + node->exceptional += exceptional; + } }rcu_assign_pointer (*slot, item); } +static inline void delete_sibling_entries (struct radix_tree_node *node, +void **slot) +{ +#ifdef CONFIG_RADIX_TREE_MULTIORDER +bool exceptional =radix_tree_exceptional_entry (*slot); +void *ptr =node_to_entry (slot); +unsigned offset =get_slot_offset (node, slot); +int i; + +for (i =1 ; offset + i <RADIX_TREE_MAP_SIZE ; i++) { +if (node->slots[offset + i] != ptr) +break ; + node->slots[offset + i] =NULL ; + node->count--; +if (exceptional) + node->exceptional--; + } +#endif +} +/** * __radix_tree_replace - replace item in a slot * @root: radix tree root
@@ -1017,6 +1058,8 @@ void __radix_tree_replace(struct radix_tree_root *root, void **slot, void *item, radix_tree_update_node_t update_node, void *private) { + if (!item) + delete_sibling_entries(node, slot);/* * This function supports replacing exceptional entries and * deleting entries, but that needs accounting against the
@@ -1793,20 +1836,6 @@ void __radix_tree_delete_node(struct radix_tree_root *root,delete_node (root, node,NULL ,NULL ); } -static inline void delete_sibling_entries (struct radix_tree_node *node, -void *ptr,unsigned offset) -{ -#ifdef CONFIG_RADIX_TREE_MULTIORDER -int i; -for (i =1 ; offset + i <RADIX_TREE_MAP_SIZE ; i++) { -if (node->slots[offset + i] != ptr) -break ; - node->slots[offset + i] =NULL ; - node->count--; - } -#endif -} -/** * radix_tree_delete_item - delete an item from a radix tree * @root: radix tree root
@@ -1846,7 +1875,6 @@ void *radix_tree_delete_item(struct radix_tree_root *root,for (tag =0 ; tag <RADIX_TREE_MAX_TAGS ; tag++)node_tag_clear (root, node, tag, offset); -delete_sibling_entries (node,node_to_entry (slot), offset);__radix_tree_replace (root, node, slot,NULL ,NULL ,NULL );return entry;
tools/testing/radix-tree/multiorder.c +75 -12
147 lines not on this page · raw message
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 27/33 radix tree test suite: Check multiorder iteration
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
The random iteration test only inserts order-0 entries currently. Update it to insert entries of order between 7 and 0. Also make the maximum index configurable, make some variables static, make the test duration variable, remove some useless spinning, and add a fifth thread which calls tag_tagged_items().
--- tools/testing/radix-tree/iteration_check.c | 80 ++++++++++++++++++------------ tools/testing/radix-tree/main.c | 3 +- tools/testing/radix-tree/multiorder.c | 23 +++++++++ tools/testing/radix-tree/test.h | 2 +- 4 files changed, 73 insertions(+), 35 deletions(-)
tools/testing/radix-tree/iteration_check.c +47 -33
@@ -16,26 +16,36 @@#include <pthread.h> #include "test.h" -#define NUM_THREADS 4 -#define TAG 0 +#define NUM_THREADS 5 +#define MAX_IDX 100 +#define TAG 0 +#define NEW_TAG 1 +static pthread_mutex_t tree_lock =PTHREAD_MUTEX_INITIALIZER ;static pthread_t threads[NUM_THREADS ];static unsigned int seeds[3 ]; -RADIX_TREE (tree,GFP_KERNEL ); -bool test_complete; +static RADIX_TREE(tree, GFP_KERNEL); +static bool test_complete; +static int max_order;/* relentlessly fill the tree with tagged entries */ static void *add_entries_fn (void *arg) { -int pgoff; -rcu_register_thread ();while (!test_complete) { -for (pgoff =0 ; pgoff <100 ; pgoff++) { +unsigned long pgoff; +int order; + +for (pgoff =0 ; pgoff <MAX_IDX ; pgoff++) {pthread_mutex_lock (&tree_lock); -if (item_insert (&tree, pgoff) ==0 ) -item_tag_set (&tree, pgoff,TAG ); +for (order = max_order; order >=0 ; order--) { +if (item_insert_order (&tree, pgoff, order) + ==0 ) { +item_tag_set (&tree, pgoff,TAG ); +break ; + } + }pthread_mutex_unlock (&tree_lock); } }
@@ -62,14 +72,7 @@ static void *tagged_iteration_fn(void *arg)while (!test_complete) {rcu_read_lock ();radix_tree_for_each_tagged (slot, &tree, &iter,0 ,TAG ) { -void *entry; -int i; - -/* busy wait to let removals happen */ -for (i =0 ; i <1000000 ; i++) - ; - - entry =radix_tree_deref_slot (slot); +void *entry =radix_tree_deref_slot (slot);if (unlikely (!entry))continue ;
@@ -110,14 +113,7 @@ static void *untagged_iteration_fn(void *arg)while (!test_complete) {rcu_read_lock ();radix_tree_for_each_slot (slot, &tree, &iter,0 ) { -void *entry; -int i; - -/* busy wait to let removals happen */ -for (i =0 ; i <1000000 ; i++) - ; - - entry =radix_tree_deref_slot (slot); +void *entry =radix_tree_deref_slot (slot);if (unlikely (!entry))continue ;
@@ -152,7 +148,7 @@ static void *remove_entries_fn(void *arg)while (!test_complete) {int pgoff; - pgoff =rand_r (&seeds[2 ]) %100 ; + pgoff =rand_r (&seeds[2 ]) %MAX_IDX ;pthread_mutex_lock (&tree_lock);item_delete (&tree, pgoff);
@@ -164,36 +160,54 @@ static void *remove_entries_fn(void *arg)return NULL ; } +static void *tag_entries_fn (void *arg) +{ +rcu_register_thread (); + +while (!test_complete) { +tag_tagged_items (&tree, &tree_lock,0 ,MAX_IDX ,10 ,TAG , +NEW_TAG ); + } +rcu_unregister_thread (); +return NULL ; +} +/* This is a unit test for a bug found by the syzkaller tester */ -void iteration_test (void ) +void iteration_test (unsigned order,unsigned test_duration) {int i; -printf ("Running iteration tests for 10 seconds\n" ); +printf ("Running %siteration tests for %d seconds\n" , + order >0 ?"multiorder " :"" , test_duration); + max_order = order; test_complete = false;for (i =0 ; i <3 ; i++) seeds[i] =rand ();if (pthread_create (&threads[0 ],NULL , tagged_iteration_fn,NULL )) { -perror ("pthread_create" ); +perror ("create tagged iteration thread" );exit (1 ); }if (pthread_create (&threads[1 ],NULL , untagged_iteration_fn,NULL )) { -perror ("pthread_create" ); +perror ("create untagged iteration thread" );exit (1 ); }if (pthread_create (&threads[2 ],NULL , add_entries_fn,NULL )) { -perror ("pthread_create" ); +perror ("create add entry thread" );exit (1 ); }if (pthread_create (&threads[3 ],NULL , remove_entries_fn,NULL )) { -perror ("pthread_create" ); +perror ("create remove entry thread" ); +exit (1 ); + } +if (pthread_create (&threads[4 ],NULL , tag_entries_fn,NULL )) { +perror ("create tag entry thread" );exit (1 ); } -sleep (10 ); +sleep (test_duration); test_complete = true;for (i =0 ; i <NUM_THREADS ; i++) {
tools/testing/radix-tree/main.c +2 -1
@@ -350,7 +350,8 @@ int main(int argc, char **argv)regression1_test ();regression2_test ();regression3_test (); -iteration_test (); +iteration_test (0 ,10 ); +iteration_test (7 ,20 );single_thread_tests (long_run);/* Free any remaining preallocated nodes */
tools/testing/radix-tree/multiorder.c +23 -0
@@ -75,8 +75,27 @@ static void __multiorder_tag_test(int index, int order)item_kill_tree (&tree); } +static void __multiorder_tag_test2 (unsigned order,unsigned long index2) +{ +RADIX_TREE (tree,GFP_KERNEL ); +unsigned long index = (1 << order); + index2 += index; + +assert (item_insert_order (&tree,0 , order) ==0 ); +assert (item_insert (&tree, index2) ==0 ); + +assert (radix_tree_tag_set (&tree,0 ,0 )); +assert (radix_tree_tag_set (&tree, index2,0 )); + +assert (tag_tagged_items (&tree,NULL ,0 , ~0UL ,10 ,0 ,1 ) ==2 ); + +item_kill_tree (&tree); +} +static void multiorder_tag_tests (void ) { +int i, j; +/* test multi-order entry for indices 0-7 with no sibling pointers */ __multiorder_tag_test (0 ,3 );__multiorder_tag_test (5 ,3 );
@@ -116,6 +135,10 @@ static void multiorder_tag_tests(void)__multiorder_tag_test (300 ,8 );__multiorder_tag_test (0x12345678UL ,8 ); + +for (i =1 ; i <10 ; i++) +for (j =0 ; j < (10 << i); j++) +__multiorder_tag_test2 (i, j); }static void multiorder_check (unsigned long index,int order)
tools/testing/radix-tree/test.h +1 -1
@@ -32,7 +32,7 @@ unsigned long find_item(struct radix_tree_root *, void *item);void tag_check (void );void multiorder_checks (void ); -void iteration_test (void ); +void iteration_test (unsigned order,unsigned duration);void benchmark (void ); struct item *
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 27/33 radix tree test suite: Check multiorder iteration
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
The random iteration test only inserts order-0 entries currently. Update it to insert entries of order between 7 and 0. Also make the maximum index configurable, make some variables static, make the test duration variable, remove some useless spinning, and add a fifth thread which calls tag_tagged_items().
--- tools/testing/radix-tree/iteration_check.c | 80 ++++++++++++++++++------------ tools/testing/radix-tree/main.c | 3 +- tools/testing/radix-tree/multiorder.c | 23 +++++++++ tools/testing/radix-tree/test.h | 2 +- 4 files changed, 73 insertions(+), 35 deletions(-)
tools/testing/radix-tree/iteration_check.c +47 -33
@@ -16,26 +16,36 @@#include <pthread.h> #include "test.h" -#define NUM_THREADS 4 -#define TAG 0 +#define NUM_THREADS 5 +#define MAX_IDX 100 +#define TAG 0 +#define NEW_TAG 1 +static pthread_mutex_t tree_lock =PTHREAD_MUTEX_INITIALIZER ;static pthread_t threads[NUM_THREADS ];static unsigned int seeds[3 ]; -RADIX_TREE (tree,GFP_KERNEL ); -bool test_complete; +static RADIX_TREE(tree, GFP_KERNEL); +static bool test_complete; +static int max_order;/* relentlessly fill the tree with tagged entries */ static void *add_entries_fn (void *arg) { -int pgoff; -rcu_register_thread ();while (!test_complete) { -for (pgoff =0 ; pgoff <100 ; pgoff++) { +unsigned long pgoff; +int order; + +for (pgoff =0 ; pgoff <MAX_IDX ; pgoff++) {pthread_mutex_lock (&tree_lock); -if (item_insert (&tree, pgoff) ==0 ) -item_tag_set (&tree, pgoff,TAG ); +for (order = max_order; order >=0 ; order--) { +if (item_insert_order (&tree, pgoff, order) + ==0 ) { +item_tag_set (&tree, pgoff,TAG ); +break ; + } + }pthread_mutex_unlock (&tree_lock); } }
@@ -62,14 +72,7 @@ static void *tagged_iteration_fn(void *arg)while (!test_complete) {rcu_read_lock ();radix_tree_for_each_tagged (slot, &tree, &iter,0 ,TAG ) { -void *entry; -int i; - -/* busy wait to let removals happen */ -for (i =0 ; i <1000000 ; i++) - ; - - entry =radix_tree_deref_slot (slot); +void *entry =radix_tree_deref_slot (slot);if (unlikely (!entry))continue ;
@@ -110,14 +113,7 @@ static void *untagged_iteration_fn(void *arg)while (!test_complete) {rcu_read_lock ();radix_tree_for_each_slot (slot, &tree, &iter,0 ) { -void *entry; -int i; - -/* busy wait to let removals happen */ -for (i =0 ; i <1000000 ; i++) - ; - - entry =radix_tree_deref_slot (slot); +void *entry =radix_tree_deref_slot (slot);if (unlikely (!entry))continue ;
@@ -152,7 +148,7 @@ static void *remove_entries_fn(void *arg)while (!test_complete) {int pgoff; - pgoff =rand_r (&seeds[2 ]) %100 ; + pgoff =rand_r (&seeds[2 ]) %MAX_IDX ;pthread_mutex_lock (&tree_lock);item_delete (&tree, pgoff);
@@ -164,36 +160,54 @@ static void *remove_entries_fn(void *arg)return NULL ; } +static void *tag_entries_fn (void *arg) +{ +rcu_register_thread (); + +while (!test_complete) { +tag_tagged_items (&tree, &tree_lock,0 ,MAX_IDX ,10 ,TAG , +NEW_TAG ); + } +rcu_unregister_thread (); +return NULL ; +} +/* This is a unit test for a bug found by the syzkaller tester */ -void iteration_test (void ) +void iteration_test (unsigned order,unsigned test_duration) {int i; -printf ("Running iteration tests for 10 seconds\n" ); +printf ("Running %siteration tests for %d seconds\n" , + order >0 ?"multiorder " :"" , test_duration); + max_order = order; test_complete = false;for (i =0 ; i <3 ; i++) seeds[i] =rand ();if (pthread_create (&threads[0 ],NULL , tagged_iteration_fn,NULL )) { -perror ("pthread_create" ); +perror ("create tagged iteration thread" );exit (1 ); }if (pthread_create (&threads[1 ],NULL , untagged_iteration_fn,NULL )) { -perror ("pthread_create" ); +perror ("create untagged iteration thread" );exit (1 ); }if (pthread_create (&threads[2 ],NULL , add_entries_fn,NULL )) { -perror ("pthread_create" ); +perror ("create add entry thread" );exit (1 ); }if (pthread_create (&threads[3 ],NULL , remove_entries_fn,NULL )) { -perror ("pthread_create" ); +perror ("create remove entry thread" ); +exit (1 ); + } +if (pthread_create (&threads[4 ],NULL , tag_entries_fn,NULL )) { +perror ("create tag entry thread" );exit (1 ); } -sleep (10 ); +sleep (test_duration); test_complete = true;for (i =0 ; i <NUM_THREADS ; i++) {
tools/testing/radix-tree/main.c +2 -1
@@ -350,7 +350,8 @@ int main(int argc, char **argv)regression1_test ();regression2_test ();regression3_test (); -iteration_test (); +iteration_test (0 ,10 ); +iteration_test (7 ,20 );single_thread_tests (long_run);/* Free any remaining preallocated nodes */
tools/testing/radix-tree/multiorder.c +23 -0
@@ -75,8 +75,27 @@ static void __multiorder_tag_test(int index, int order)item_kill_tree (&tree); } +static void __multiorder_tag_test2 (unsigned order,unsigned long index2) +{ +RADIX_TREE (tree,GFP_KERNEL ); +unsigned long index = (1 << order); + index2 += index; + +assert (item_insert_order (&tree,0 , order) ==0 ); +assert (item_insert (&tree, index2) ==0 ); + +assert (radix_tree_tag_set (&tree,0 ,0 )); +assert (radix_tree_tag_set (&tree, index2,0 )); + +assert (tag_tagged_items (&tree,NULL ,0 , ~0UL ,10 ,0 ,1 ) ==2 ); + +item_kill_tree (&tree); +} +static void multiorder_tag_tests (void ) { +int i, j; +/* test multi-order entry for indices 0-7 with no sibling pointers */ __multiorder_tag_test (0 ,3 );__multiorder_tag_test (5 ,3 );
@@ -116,6 +135,10 @@ static void multiorder_tag_tests(void)__multiorder_tag_test (300 ,8 );__multiorder_tag_test (0x12345678UL ,8 ); + +for (i =1 ; i <10 ; i++) +for (j =0 ; j < (10 << i); j++) +__multiorder_tag_test2 (i, j); }static void multiorder_check (unsigned long index,int order)
tools/testing/radix-tree/test.h +1 -1
@@ -32,7 +32,7 @@ unsigned long find_item(struct radix_tree_root *, void *item);void tag_check (void );void multiorder_checks (void ); -void iteration_test (void ); +void iteration_test (unsigned order,unsigned duration);void benchmark (void ); struct item *
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 28/33 idr: Add ida_is_empty
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Two of the USB Gadgets were poking around in the internals of struct ida in order to determine if it is empty. Add the appropriate abstraction.
--- drivers/usb/gadget/function/f_hid.c | 6 +++--- drivers/usb/gadget/function/f_printer.c | 6 +++--- include/linux/idr.h | 5 +++++ 3 files changed, 11 insertions(+), 6 deletions(-)
drivers/usb/gadget/function/f_hid.c +3 -3
@@ -905,7 +905,7 @@ static void hidg_free_inst(struct usb_function_instance *f)mutex_lock (&hidg_ida_lock);hidg_put_minor (opts->minor); -if (idr_is_empty (&hidg_ida.idr)) +if (ida_is_empty (&hidg_ida))ghid_cleanup ();mutex_unlock (&hidg_ida_lock);
@@ -931,7 +931,7 @@ static struct usb_function_instance *hidg_alloc_inst(void)mutex_lock (&hidg_ida_lock); -if (idr_is_empty (&hidg_ida.idr)) { +if (ida_is_empty (&hidg_ida)) { status =ghid_setup (NULL ,HIDG_MINORS );if (status) { ret =ERR_PTR (status);
@@ -944,7 +944,7 @@ static struct usb_function_instance *hidg_alloc_inst(void)if (opts->minor <0 ) { ret =ERR_PTR (opts->minor);kfree (opts); -if (idr_is_empty (&hidg_ida.idr)) +if (ida_is_empty (&hidg_ida))ghid_cleanup (); goto unlock; }
drivers/usb/gadget/function/f_printer.c +3 -3
@@ -1265,7 +1265,7 @@ static void gprinter_free_inst(struct usb_function_instance *f)mutex_lock (&printer_ida_lock);gprinter_put_minor (opts->minor); -if (idr_is_empty (&printer_ida.idr)) +if (ida_is_empty (&printer_ida))gprinter_cleanup ();mutex_unlock (&printer_ida_lock);
@@ -1289,7 +1289,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void)mutex_lock (&printer_ida_lock); -if (idr_is_empty (&printer_ida.idr)) { +if (ida_is_empty (&printer_ida)) { status =gprinter_setup (PRINTER_MINORS );if (status) { ret =ERR_PTR (status);
@@ -1302,7 +1302,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void)if (opts->minor <0 ) { ret =ERR_PTR (opts->minor);kfree (opts); -if (idr_is_empty (&printer_ida.idr)) +if (ida_is_empty (&printer_ida))gprinter_cleanup (); goto unlock; }
include/linux/idr.h +5 -0
@@ -195,6 +195,11 @@ static inline int ida_get_new(struct ida *ida, int *p_id) return ida_get_new_above(ida, 0, p_id); } +static inline bool ida_is_empty (struct ida *ida) +{ +return idr_is_empty (&ida->idr); +} +void __init idr_init_cache (void );#endif /* __IDR_H__ */
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 28/33 idr: Add ida_is_empty
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Two of the USB Gadgets were poking around in the internals of struct ida in order to determine if it is empty. Add the appropriate abstraction.
--- drivers/usb/gadget/function/f_hid.c | 6 +++--- drivers/usb/gadget/function/f_printer.c | 6 +++--- include/linux/idr.h | 5 +++++ 3 files changed, 11 insertions(+), 6 deletions(-)
drivers/usb/gadget/function/f_hid.c +3 -3
@@ -905,7 +905,7 @@ static void hidg_free_inst(struct usb_function_instance *f)mutex_lock (&hidg_ida_lock);hidg_put_minor (opts->minor); -if (idr_is_empty (&hidg_ida.idr)) +if (ida_is_empty (&hidg_ida))ghid_cleanup ();mutex_unlock (&hidg_ida_lock);
@@ -931,7 +931,7 @@ static struct usb_function_instance *hidg_alloc_inst(void)mutex_lock (&hidg_ida_lock); -if (idr_is_empty (&hidg_ida.idr)) { +if (ida_is_empty (&hidg_ida)) { status =ghid_setup (NULL ,HIDG_MINORS );if (status) { ret =ERR_PTR (status);
@@ -944,7 +944,7 @@ static struct usb_function_instance *hidg_alloc_inst(void)if (opts->minor <0 ) { ret =ERR_PTR (opts->minor);kfree (opts); -if (idr_is_empty (&hidg_ida.idr)) +if (ida_is_empty (&hidg_ida))ghid_cleanup (); goto unlock; }
drivers/usb/gadget/function/f_printer.c +3 -3
@@ -1265,7 +1265,7 @@ static void gprinter_free_inst(struct usb_function_instance *f)mutex_lock (&printer_ida_lock);gprinter_put_minor (opts->minor); -if (idr_is_empty (&printer_ida.idr)) +if (ida_is_empty (&printer_ida))gprinter_cleanup ();mutex_unlock (&printer_ida_lock);
@@ -1289,7 +1289,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void)mutex_lock (&printer_ida_lock); -if (idr_is_empty (&printer_ida.idr)) { +if (ida_is_empty (&printer_ida)) { status =gprinter_setup (PRINTER_MINORS );if (status) { ret =ERR_PTR (status);
@@ -1302,7 +1302,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void)if (opts->minor <0 ) { ret =ERR_PTR (opts->minor);kfree (opts); -if (idr_is_empty (&printer_ida.idr)) +if (ida_is_empty (&printer_ida))gprinter_cleanup (); goto unlock; }
include/linux/idr.h +5 -0
@@ -195,6 +195,11 @@ static inline int ida_get_new(struct ida *ida, int *p_id) return ida_get_new_above(ida, 0, p_id); } +static inline bool ida_is_empty (struct ida *ida) +{ +return idr_is_empty (&ida->idr); +} +void __init idr_init_cache (void );#endif /* __IDR_H__ */
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 29/33 tpm: Use idr_find(), not idr_find_slowpath()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
idr_find_slowpath() is not intended to be part of the public API, it's an implementation detail. There's no reason to skip straight to the slowpath here.
--- drivers/char/tpm/tpm-chip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/char/tpm/tpm-chip.c +2 -2
@@ -84,7 +84,7 @@ EXPORT_SYMBOL_GPL(tpm_put_ops);* * The return'd chip has been tpm_try_get_ops'd and must be released via * tpm_put_ops -*/ +*/ struct tpm_chip *tpm_chip_find_get (int chip_num) {struct tpm_chip *chip, *res =NULL ;
@@ -103,7 +103,7 @@ struct tpm_chip *tpm_chip_find_get(int chip_num) } }while (chip_prev != chip_num); }else { - chip =idr_find_slowpath (&dev_nums_idr, chip_num); + chip =idr_find (&dev_nums_idr, chip_num);if (chip && !tpm_try_get_ops (chip)) res = chip; }
Signature · 1 line
2.10.2
PATCH v3 29/33 tpm: Use idr_find(), not idr_find_slowpath()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
idr_find_slowpath() is not intended to be part of the public API, it's an implementation detail. There's no reason to skip straight to the slowpath here.
--- drivers/char/tpm/tpm-chip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/char/tpm/tpm-chip.c +2 -2
@@ -84,7 +84,7 @@ EXPORT_SYMBOL_GPL(tpm_put_ops);* * The return'd chip has been tpm_try_get_ops'd and must be released via * tpm_put_ops -*/ +*/ struct tpm_chip *tpm_chip_find_get (int chip_num) {struct tpm_chip *chip, *res =NULL ;
@@ -103,7 +103,7 @@ struct tpm_chip *tpm_chip_find_get(int chip_num) } }while (chip_prev != chip_num); }else { - chip =idr_find_slowpath (&dev_nums_idr, chip_num); + chip =idr_find (&dev_nums_idr, chip_num);if (chip && !tpm_try_get_ops (chip)) res = chip; }
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 30/33 rxrpc: Abstract away knowledge of IDR internals
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Add idr_get_cursor() / idr_set_cursor() APIs, and remove the reference to IDR_SIZE.
--- include/linux/idr.h | 26 ++++++++++++++++++++++++++ net/rxrpc/af_rxrpc.c | 11 ++++++----- net/rxrpc/conn_client.c | 4 ++-- 3 files changed, 34 insertions(+), 7 deletions(-)
include/linux/idr.h +26 -0
@@ -56,6 +56,32 @@ struct idr {#define DEFINE_IDR (name) struct idr name = IDR_INIT(name)/** +* idr_get_cursor - Return the current position of the cyclic allocator +* @idr: idr handle +* +* The value returned is the value that will be next returned from +* idr_alloc_cyclic() if it is free (otherwise the search will start from +* this position). +*/ +static inline unsigned int idr_get_cursor (struct idr *idr) +{ +return READ_ONCE (idr->cur); +} + +/** +* idr_set_cursor - Set the current position of the cyclic allocator +* @idr: idr handle +* @val: new position +* +* The next call to idr_alloc_cyclic() will return @val if it is free +* (otherwise the search will start from this position). +*/ +static inline void idr_set_cursor (struct idr *idr,unsigned int val) +{ +WRITE_ONCE (idr->cur, val); +} + +/** * DOC: idr sync * idr synchronization (stolen from radix-tree.h) *
net/rxrpc/af_rxrpc.c +6 -5
@@ -762,16 +762,17 @@ static const struct net_proto_family rxrpc_family_ops = {static int __init af_rxrpc_init (void ) {int ret =-1 ; +unsigned int tmp; BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > FIELD_SIZEOF(struct sk_buff, cb));get_random_bytes (&rxrpc_epoch,sizeof (rxrpc_epoch)); rxrpc_epoch |=RXRPC_RANDOM_EPOCH ; -get_random_bytes (&rxrpc_client_conn_ids.cur, -sizeof (rxrpc_client_conn_ids.cur)); - rxrpc_client_conn_ids.cur &=0x3fffffff ; -if (rxrpc_client_conn_ids.cur ==0 ) - rxrpc_client_conn_ids.cur =1 ; +get_random_bytes (&tmp,sizeof (tmp)); + tmp &=0x3fffffff ; +if (tmp ==0 ) + tmp =1 ; +idr_set_cursor (&rxrpc_client_conn_ids, tmp); ret = -ENOMEM ; rxrpc_call_jar = kmem_cache_create(
net/rxrpc/conn_client.c +2 -2
@@ -263,12 +263,12 @@ static bool rxrpc_may_reuse_conn(struct rxrpc_connection *conn)* times the maximum number of client conns away from the current * allocation point to try and keep the IDs concentrated. */ - id_cursor =READ_ONCE (rxrpc_client_conn_ids.cur); + id_cursor =idr_get_cursor (&rxrpc_client_conn_ids); id = conn->proto.cid >>RXRPC_CIDSHIFT ; distance = id - id_cursor;if (distance <0 ) distance = -distance; - limit =round_up (rxrpc_max_client_connections,IDR_SIZE ) *4 ; + limit =max (rxrpc_max_client_connections *4 ,1024U );if (distance > limit) goto mark_dont_reuse;
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 30/33 rxrpc: Abstract away knowledge of IDR internals
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Add idr_get_cursor() / idr_set_cursor() APIs, and remove the reference to IDR_SIZE.
--- include/linux/idr.h | 26 ++++++++++++++++++++++++++ net/rxrpc/af_rxrpc.c | 11 ++++++----- net/rxrpc/conn_client.c | 4 ++-- 3 files changed, 34 insertions(+), 7 deletions(-)
include/linux/idr.h +26 -0
@@ -56,6 +56,32 @@ struct idr {#define DEFINE_IDR (name) struct idr name = IDR_INIT(name)/** +* idr_get_cursor - Return the current position of the cyclic allocator +* @idr: idr handle +* +* The value returned is the value that will be next returned from +* idr_alloc_cyclic() if it is free (otherwise the search will start from +* this position). +*/ +static inline unsigned int idr_get_cursor (struct idr *idr) +{ +return READ_ONCE (idr->cur); +} + +/** +* idr_set_cursor - Set the current position of the cyclic allocator +* @idr: idr handle +* @val: new position +* +* The next call to idr_alloc_cyclic() will return @val if it is free +* (otherwise the search will start from this position). +*/ +static inline void idr_set_cursor (struct idr *idr,unsigned int val) +{ +WRITE_ONCE (idr->cur, val); +} + +/** * DOC: idr sync * idr synchronization (stolen from radix-tree.h) *
net/rxrpc/af_rxrpc.c +6 -5
@@ -762,16 +762,17 @@ static const struct net_proto_family rxrpc_family_ops = {static int __init af_rxrpc_init (void ) {int ret =-1 ; +unsigned int tmp; BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > FIELD_SIZEOF(struct sk_buff, cb));get_random_bytes (&rxrpc_epoch,sizeof (rxrpc_epoch)); rxrpc_epoch |=RXRPC_RANDOM_EPOCH ; -get_random_bytes (&rxrpc_client_conn_ids.cur, -sizeof (rxrpc_client_conn_ids.cur)); - rxrpc_client_conn_ids.cur &=0x3fffffff ; -if (rxrpc_client_conn_ids.cur ==0 ) - rxrpc_client_conn_ids.cur =1 ; +get_random_bytes (&tmp,sizeof (tmp)); + tmp &=0x3fffffff ; +if (tmp ==0 ) + tmp =1 ; +idr_set_cursor (&rxrpc_client_conn_ids, tmp); ret = -ENOMEM ; rxrpc_call_jar = kmem_cache_create(
net/rxrpc/conn_client.c +2 -2
@@ -263,12 +263,12 @@ static bool rxrpc_may_reuse_conn(struct rxrpc_connection *conn)* times the maximum number of client conns away from the current * allocation point to try and keep the IDs concentrated. */ - id_cursor =READ_ONCE (rxrpc_client_conn_ids.cur); + id_cursor =idr_get_cursor (&rxrpc_client_conn_ids); id = conn->proto.cid >>RXRPC_CIDSHIFT ; distance = id - id_cursor;if (distance <0 ) distance = -distance; - limit =round_up (rxrpc_max_client_connections,IDR_SIZE ) *4 ; + limit =max (rxrpc_max_client_connections *4 ,1024U );if (distance > limit) goto mark_dont_reuse;
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 31/33 idr: Reduce the number of bits per level from 8 to 6
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
In preparation for merging the IDR and radix tree, reduce the fanout at each level from 256 to 64. If this causes a performance problem then a bisect will point to this commit, and we'll have a better idea about what we might do to fix it.
--- include/linux/idr.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
include/linux/idr.h +4 -5
@@ -18,12 +18,11 @@#include <linux/rcupdate.h> /* -* We want shallower trees and thus more bits covered at each layer. 8 -* bits gives us large enough first layer for most use cases and maximum -* tree depth of 4. Each idr_layer is slightly larger than 2k on 64bit and -* 1k on 32bit. +* Using 6 bits at each layer allows us to allocate 7 layers out of each page. +* 8 bits only gave us 3 layers out of every pair of pages, which is less +* efficient except for trees with a largest element between 192-255 inclusive. */ -#define IDR_BITS 8 +#define IDR_BITS 6#define IDR_SIZE (1 << IDR_BITS)#define IDR_MASK ((1 << IDR_BITS)-1)
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 31/33 idr: Reduce the number of bits per level from 8 to 6
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
In preparation for merging the IDR and radix tree, reduce the fanout at each level from 256 to 64. If this causes a performance problem then a bisect will point to this commit, and we'll have a better idea about what we might do to fix it.
--- include/linux/idr.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
include/linux/idr.h +4 -5
@@ -18,12 +18,11 @@#include <linux/rcupdate.h> /* -* We want shallower trees and thus more bits covered at each layer. 8 -* bits gives us large enough first layer for most use cases and maximum -* tree depth of 4. Each idr_layer is slightly larger than 2k on 64bit and -* 1k on 32bit. +* Using 6 bits at each layer allows us to allocate 7 layers out of each page. +* 8 bits only gave us 3 layers out of every pair of pages, which is less +* efficient except for trees with a largest element between 192-255 inclusive. */ -#define IDR_BITS 8 +#define IDR_BITS 6#define IDR_SIZE (1 << IDR_BITS)#define IDR_MASK ((1 << IDR_BITS)-1)
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 32/33 radix tree test suite: Add some more functionality
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
IDR needs more functionality from the kernel: kmalloc()/kfree(), and xchg().
--- tools/testing/radix-tree/linux.c | 15 +++++++++++++++ tools/testing/radix-tree/linux/kernel.h | 3 +++ tools/testing/radix-tree/linux/slab.h | 3 +++ 3 files changed, 21 insertions(+)
tools/testing/radix-tree/linux.c +15 -0
@@ -54,6 +54,21 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp)free (objp); } +void *kmalloc (size_t size,gfp_t gfp) +{ +void *ret =malloc (size); +uatomic_inc (&nr_allocated); +return ret; +} + +void kfree (void *p) +{ +if (!p) +return ; +uatomic_dec (&nr_allocated); +free (p); +} +struct kmem_cache *kmem_cache_create (const char *name,size_t size,size_t offset,unsigned long flags,void (*ctor)(void *))
tools/testing/radix-tree/linux/kernel.h +3 -0
@@ -8,6 +8,7 @@#include <limits.h> #include "../../include/linux/compiler.h" +#include "../../include/linux/err.h" #include "../../../include/linux/kconfig.h" #ifdef BENCHMARK
@@ -58,4 +59,6 @@ static inline int in_interrupt(void)#define round_up (x, y) ((((x)-1) | __round_mask(x, y))+1)#define round_down (x, y) ((x) & ~__round_mask(x, y)) +#define xchg (ptr, x) uatomic_xchg(ptr, x) +#endif /* _KERNEL_H */
tools/testing/radix-tree/linux/slab.h +3 -0
@@ -7,6 +7,9 @@#define SLAB_PANIC 2#define SLAB_RECLAIM_ACCOUNT 0x00020000UL/* Objects are reclaimable */ +void *kmalloc (size_t size,gfp_t ); +void kfree (void *); +struct kmem_cache {int size;void (*ctor)(void *);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 32/33 radix tree test suite: Add some more functionality
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
IDR needs more functionality from the kernel: kmalloc()/kfree(), and xchg().
--- tools/testing/radix-tree/linux.c | 15 +++++++++++++++ tools/testing/radix-tree/linux/kernel.h | 3 +++ tools/testing/radix-tree/linux/slab.h | 3 +++ 3 files changed, 21 insertions(+)
tools/testing/radix-tree/linux.c +15 -0
@@ -54,6 +54,21 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp)free (objp); } +void *kmalloc (size_t size,gfp_t gfp) +{ +void *ret =malloc (size); +uatomic_inc (&nr_allocated); +return ret; +} + +void kfree (void *p) +{ +if (!p) +return ; +uatomic_dec (&nr_allocated); +free (p); +} +struct kmem_cache *kmem_cache_create (const char *name,size_t size,size_t offset,unsigned long flags,void (*ctor)(void *))
tools/testing/radix-tree/linux/kernel.h +3 -0
@@ -8,6 +8,7 @@#include <limits.h> #include "../../include/linux/compiler.h" +#include "../../include/linux/err.h" #include "../../../include/linux/kconfig.h" #ifdef BENCHMARK
@@ -58,4 +59,6 @@ static inline int in_interrupt(void)#define round_up (x, y) ((((x)-1) | __round_mask(x, y))+1)#define round_down (x, y) ((x) & ~__round_mask(x, y)) +#define xchg (ptr, x) uatomic_xchg(ptr, x) +#endif /* _KERNEL_H */
tools/testing/radix-tree/linux/slab.h +3 -0
@@ -7,6 +7,9 @@#define SLAB_PANIC 2#define SLAB_RECLAIM_ACCOUNT 0x00020000UL/* Objects are reclaimable */ +void *kmalloc (size_t size,gfp_t ); +void kfree (void *); +struct kmem_cache {int size;void (*ctor)(void *);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 33/33 Reimplement IDR and IDA using the radix tree
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
The IDR is very similar to the radix tree. It has some functionality that the radix tree did not have (alloc next free, cyclic allocation, a callback-based for_each, destroy tree), which is readily implementable on top of the radix tree. A few small changes were needed in order to use a tag to represent nodes with free space below them.
The IDA is reimplemented as a client of the newly enhanced radix tree. As in the current implementation, it uses a bitmap at the last level of the tree.
--- include/linux/idr.h | 132 ++-- include/linux/radix-tree.h | 5 +- init/main.c | 3 +- lib/idr.c | 1078 ------------------------------- lib/radix-tree.c | 632 ++++++++++++++++-- tools/testing/radix-tree/Makefile | 5 +- tools/testing/radix-tree/idr.c | 160 +++++ tools/testing/radix-tree/linux/idr.h | 1 + tools/testing/radix-tree/linux/kernel.h | 2 + tools/testing/radix-tree/main.c | 6 + tools/testing/radix-tree/test.h | 2 + 11 files changed, 811 insertions(+), 1215 deletions(-) create mode 100644 tools/testing/radix-tree/idr.c create mode 100644 tools/testing/radix-tree/linux/idr.h
include/linux/idr.h +54 -78
@@ -12,47 +12,22 @@#ifndef __IDR_H__#define __IDR_H__ -#include <linux/types.h> -#include <linux/bitops.h> -#include <linux/init.h> -#include <linux/rcupdate.h> - -/* -* Using 6 bits at each layer allows us to allocate 7 layers out of each page. -* 8 bits only gave us 3 layers out of every pair of pages, which is less -* efficient except for trees with a largest element between 192-255 inclusive. -*/ -#define IDR_BITS 6 -#define IDR_SIZE (1 << IDR_BITS) -#define IDR_MASK ((1 << IDR_BITS)-1) - -struct idr_layer { -int prefix;/* the ID prefix of this idr_layer */ -int layer;/* distance from leaf */ -struct idr_layer __rcu *ary[1 <<IDR_BITS ]; -int count;/* When zero, we can release it */ -union { -/* A zero bit means "space here" */ - DECLARE_BITMAP(bitmap, IDR_SIZE); -struct rcu_head rcu_head; - }; -}; +#include <linux/radix-tree.h> +#include <linux/gfp.h> struct idr { -struct idr_layer __rcu *hint;/* the last layer allocated from */ -struct idr_layer __rcu *top; -int layers;/* only valid w/o concurrent changes */ -int cur;/* current pos for cyclic allocation */ -spinlock_t lock; -int id_free_cnt; -struct idr_layer *id_free; +struct radix_tree_root idr_rt; +unsigned int idr_next; }; -#define IDR_INIT (name) \ +/* Set the IDR flag and the IDR_FREE tag */ +#define IDR_RT_MARKER ((__force gfp_t)(3 << __GFP_BITS_SHIFT)) + +#define IDR_INIT \ { \ - .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ + .idr_rt = RADIX_TREE_INIT(IDR_RT_MARKER) \ } -#define DEFINE_IDR (name) struct idr name = IDR_INIT(name) +#define DEFINE_IDR (name) struct idr name = IDR_INIT/** * idr_get_cursor - Return the current position of the cyclic allocator
@@ -64,7 +39,7 @@ struct idr {*/ static inline unsigned int idr_get_cursor (struct idr *idr) { -return READ_ONCE (idr->cur); +return READ_ONCE (idr->idr_next); }/**
@@ -77,7 +52,7 @@ static inline unsigned int idr_get_cursor(struct idr *idr)*/ static inline void idr_set_cursor (struct idr *idr,unsigned int val) { -WRITE_ONCE (idr->cur, val); +WRITE_ONCE (idr->idr_next, val); }/**
@@ -97,22 +72,30 @@ static inline void idr_set_cursor(struct idr *idr, unsigned int val)* period). */ -/* -* This is what we export. -*/ - -void *idr_find_slowpath (struct idr *idp,int id);void idr_preload (gfp_t gfp_mask); -int idr_alloc (struct idr *idp,void *ptr,int start,int end,gfp_t gfp_mask); -int idr_alloc_cyclic (struct idr *idr,void *ptr,int start,int end,gfp_t gfp_mask); -int idr_for_each (struct idr *idp, +int idr_alloc (struct idr *,void *,int start,int end,gfp_t gfp_mask); +int idr_alloc_cyclic (struct idr *,void *,int start,int end,gfp_t gfp_mask); +int idr_for_each (struct idr *,int (*fn)(int id,void *p,void *data),void *data); -void *idr_get_next (struct idr *idp,int *nextid); -void *idr_replace (struct idr *idp,void *ptr,int id); -void idr_remove (struct idr *idp,int id); -void idr_destroy (struct idr *idp); -void idr_init (struct idr *idp); -bool idr_is_empty (struct idr *idp); +void *idr_get_next (struct idr *,int *nextid); +void *idr_replace (struct idr *,void *,int id); +void idr_destroy (struct idr *); + +static inline void idr_remove (struct idr *idp,int id) +{ +radix_tree_delete (&idp->idr_rt, id); +} + +static inline void idr_init (struct idr *idp) +{ +memset (idp,0 ,sizeof (*idp)); + idp->idr_rt.gfp_mask =IDR_RT_MARKER ; +} + +static inline bool idr_is_empty (struct idr *idp) +{ +return radix_tree_empty (&idp->idr_rt); +}/** * idr_preload_end - end preload section started with idr_preload()
@@ -139,17 +122,12 @@ static inline void idr_preload_end(void)*/ static inline void *idr_find (struct idr *idr,int id) { -struct idr_layer *hint =rcu_dereference_raw (idr->hint); - -if (hint && (id & ~IDR_MASK ) == hint->prefix) -return rcu_dereference_raw (hint->ary[id &IDR_MASK ]); - -return idr_find_slowpath (idr, id); +return radix_tree_lookup (&idr->idr_rt, id); }/** * idr_for_each_entry - iterate over an idr's elements of a given type -* @idp: idr handle +* @idr: idr handle * @entry: the type * to use as cursor * @id: id entry's key *
@@ -157,57 +135,58 @@ static inline void *idr_find(struct idr *idr, int id)* after normal terminatinon @entry is left with the value NULL. This * is convenient for a "not found" value. */ -#define idr_for_each_entry (idp, entry, id) \ - for (id = 0; ((entry) = idr_get_next(idp, &(id))) != NULL; ++id) +#define idr_for_each_entry (idr, entry, id) \ + for (id = 0; ((entry) = idr_get_next(idr, &(id))) != NULL; ++id)/** -* idr_for_each_entry - continue iteration over an idr's elements of a given type -* @idp: idr handle +* idr_for_each_entry_continue - continue iteration over an idr's elements of a given type +* @idr: idr handle * @entry: the type * to use as cursor * @id: id entry's key * * Continue to iterate over list of given type, continuing after * the current position. */ -#define idr_for_each_entry_continue (idp, entry, id) \ - for ((entry) = idr_get_next((idp), &(id)); \ +#define idr_for_each_entry_continue (idr, entry, id) \ + for ((entry) = idr_get_next((idr), &(id)); \ entry; \ - ++id, (entry) = idr_get_next((idp), &(id))) + ++id, (entry) = idr_get_next((idr), &(id)))/* * IDA - IDR based id allocator, use when translation from id to * pointer isn't necessary. -* -* IDA_BITMAP_LONGS is calculated to be one less to accommodate -* ida_bitmap->nr_busy so that the whole struct fits in 128 bytes. */ #define IDA_CHUNK_SIZE 128/* 128 bytes per chunk */ -#define IDA_BITMAP_LONGS (IDA_CHUNK_SIZE / sizeof(long) - 1) +#define IDA_BITMAP_LONGS (IDA_CHUNK_SIZE / sizeof(long))#define IDA_BITMAP_BITS (IDA_BITMAP_LONGS * sizeof(long) * 8)struct ida_bitmap { -long nr_busy;unsigned long bitmap[IDA_BITMAP_LONGS ]; };struct ida { -struct idr idr; +struct radix_tree_root ida_rt;struct ida_bitmap *free_bitmap; }; -#define IDA_INIT (name) { .idr = IDR_INIT((name).idr), .free_bitmap = NULL, } -#define DEFINE_IDA (name) struct ida name = IDA_INIT(name) +#define IDA_INIT { .ida_rt = RADIX_TREE_INIT(IDR_RT_MARKER), } +#define DEFINE_IDA (name) struct ida name = IDA_INITint 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);void ida_destroy (struct ida *ida); -void ida_init (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); +static inline void ida_init (struct ida *ida) +{ +memset (ida,0 ,sizeof (*ida)); + ida->ida_rt.gfp_mask =IDR_RT_MARKER ; +} +/** * ida_get_new - allocate new ID * @ida: idr handle
@@ -222,9 +201,6 @@ static inline int ida_get_new(struct ida *ida, int *p_id)static inline bool ida_is_empty (struct ida *ida) { -return idr_is_empty (&ida->idr); +return radix_tree_empty (&ida->ida_rt); } - -void __init idr_init_cache (void ); -#endif /* __IDR_H__ */
include/linux/radix-tree.h +4 -1
@@ -103,7 +103,10 @@ struct radix_tree_node {unsigned long tags[RADIX_TREE_MAX_TAGS ][RADIX_TREE_TAG_LONGS ]; }; -/* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */ +/* The top bits of gfp_mask are used to store the root tags and the IDR flag */ +#define ROOT_IS_IDR (1 << __GFP_BITS_SHIFT) +#define ROOT_TAG_SHIFT (__GFP_BITS_SHIFT + 1) +struct radix_tree_root {gfp_t gfp_mask;struct radix_tree_node __rcu *rnode;
init/main.c +1 -2
@@ -550,7 +550,7 @@ asmlinkage __visible void __init start_kernel(void)if (WARN (!irqs_disabled (),"Interrupts were enabled *very* early, fixing it\n" ))local_irq_disable (); -idr_init_cache (); +radix_tree_init ();/* * Allow workqueue creation and work item queueing/cancelling
@@ -565,7 +565,6 @@ asmlinkage __visible void __init start_kernel(void)trace_init ();context_tracking_init (); -radix_tree_init ();/* init some links before init_ISA_irqs() */ early_irq_init ();init_IRQ ();
lib/idr.c +0 -1078
1,089 lines not on this page · raw message
lib/radix-tree.c +578 -54
821 lines not on this page · raw message
tools/testing/radix-tree/Makefile +3 -2
@@ -3,7 +3,7 @@ CFLAGS += -I. -I../../include -g -O2 -Wall -D_LGPL_SOURCE LDFLAGS += -lpthread -lurcu TARGETS = main OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \ - regression1.o regression2.o regression3.o multiorder.o \ + regression1.o regression2.o regression3.o multiorder.o idr.o \ iteration_check.o benchmark.o ifdef BENCHMARK
@@ -23,7 +23,8 @@ find_next_bit.o: ../../lib/find_bit.c $(OFILES): *.h */*.h \ ../../include/linux/*.h \ - ../../../include/linux/radix-tree.h + ../../../include/linux/radix-tree.h \ + ../../../include/linux/idr.h radix-tree.c: ../../../lib/radix-tree.c sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@
tools/testing/radix-tree/idr.c new file +160 -0
160 lines not on this page · raw message
tools/testing/radix-tree/linux/idr.h new file +1 -0
@@ -0,0 +1 @@ +#include "../../../../include/linux/idr.h"
tools/testing/radix-tree/linux/kernel.h +2 -0
@@ -30,6 +30,7 @@#define __force#define DIV_ROUND_UP (n,d) (((n) + (d) - 1) / (d))#define pr_debug printk +#define pr_cont printk#define smp_rmb () barrier()#define smp_wmb () barrier()
@@ -41,6 +42,7 @@ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type, member) );})#define min (a, b) ((a) < (b) ? (a) : (b)) +#define max (a, b) ((a) < (b) ? (b) : (a))#define cond_resched () sched_yield()
tools/testing/radix-tree/main.c +6 -0
@@ -3,6 +3,7 @@#include <unistd.h> #include <time.h> #include <assert.h> +#include <limits.h> #include <linux/slab.h> #include <linux/radix-tree.h>
@@ -314,6 +315,11 @@ static void single_thread_tests(bool long_run)rcu_barrier ();printf ("after dynamic_height_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count); +idr_checks (); +ida_checks (); +rcu_barrier (); +printf ("after idr_checks: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);big_gang_check (long_run);rcu_barrier (); printf("after big_gang_check: %d allocated, preempt %d\n",
tools/testing/radix-tree/test.h +2 -0
@@ -34,6 +34,8 @@ void tag_check(void);void multiorder_checks (void );void iteration_test (unsigned order,unsigned duration);void benchmark (void ); +void idr_checks (void ); +void ida_checks (void );struct item *item_tag_set (struct radix_tree_root *root,unsigned long index,int tag);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 33/33 Reimplement IDR and IDA using the radix tree
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
The IDR is very similar to the radix tree. It has some functionality that the radix tree did not have (alloc next free, cyclic allocation, a callback-based for_each, destroy tree), which is readily implementable on top of the radix tree. A few small changes were needed in order to use a tag to represent nodes with free space below them.
The IDA is reimplemented as a client of the newly enhanced radix tree. As in the current implementation, it uses a bitmap at the last level of the tree.
--- include/linux/idr.h | 132 ++-- include/linux/radix-tree.h | 5 +- init/main.c | 3 +- lib/idr.c | 1078 ------------------------------- lib/radix-tree.c | 632 ++++++++++++++++-- tools/testing/radix-tree/Makefile | 5 +- tools/testing/radix-tree/idr.c | 160 +++++ tools/testing/radix-tree/linux/idr.h | 1 + tools/testing/radix-tree/linux/kernel.h | 2 + tools/testing/radix-tree/main.c | 6 + tools/testing/radix-tree/test.h | 2 + 11 files changed, 811 insertions(+), 1215 deletions(-) create mode 100644 tools/testing/radix-tree/idr.c create mode 100644 tools/testing/radix-tree/linux/idr.h
include/linux/idr.h +54 -78
@@ -12,47 +12,22 @@#ifndef __IDR_H__#define __IDR_H__ -#include <linux/types.h> -#include <linux/bitops.h> -#include <linux/init.h> -#include <linux/rcupdate.h> - -/* -* Using 6 bits at each layer allows us to allocate 7 layers out of each page. -* 8 bits only gave us 3 layers out of every pair of pages, which is less -* efficient except for trees with a largest element between 192-255 inclusive. -*/ -#define IDR_BITS 6 -#define IDR_SIZE (1 << IDR_BITS) -#define IDR_MASK ((1 << IDR_BITS)-1) - -struct idr_layer { -int prefix;/* the ID prefix of this idr_layer */ -int layer;/* distance from leaf */ -struct idr_layer __rcu *ary[1 <<IDR_BITS ]; -int count;/* When zero, we can release it */ -union { -/* A zero bit means "space here" */ - DECLARE_BITMAP(bitmap, IDR_SIZE); -struct rcu_head rcu_head; - }; -}; +#include <linux/radix-tree.h> +#include <linux/gfp.h> struct idr { -struct idr_layer __rcu *hint;/* the last layer allocated from */ -struct idr_layer __rcu *top; -int layers;/* only valid w/o concurrent changes */ -int cur;/* current pos for cyclic allocation */ -spinlock_t lock; -int id_free_cnt; -struct idr_layer *id_free; +struct radix_tree_root idr_rt; +unsigned int idr_next; }; -#define IDR_INIT (name) \ +/* Set the IDR flag and the IDR_FREE tag */ +#define IDR_RT_MARKER ((__force gfp_t)(3 << __GFP_BITS_SHIFT)) + +#define IDR_INIT \ { \ - .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ + .idr_rt = RADIX_TREE_INIT(IDR_RT_MARKER) \ } -#define DEFINE_IDR (name) struct idr name = IDR_INIT(name) +#define DEFINE_IDR (name) struct idr name = IDR_INIT/** * idr_get_cursor - Return the current position of the cyclic allocator
@@ -64,7 +39,7 @@ struct idr {*/ static inline unsigned int idr_get_cursor (struct idr *idr) { -return READ_ONCE (idr->cur); +return READ_ONCE (idr->idr_next); }/**
@@ -77,7 +52,7 @@ static inline unsigned int idr_get_cursor(struct idr *idr)*/ static inline void idr_set_cursor (struct idr *idr,unsigned int val) { -WRITE_ONCE (idr->cur, val); +WRITE_ONCE (idr->idr_next, val); }/**
@@ -97,22 +72,30 @@ static inline void idr_set_cursor(struct idr *idr, unsigned int val)* period). */ -/* -* This is what we export. -*/ - -void *idr_find_slowpath (struct idr *idp,int id);void idr_preload (gfp_t gfp_mask); -int idr_alloc (struct idr *idp,void *ptr,int start,int end,gfp_t gfp_mask); -int idr_alloc_cyclic (struct idr *idr,void *ptr,int start,int end,gfp_t gfp_mask); -int idr_for_each (struct idr *idp, +int idr_alloc (struct idr *,void *,int start,int end,gfp_t gfp_mask); +int idr_alloc_cyclic (struct idr *,void *,int start,int end,gfp_t gfp_mask); +int idr_for_each (struct idr *,int (*fn)(int id,void *p,void *data),void *data); -void *idr_get_next (struct idr *idp,int *nextid); -void *idr_replace (struct idr *idp,void *ptr,int id); -void idr_remove (struct idr *idp,int id); -void idr_destroy (struct idr *idp); -void idr_init (struct idr *idp); -bool idr_is_empty (struct idr *idp); +void *idr_get_next (struct idr *,int *nextid); +void *idr_replace (struct idr *,void *,int id); +void idr_destroy (struct idr *); + +static inline void idr_remove (struct idr *idp,int id) +{ +radix_tree_delete (&idp->idr_rt, id); +} + +static inline void idr_init (struct idr *idp) +{ +memset (idp,0 ,sizeof (*idp)); + idp->idr_rt.gfp_mask =IDR_RT_MARKER ; +} + +static inline bool idr_is_empty (struct idr *idp) +{ +return radix_tree_empty (&idp->idr_rt); +}/** * idr_preload_end - end preload section started with idr_preload()
@@ -139,17 +122,12 @@ static inline void idr_preload_end(void)*/ static inline void *idr_find (struct idr *idr,int id) { -struct idr_layer *hint =rcu_dereference_raw (idr->hint); - -if (hint && (id & ~IDR_MASK ) == hint->prefix) -return rcu_dereference_raw (hint->ary[id &IDR_MASK ]); - -return idr_find_slowpath (idr, id); +return radix_tree_lookup (&idr->idr_rt, id); }/** * idr_for_each_entry - iterate over an idr's elements of a given type -* @idp: idr handle +* @idr: idr handle * @entry: the type * to use as cursor * @id: id entry's key *
@@ -157,57 +135,58 @@ static inline void *idr_find(struct idr *idr, int id)* after normal terminatinon @entry is left with the value NULL. This * is convenient for a "not found" value. */ -#define idr_for_each_entry (idp, entry, id) \ - for (id = 0; ((entry) = idr_get_next(idp, &(id))) != NULL; ++id) +#define idr_for_each_entry (idr, entry, id) \ + for (id = 0; ((entry) = idr_get_next(idr, &(id))) != NULL; ++id)/** -* idr_for_each_entry - continue iteration over an idr's elements of a given type -* @idp: idr handle +* idr_for_each_entry_continue - continue iteration over an idr's elements of a given type +* @idr: idr handle * @entry: the type * to use as cursor * @id: id entry's key * * Continue to iterate over list of given type, continuing after * the current position. */ -#define idr_for_each_entry_continue (idp, entry, id) \ - for ((entry) = idr_get_next((idp), &(id)); \ +#define idr_for_each_entry_continue (idr, entry, id) \ + for ((entry) = idr_get_next((idr), &(id)); \ entry; \ - ++id, (entry) = idr_get_next((idp), &(id))) + ++id, (entry) = idr_get_next((idr), &(id)))/* * IDA - IDR based id allocator, use when translation from id to * pointer isn't necessary. -* -* IDA_BITMAP_LONGS is calculated to be one less to accommodate -* ida_bitmap->nr_busy so that the whole struct fits in 128 bytes. */ #define IDA_CHUNK_SIZE 128/* 128 bytes per chunk */ -#define IDA_BITMAP_LONGS (IDA_CHUNK_SIZE / sizeof(long) - 1) +#define IDA_BITMAP_LONGS (IDA_CHUNK_SIZE / sizeof(long))#define IDA_BITMAP_BITS (IDA_BITMAP_LONGS * sizeof(long) * 8)struct ida_bitmap { -long nr_busy;unsigned long bitmap[IDA_BITMAP_LONGS ]; };struct ida { -struct idr idr; +struct radix_tree_root ida_rt;struct ida_bitmap *free_bitmap; }; -#define IDA_INIT (name) { .idr = IDR_INIT((name).idr), .free_bitmap = NULL, } -#define DEFINE_IDA (name) struct ida name = IDA_INIT(name) +#define IDA_INIT { .ida_rt = RADIX_TREE_INIT(IDR_RT_MARKER), } +#define DEFINE_IDA (name) struct ida name = IDA_INITint 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);void ida_destroy (struct ida *ida); -void ida_init (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); +static inline void ida_init (struct ida *ida) +{ +memset (ida,0 ,sizeof (*ida)); + ida->ida_rt.gfp_mask =IDR_RT_MARKER ; +} +/** * ida_get_new - allocate new ID * @ida: idr handle
@@ -222,9 +201,6 @@ static inline int ida_get_new(struct ida *ida, int *p_id)static inline bool ida_is_empty (struct ida *ida) { -return idr_is_empty (&ida->idr); +return radix_tree_empty (&ida->ida_rt); } - -void __init idr_init_cache (void ); -#endif /* __IDR_H__ */
include/linux/radix-tree.h +4 -1
@@ -103,7 +103,10 @@ struct radix_tree_node {unsigned long tags[RADIX_TREE_MAX_TAGS ][RADIX_TREE_TAG_LONGS ]; }; -/* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */ +/* The top bits of gfp_mask are used to store the root tags and the IDR flag */ +#define ROOT_IS_IDR (1 << __GFP_BITS_SHIFT) +#define ROOT_TAG_SHIFT (__GFP_BITS_SHIFT + 1) +struct radix_tree_root {gfp_t gfp_mask;struct radix_tree_node __rcu *rnode;
init/main.c +1 -2
@@ -550,7 +550,7 @@ asmlinkage __visible void __init start_kernel(void)if (WARN (!irqs_disabled (),"Interrupts were enabled *very* early, fixing it\n" ))local_irq_disable (); -idr_init_cache (); +radix_tree_init ();/* * Allow workqueue creation and work item queueing/cancelling
@@ -565,7 +565,6 @@ asmlinkage __visible void __init start_kernel(void)trace_init ();context_tracking_init (); -radix_tree_init ();/* init some links before init_ISA_irqs() */ early_irq_init ();init_IRQ ();
lib/idr.c +0 -1078
1,089 lines not on this page · raw message
lib/radix-tree.c +578 -54
821 lines not on this page · raw message
tools/testing/radix-tree/Makefile +3 -2
@@ -3,7 +3,7 @@ CFLAGS += -I. -I../../include -g -O2 -Wall -D_LGPL_SOURCE LDFLAGS += -lpthread -lurcu TARGETS = main OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \ - regression1.o regression2.o regression3.o multiorder.o \ + regression1.o regression2.o regression3.o multiorder.o idr.o \ iteration_check.o benchmark.o ifdef BENCHMARK
@@ -23,7 +23,8 @@ find_next_bit.o: ../../lib/find_bit.c $(OFILES): *.h */*.h \ ../../include/linux/*.h \ - ../../../include/linux/radix-tree.h + ../../../include/linux/radix-tree.h \ + ../../../include/linux/idr.h radix-tree.c: ../../../lib/radix-tree.c sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@
tools/testing/radix-tree/idr.c new file +160 -0
160 lines not on this page · raw message
tools/testing/radix-tree/linux/idr.h new file +1 -0
@@ -0,0 +1 @@ +#include "../../../../include/linux/idr.h"
tools/testing/radix-tree/linux/kernel.h +2 -0
@@ -30,6 +30,7 @@#define __force#define DIV_ROUND_UP (n,d) (((n) + (d) - 1) / (d))#define pr_debug printk +#define pr_cont printk#define smp_rmb () barrier()#define smp_wmb () barrier()
@@ -41,6 +42,7 @@ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type, member) );})#define min (a, b) ((a) < (b) ? (a) : (b)) +#define max (a, b) ((a) < (b) ? (b) : (a))#define cond_resched () sched_yield()
tools/testing/radix-tree/main.c +6 -0
@@ -3,6 +3,7 @@#include <unistd.h> #include <time.h> #include <assert.h> +#include <limits.h> #include <linux/slab.h> #include <linux/radix-tree.h>
@@ -314,6 +315,11 @@ static void single_thread_tests(bool long_run)rcu_barrier ();printf ("after dynamic_height_check: %d allocated, preempt %d\n" , nr_allocated, preempt_count); +idr_checks (); +ida_checks (); +rcu_barrier (); +printf ("after idr_checks: %d allocated, preempt %d\n" , + nr_allocated, preempt_count);big_gang_check (long_run);rcu_barrier (); printf("after big_gang_check: %d allocated, preempt %d\n",
tools/testing/radix-tree/test.h +2 -0
@@ -34,6 +34,8 @@ void tag_check(void);void multiorder_checks (void );void iteration_test (unsigned order,unsigned duration);void benchmark (void ); +void idr_checks (void ); +void ida_checks (void );struct item *item_tag_set (struct radix_tree_root *root,unsigned long index,int tag);
Signature · 7 lines
2.10.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerRe: [PATCH v3 33/33] Reimplement IDR and IDA using the radix tree
Quoted 10 lines · On Mon, 28 Nov 2016 13:50:37 -0800 Matthew Wilcox <[email protected]>…
The IDR is very similar to the radix tree. It has some functionality that the radix tree did not have (alloc next free, cyclic allocation, a callback-based
for_each, destroy tree), which is readily implementable on top of the radix tree. A few small changes were needed in order to use a tag to represent nodes with free space below them.The IDA is reimplemented as a client of the newly enhanced radix tree. As in the current implementation, it uses a bitmap at the last level of the tree.
Quoted 7 lines
Signed-off-by Matthew Wilcox <[email protected]>--- include/linux/idr.h | 132 ++-- include/linux/radix-tree.h | 5 +- init/main.c | 3 +- lib/idr.c | 1078 ------------------------------- lib/radix-tree.c | 632 ++++++++++++++++--
hm. It's just a cosmetic issue, but perhaps the idr wrappers-around-radix-tree code should be in a different .c file.
Before:
akpm3:/usr/src/25> size lib/idr.o lib/radix-tree.o
text data bss dec hex filename
6566 89 16 6671 1a0f lib/idr.o
11811 117 8 11936 2ea0 lib/radix-tree.o
After:
text data bss dec hex filename
14151 118 8 14277 37c5 lib/radix-tree.o
So 4500 bytes saved. Decent.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
RE: [PATCH v3 33/33] Reimplement IDR and IDA using the radix tree
RnJvbTogQW5kcmV3IE1vcnRvbiBbbWFpbHRvOmFrcG1AbGludXgtZm91bmRhdGlvbi5vcmddDQo+ IE9uIE1vbiwgMjggTm92IDIwMTYgMTM6NTA6MzcgLTA4MDAgTWF0dGhldyBXaWxjb3gNCj4gPG1h d2lsY294QGxpbnV4b25oeXBlcnYuY29tPiB3cm90ZToNCj4gPiAgaW5jbHVkZS9saW51eC9pZHIu aCAgICAgICAgICAgICAgICAgICAgIHwgIDEzMiArKy0tDQo+ID4gIGluY2x1ZGUvbGludXgvcmFk aXgtdHJlZS5oICAgICAgICAgICAgICB8ICAgIDUgKy0NCj4gPiAgaW5pdC9tYWluLmMgICAgICAg ICAgICAgICAgICAgICAgICAgICAgIHwgICAgMyArLQ0KPiA+ICBsaWIvaWRyLmMgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgfCAxMDc4IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t LS0NCj4gPiAgbGliL3JhZGl4LXRyZWUuYyAgICAgICAgICAgICAgICAgICAgICAgIHwgIDYzMiAr KysrKysrKysrKysrKysrLS0NCj4gDQo+IGhtLiAgSXQncyBqdXN0IGEgY29zbWV0aWMgaXNzdWUs IGJ1dCBwZXJoYXBzIHRoZSBpZHINCj4gd3JhcHBlcnMtYXJvdW5kLXJhZGl4LXRyZWUgY29kZSBz aG91bGQgYmUgaW4gYSBkaWZmZXJlbnQgLmMgZmlsZS4NCg0KSSBjYW4gcHV0IHNvbWUgb2YgdGhl bSBiYWNrIGludG8gaWRyLmMgLS0gdGhlcmUncyBhIGNvdXBsZSBvZiByb3V0aW5lcyBsZWZ0IGlu IHRoZXJlIHN0aWxsLCBzbyBhZGRpbmcgc29tZSBtb3JlIHdvbid0IGh1cnQuDQoNCj4gQmVmb3Jl Og0KPiANCj4gYWtwbTM6L3Vzci9zcmMvMjU+IHNpemUgbGliL2lkci5vIGxpYi9yYWRpeC10cmVl Lm8NCj4gICAgdGV4dCAgICBkYXRhICAgICBic3MgICAgIGRlYyAgICAgaGV4IGZpbGVuYW1lDQo+ ICAgIDY1NjYgICAgICA4OSAgICAgIDE2ICAgIDY2NzEgICAgMWEwZiBsaWIvaWRyLm8NCj4gICAx MTgxMSAgICAgMTE3ICAgICAgIDggICAxMTkzNiAgICAyZWEwIGxpYi9yYWRpeC10cmVlLm8NCj4g DQo+IEFmdGVyOg0KPiANCj4gICAgdGV4dCAgICBkYXRhICAgICBic3MgICAgIGRlYyAgICAgaGV4 IGZpbGVuYW1lDQo+ICAgMTQxNTEgICAgIDExOCAgICAgICA4ICAgMTQyNzcgICAgMzdjNSBsaWIv cmFkaXgtdHJlZS5vDQo+IA0KPiANCj4gU28gNDUwMCBieXRlcyBzYXZlZC4gIERlY2VudC4NCg0K Tm90IGJhZC4gIDBkYXkgaGFzIGJlZW4gc2VuZGluZyBtZSBlbWFpbCB0ZWxsaW5nIG1lIHRoYXQg SSBzYXZlZCBzb21ldGhpbmcgbW9yZSBsaWtlIDE1MDAgYnl0ZXMsIGJ1dCB0aGF0J3Mgb24gYSBt aW5pbWFsIGNvbmZpZy4gIChhbHNvIGEgY291cGxlIG9mIHJvdXRpbmVzIHN0YXllZCBpbiBpZHIu Yywgc28geW91J3JlIG92ZXJzdGF0aW5nIGhvdyBtdWNoIEkgc2F2ZWQpDQo=
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerRe: [PATCH v3 33/33] Reimplement IDR and IDA using the radix tree
Quoted 9 lines · On Tue, 6 Dec 2016 21:17:52 +0000 Matthew Wilcox <[email protected]> wrote:
From: Andrew Morton [mailto:[email protected]]
Quoted 8 lines
Quoted 5 lines · On Mon, 28 Nov 2016 13:50:37 -0800 Matthew Wilcox <[email protected]>…
include/linux/idr.h | 132 ++-- include/linux/radix-tree.h | 5 +- init/main.c | 3 +- lib/idr.c | 1078 ------------------------------- lib/radix-tree.c | 632 ++++++++++++++++--
Quoted 4 lines
Quoted 2 lines
hm. It's just a cosmetic issue, but perhaps the idr wrappers-around-radix-tree code should be in a different .c file.
I can put some of them back into
idr.c-- there's a couple of routines left in there still, so adding some more won't hurt.
OK. Sometime. Let's see how this lot pans out as-is for now?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 19 lines · Matthew Wilcox
From: Matthew Wilcox <[email protected]>
Hi Andrew,
Please include these patches in the -mm tree for 4.10. Mostly these are improvements; the only bug fixes in here relate to multiorder entries (which are unused in the 4.9 tree). The IDR rewrite has the highest potential for causing mayhem as the test suite is quite spartan. We have an Outreachy intern scheduled to work on the test suite for the 2016 winter season, so hopefully it will improve soon.
I did not include Konstantin's suggested change to the API for
radix_tree_iter_resume(). Many of the callers do not currently care about the size of the entry they are consuming, and determining that information is not always trivial. Since this is not a performance critical API (it's called when we've paused iterating through a tree in order to schedule for a higher priority task), I think it's more important to have a simpler interface.
Quoted 3 lines
I'd like to thank Kiryl for all the testing he's been doing. He's found at least two bugs which weren't related to the API extensions that he really wanted from this patch set.
I would like to get in as my ext4 patchset depends on this.
Signature · 7 lines
Kirill A. Shutemov -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>