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

XArray: Fix xas_pause at ULONG_MAX



If we were unlucky enough to call xas_pause() when the index was at
ULONG_MAX (or a multi-slot entry which ends at ULONG_MAX), we would
wrap the index back around to 0 and restart the iteration from the
beginning.  Use the XAS_BOUNDS state to indicate that we should just
stop the iteration.

Signed-off-by: default avatarMatthew Wilcox (Oracle) <willy@infradead.org>
parent 0058b0a5
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -1132,6 +1132,27 @@ static noinline void check_move_tiny(struct xarray *xa)
	XA_BUG_ON(xa, !xa_empty(xa));
}

static noinline void check_move_max(struct xarray *xa)
{
	XA_STATE(xas, xa, 0);

	xa_store_index(xa, ULONG_MAX, GFP_KERNEL);
	rcu_read_lock();
	XA_BUG_ON(xa, xas_find(&xas, ULONG_MAX) != xa_mk_index(ULONG_MAX));
	XA_BUG_ON(xa, xas_find(&xas, ULONG_MAX) != NULL);
	rcu_read_unlock();

	xas_set(&xas, 0);
	rcu_read_lock();
	XA_BUG_ON(xa, xas_find(&xas, ULONG_MAX) != xa_mk_index(ULONG_MAX));
	xas_pause(&xas);
	XA_BUG_ON(xa, xas_find(&xas, ULONG_MAX) != NULL);
	rcu_read_unlock();

	xa_erase_index(xa, ULONG_MAX);
	XA_BUG_ON(xa, !xa_empty(xa));
}

static noinline void check_move_small(struct xarray *xa, unsigned long idx)
{
	XA_STATE(xas, xa, 0);
@@ -1240,6 +1261,7 @@ static noinline void check_move(struct xarray *xa)
	xa_destroy(xa);

	check_move_tiny(xa);
	check_move_max(xa);

	for (i = 0; i < 16; i++)
		check_move_small(xa, 1UL << i);
+5 −3
Original line number Diff line number Diff line
@@ -967,6 +967,7 @@ void xas_pause(struct xa_state *xas)
	if (xas_invalid(xas))
		return;

	xas->xa_node = XAS_RESTART;
	if (node) {
		unsigned int offset = xas->xa_offset;
		while (++offset < XA_CHUNK_SIZE) {
@@ -974,10 +975,11 @@ void xas_pause(struct xa_state *xas)
				break;
		}
		xas->xa_index += (offset - xas->xa_offset) << node->shift;
		if (xas->xa_index == 0)
			xas->xa_node = XAS_BOUNDS;
	} else {
		xas->xa_index++;
	}
	xas->xa_node = XAS_RESTART;
}
EXPORT_SYMBOL_GPL(xas_pause);

@@ -1079,13 +1081,13 @@ void *xas_find(struct xa_state *xas, unsigned long max)
{
	void *entry;

	if (xas_error(xas))
	if (xas_error(xas) || xas->xa_node == XAS_BOUNDS)
		return NULL;

	if (!xas->xa_node) {
		xas->xa_index = 1;
		return set_bounds(xas);
	} else if (xas_top(xas->xa_node)) {
	} else if (xas->xa_node == XAS_RESTART) {
		entry = xas_load(xas);
		if (entry || xas_not_node(xas->xa_node))
			return entry;