Commit 01305db8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'xarray-5.2-rc6' of git://git.infradead.org/users/willy/linux-dax

Pull XArray fixes from Matthew Wilcox:

 - Account XArray nodes for the page cache to the appropriate cgroup
   (Johannes Weiner)

 - Fix idr_get_next() when called under the RCU lock (Matthew Wilcox)

 - Add a test for xa_insert() (Matthew Wilcox)

* tag 'xarray-5.2-rc6' of git://git.infradead.org/users/willy/linux-dax:
  XArray tests: Add check_insert
  idr: Fix idr_get_next race with idr_remove
  mm: fix page cache convergence regression
parents 0839c537 12fd2aee
...@@ -362,7 +362,7 @@ EXPORT_SYMBOL(inc_nlink); ...@@ -362,7 +362,7 @@ EXPORT_SYMBOL(inc_nlink);
static void __address_space_init_once(struct address_space *mapping) static void __address_space_init_once(struct address_space *mapping)
{ {
xa_init_flags(&mapping->i_pages, XA_FLAGS_LOCK_IRQ); xa_init_flags(&mapping->i_pages, XA_FLAGS_LOCK_IRQ | XA_FLAGS_ACCOUNT);
init_rwsem(&mapping->i_mmap_rwsem); init_rwsem(&mapping->i_mmap_rwsem);
INIT_LIST_HEAD(&mapping->private_list); INIT_LIST_HEAD(&mapping->private_list);
spin_lock_init(&mapping->private_lock); spin_lock_init(&mapping->private_lock);
......
...@@ -265,6 +265,7 @@ enum xa_lock_type { ...@@ -265,6 +265,7 @@ enum xa_lock_type {
#define XA_FLAGS_TRACK_FREE ((__force gfp_t)4U) #define XA_FLAGS_TRACK_FREE ((__force gfp_t)4U)
#define XA_FLAGS_ZERO_BUSY ((__force gfp_t)8U) #define XA_FLAGS_ZERO_BUSY ((__force gfp_t)8U)
#define XA_FLAGS_ALLOC_WRAPPED ((__force gfp_t)16U) #define XA_FLAGS_ALLOC_WRAPPED ((__force gfp_t)16U)
#define XA_FLAGS_ACCOUNT ((__force gfp_t)32U)
#define XA_FLAGS_MARK(mark) ((__force gfp_t)((1U << __GFP_BITS_SHIFT) << \ #define XA_FLAGS_MARK(mark) ((__force gfp_t)((1U << __GFP_BITS_SHIFT) << \
(__force unsigned)(mark))) (__force unsigned)(mark)))
......
...@@ -228,11 +228,21 @@ void *idr_get_next(struct idr *idr, int *nextid) ...@@ -228,11 +228,21 @@ void *idr_get_next(struct idr *idr, int *nextid)
{ {
struct radix_tree_iter iter; struct radix_tree_iter iter;
void __rcu **slot; void __rcu **slot;
void *entry = NULL;
unsigned long base = idr->idr_base; unsigned long base = idr->idr_base;
unsigned long id = *nextid; unsigned long id = *nextid;
id = (id < base) ? 0 : id - base; id = (id < base) ? 0 : id - base;
slot = radix_tree_iter_find(&idr->idr_rt, &iter, id); radix_tree_for_each_slot(slot, &idr->idr_rt, &iter, id) {
entry = rcu_dereference_raw(*slot);
if (!entry)
continue;
if (!xa_is_internal(entry))
break;
if (slot != &idr->idr_rt.xa_head && !xa_is_retry(entry))
break;
slot = radix_tree_iter_retry(&iter);
}
if (!slot) if (!slot)
return NULL; return NULL;
id = iter.index + base; id = iter.index + base;
...@@ -241,7 +251,7 @@ void *idr_get_next(struct idr *idr, int *nextid) ...@@ -241,7 +251,7 @@ void *idr_get_next(struct idr *idr, int *nextid)
return NULL; return NULL;
*nextid = id; *nextid = id;
return rcu_dereference_raw(*slot); return entry;
} }
EXPORT_SYMBOL(idr_get_next); EXPORT_SYMBOL(idr_get_next);
......
...@@ -38,6 +38,12 @@ static void *xa_store_index(struct xarray *xa, unsigned long index, gfp_t gfp) ...@@ -38,6 +38,12 @@ static void *xa_store_index(struct xarray *xa, unsigned long index, gfp_t gfp)
return xa_store(xa, index, xa_mk_index(index), gfp); return xa_store(xa, index, xa_mk_index(index), gfp);
} }
static void xa_insert_index(struct xarray *xa, unsigned long index)
{
XA_BUG_ON(xa, xa_insert(xa, index, xa_mk_index(index),
GFP_KERNEL) != 0);
}
static void xa_alloc_index(struct xarray *xa, unsigned long index, gfp_t gfp) static void xa_alloc_index(struct xarray *xa, unsigned long index, gfp_t gfp)
{ {
u32 id; u32 id;
...@@ -338,6 +344,37 @@ static noinline void check_xa_shrink(struct xarray *xa) ...@@ -338,6 +344,37 @@ static noinline void check_xa_shrink(struct xarray *xa)
} }
} }
static noinline void check_insert(struct xarray *xa)
{
unsigned long i;
for (i = 0; i < 1024; i++) {
xa_insert_index(xa, i);
XA_BUG_ON(xa, xa_load(xa, i - 1) != NULL);
XA_BUG_ON(xa, xa_load(xa, i + 1) != NULL);
xa_erase_index(xa, i);
}
for (i = 10; i < BITS_PER_LONG; i++) {
xa_insert_index(xa, 1UL << i);
XA_BUG_ON(xa, xa_load(xa, (1UL << i) - 1) != NULL);
XA_BUG_ON(xa, xa_load(xa, (1UL << i) + 1) != NULL);
xa_erase_index(xa, 1UL << i);
xa_insert_index(xa, (1UL << i) - 1);
XA_BUG_ON(xa, xa_load(xa, (1UL << i) - 2) != NULL);
XA_BUG_ON(xa, xa_load(xa, 1UL << i) != NULL);
xa_erase_index(xa, (1UL << i) - 1);
}
xa_insert_index(xa, ~0UL);
XA_BUG_ON(xa, xa_load(xa, 0UL) != NULL);
XA_BUG_ON(xa, xa_load(xa, ~1UL) != NULL);
xa_erase_index(xa, ~0UL);
XA_BUG_ON(xa, !xa_empty(xa));
}
static noinline void check_cmpxchg(struct xarray *xa) static noinline void check_cmpxchg(struct xarray *xa)
{ {
void *FIVE = xa_mk_value(5); void *FIVE = xa_mk_value(5);
...@@ -1527,6 +1564,7 @@ static int xarray_checks(void) ...@@ -1527,6 +1564,7 @@ static int xarray_checks(void)
check_xa_mark(&array); check_xa_mark(&array);
check_xa_shrink(&array); check_xa_shrink(&array);
check_xas_erase(&array); check_xas_erase(&array);
check_insert(&array);
check_cmpxchg(&array); check_cmpxchg(&array);
check_reserve(&array); check_reserve(&array);
check_reserve(&xa0); check_reserve(&xa0);
......
...@@ -298,6 +298,8 @@ bool xas_nomem(struct xa_state *xas, gfp_t gfp) ...@@ -298,6 +298,8 @@ bool xas_nomem(struct xa_state *xas, gfp_t gfp)
xas_destroy(xas); xas_destroy(xas);
return false; return false;
} }
if (xas->xa->xa_flags & XA_FLAGS_ACCOUNT)
gfp |= __GFP_ACCOUNT;
xas->xa_alloc = kmem_cache_alloc(radix_tree_node_cachep, gfp); xas->xa_alloc = kmem_cache_alloc(radix_tree_node_cachep, gfp);
if (!xas->xa_alloc) if (!xas->xa_alloc)
return false; return false;
...@@ -325,6 +327,8 @@ static bool __xas_nomem(struct xa_state *xas, gfp_t gfp) ...@@ -325,6 +327,8 @@ static bool __xas_nomem(struct xa_state *xas, gfp_t gfp)
xas_destroy(xas); xas_destroy(xas);
return false; return false;
} }
if (xas->xa->xa_flags & XA_FLAGS_ACCOUNT)
gfp |= __GFP_ACCOUNT;
if (gfpflags_allow_blocking(gfp)) { if (gfpflags_allow_blocking(gfp)) {
xas_unlock_type(xas, lock_type); xas_unlock_type(xas, lock_type);
xas->xa_alloc = kmem_cache_alloc(radix_tree_node_cachep, gfp); xas->xa_alloc = kmem_cache_alloc(radix_tree_node_cachep, gfp);
...@@ -358,8 +362,12 @@ static void *xas_alloc(struct xa_state *xas, unsigned int shift) ...@@ -358,8 +362,12 @@ static void *xas_alloc(struct xa_state *xas, unsigned int shift)
if (node) { if (node) {
xas->xa_alloc = NULL; xas->xa_alloc = NULL;
} else { } else {
node = kmem_cache_alloc(radix_tree_node_cachep, gfp_t gfp = GFP_NOWAIT | __GFP_NOWARN;
GFP_NOWAIT | __GFP_NOWARN);
if (xas->xa->xa_flags & XA_FLAGS_ACCOUNT)
gfp |= __GFP_ACCOUNT;
node = kmem_cache_alloc(radix_tree_node_cachep, gfp);
if (!node) { if (!node) {
xas_set_err(xas, -ENOMEM); xas_set_err(xas, -ENOMEM);
return NULL; return NULL;
......
...@@ -279,6 +279,51 @@ static void idr_align_test(struct idr *idr) ...@@ -279,6 +279,51 @@ static void idr_align_test(struct idr *idr)
} }
} }
DEFINE_IDR(find_idr);
static void *idr_throbber(void *arg)
{
time_t start = time(NULL);
int id = *(int *)arg;
rcu_register_thread();
do {
idr_alloc(&find_idr, xa_mk_value(id), id, id + 1, GFP_KERNEL);
idr_remove(&find_idr, id);
} while (time(NULL) < start + 10);
rcu_unregister_thread();
return NULL;
}
void idr_find_test_1(int anchor_id, int throbber_id)
{
pthread_t throbber;
time_t start = time(NULL);
pthread_create(&throbber, NULL, idr_throbber, &throbber_id);
BUG_ON(idr_alloc(&find_idr, xa_mk_value(anchor_id), anchor_id,
anchor_id + 1, GFP_KERNEL) != anchor_id);
do {
int id = 0;
void *entry = idr_get_next(&find_idr, &id);
BUG_ON(entry != xa_mk_value(id));
} while (time(NULL) < start + 11);
pthread_join(throbber, NULL);
idr_remove(&find_idr, anchor_id);
BUG_ON(!idr_is_empty(&find_idr));
}
void idr_find_test(void)
{
idr_find_test_1(100000, 0);
idr_find_test_1(0, 100000);
}
void idr_checks(void) void idr_checks(void)
{ {
unsigned long i; unsigned long i;
...@@ -360,6 +405,7 @@ void idr_checks(void) ...@@ -360,6 +405,7 @@ void idr_checks(void)
idr_u32_test(1); idr_u32_test(1);
idr_u32_test(0); idr_u32_test(0);
idr_align_test(&idr); idr_align_test(&idr);
idr_find_test();
} }
#define module_init(x) #define module_init(x)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment