Commit cbc02854 authored by Matthew Wilcox (Oracle)'s avatar Matthew Wilcox (Oracle)
Browse files

XArray: Do not return sibling entries from xa_load()



It is possible for xa_load() to observe a sibling entry pointing to
another sibling entry.  An example:

Thread A:		Thread B:
			xa_store_range(xa, entry, 188, 191, gfp);
xa_load(xa, 191);
entry = xa_entry(xa, node, 63);
[entry is a sibling of 188]
			xa_store_range(xa, entry, 184, 191, gfp);
if (xa_is_sibling(entry))
offset = xa_to_sibling(entry);
entry = xa_entry(xas->xa, node, offset);
[entry is now a sibling of 184]

It is sufficient to go around this loop until we hit a non-sibling entry.
Sibling entries always point earlier in the node, so we are guaranteed
to terminate this search.

Signed-off-by: default avatarMatthew Wilcox (Oracle) <willy@infradead.org>
Fixes: 6b24ca4a ("mm: Use multi-index entries in the page cache")
Cc: stable@vger.kernel.org
parent f837f0a3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -206,7 +206,7 @@ static void *xas_descend(struct xa_state *xas, struct xa_node *node)
	void *entry = xa_entry(xas->xa, node, offset);

	xas->xa_node = node;
	if (xa_is_sibling(entry)) {
	while (xa_is_sibling(entry)) {
		offset = xa_to_sibling(entry);
		entry = xa_entry(xas->xa, node, offset);
		if (node->shift && xa_is_node(entry))
+66 −2
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ void multiorder_tagged_iteration(struct xarray *xa)
	item_kill_tree(xa);
}

bool stop_iteration = false;
bool stop_iteration;

static void *creator_func(void *ptr)
{
@@ -201,6 +201,7 @@ static void multiorder_iteration_race(struct xarray *xa)
	pthread_t worker_thread[num_threads];
	int i;

	stop_iteration = false;
	pthread_create(&worker_thread[0], NULL, &creator_func, xa);
	for (i = 1; i < num_threads; i++)
		pthread_create(&worker_thread[i], NULL, &iterator_func, xa);
@@ -211,6 +212,61 @@ static void multiorder_iteration_race(struct xarray *xa)
	item_kill_tree(xa);
}

static void *load_creator(void *ptr)
{
	/* 'order' is set up to ensure we have sibling entries */
	unsigned int order;
	struct radix_tree_root *tree = ptr;
	int i;

	rcu_register_thread();
	item_insert_order(tree, 3 << RADIX_TREE_MAP_SHIFT, 0);
	item_insert_order(tree, 2 << RADIX_TREE_MAP_SHIFT, 0);
	for (i = 0; i < 10000; i++) {
		for (order = 1; order < RADIX_TREE_MAP_SHIFT; order++) {
			unsigned long index = (3 << RADIX_TREE_MAP_SHIFT) -
						(1 << order);
			item_insert_order(tree, index, order);
			item_delete_rcu(tree, index);
		}
	}
	rcu_unregister_thread();

	stop_iteration = true;
	return NULL;
}

static void *load_worker(void *ptr)
{
	unsigned long index = (3 << RADIX_TREE_MAP_SHIFT) - 1;

	rcu_register_thread();
	while (!stop_iteration) {
		struct item *item = xa_load(ptr, index);
		assert(!xa_is_internal(item));
	}
	rcu_unregister_thread();

	return NULL;
}

static void load_race(struct xarray *xa)
{
	const int num_threads = sysconf(_SC_NPROCESSORS_ONLN) * 4;
	pthread_t worker_thread[num_threads];
	int i;

	stop_iteration = false;
	pthread_create(&worker_thread[0], NULL, &load_creator, xa);
	for (i = 1; i < num_threads; i++)
		pthread_create(&worker_thread[i], NULL, &load_worker, xa);

	for (i = 0; i < num_threads; i++)
		pthread_join(worker_thread[i], NULL);

	item_kill_tree(xa);
}

static DEFINE_XARRAY(array);

void multiorder_checks(void)
@@ -218,12 +274,20 @@ void multiorder_checks(void)
	multiorder_iteration(&array);
	multiorder_tagged_iteration(&array);
	multiorder_iteration_race(&array);
	load_race(&array);

	radix_tree_cpu_dead(0);
}

int __weak main(void)
int __weak main(int argc, char **argv)
{
	int opt;

	while ((opt = getopt(argc, argv, "ls:v")) != -1) {
		if (opt == 'v')
			test_verbose++;
	}

	rcu_register_thread();
	radix_tree_init();
	multiorder_checks();