2481 lines · 63.7 KB · C
RawDownload
		xas->xa_node = parent;		xas->xa_offset = node->offset;		xa_node_free(node); 		if (!parent) {			xas->xa->xa_head = NULL;			xas->xa_node = XAS_BOUNDS;			return;		} 		parent->slots[xas->xa_offset] = NULL;		parent->count--;		XA_NODE_BUG_ON(parent, parent->count > XA_CHUNK_SIZE);		node = parent;		xas_update(xas, node);	} 	if (!node->parent)		xas_shrink(xas);} /** * xas_free_nodes() - Free this node and all nodes that it references * @xas: Array operation state. * @top: Node to free * * This node has been removed from the tree.  We must now free it and all * of its subnodes.  There may be RCU walkers with references into the tree, * so we must replace all entries with retry markers. */static void xas_free_nodes(struct xa_state *xas, struct xa_node *top){	unsigned int offset = 0;	struct xa_node *node = top; 	for (;;) {		void *entry = xa_entry_locked(xas->xa, node, offset); 		if (node->shift && xa_is_node(entry)) {			node = xa_to_node(entry);			offset = 0;			continue;		}		if (entry)			RCU_INIT_POINTER(node->slots[offset], XA_RETRY_ENTRY);		offset++;		while (offset == XA_CHUNK_SIZE) {			struct xa_node *parent; 			parent = xa_parent_locked(xas->xa, node);			offset = node->offset + 1;			node->count = 0;			node->nr_values = 0;			xas_update(xas, node);			xa_node_free(node);			if (node == top)				return;			node = parent;		}	}} /* * xas_expand adds nodes to the head of the tree until it has reached * sufficient height to be able to contain @xas->xa_index */static int xas_expand(struct xa_state *xas, void *head){	struct xarray *xa = xas->xa;	struct xa_node *node = NULL;	unsigned int shift = 0;	unsigned long max = xas_max(xas); 	if (!head) {		if (max == 0)			return 0;		while ((max >> shift) >= XA_CHUNK_SIZE)			shift += XA_CHUNK_SHIFT;		return shift + XA_CHUNK_SHIFT;	} else if (xa_is_node(head)) {		node = xa_to_node(head);		shift = node->shift + XA_CHUNK_SHIFT;	}	xas->xa_node = NULL; 	while (max > max_index(head)) {		xa_mark_t mark = 0; 		XA_NODE_BUG_ON(node, shift > BITS_PER_LONG);		node = xas_alloc(xas, shift);		if (!node)			return -ENOMEM; 		node->count = 1;		if (xa_is_value(head))			node->nr_values = 1;		RCU_INIT_POINTER(node->slots[0], head); 		/* Propagate the aggregated mark info to the new child */		for (;;) {			if (xa_track_free(xa) && mark == XA_FREE_MARK) {				node_mark_all(node, XA_FREE_MARK);				if (!xa_marked(xa, XA_FREE_MARK)) {					node_clear_mark(node, 0, XA_FREE_MARK);					xa_mark_set(xa, XA_FREE_MARK);				}			} else if (xa_marked(xa, mark)) {				node_set_mark(node, 0, mark);			}			if (mark == XA_MARK_MAX)				break;			mark_inc(mark);		} 		/*		 * Now that the new node is fully initialised, we can add		 * it to the tree		 */		if (xa_is_node(head)) {			xa_to_node(head)->offset = 0;			rcu_assign_pointer(xa_to_node(head)->parent, node);		}		head = xa_mk_node(node);		rcu_assign_pointer(xa->xa_head, head);		xas_update(xas, node); 		shift += XA_CHUNK_SHIFT;	} 	xas->xa_node = node;	return shift;} /* * xas_create() - Create a slot to store an entry in. * @xas: XArray operation state. * @allow_root: %true if we can store the entry in the root directly * * Most users will not need to call this function directly, as it is called * by xas_store().  It is useful for doing conditional store operations * (see the xa_cmpxchg() implementation for an example). * * Return: If the slot already existed, returns the contents of this slot. * If the slot was newly created, returns %NULL.  If it failed to create the * slot, returns %NULL and indicates the error in @xas. */static void *xas_create(struct xa_state *xas, bool allow_root){	struct xarray *xa = xas->xa;	void *entry;	void __rcu **slot;	struct xa_node *node = xas->xa_node;	int shift;	unsigned int order = xas->xa_shift; 	if (xas_top(node)) {		entry = xa_head_locked(xa);		xas->xa_node = NULL;		if (!entry && xa_zero_busy(xa))			entry = XA_ZERO_ENTRY;		shift = xas_expand(xas, entry);		if (shift < 0)			return NULL;		if (!shift && !allow_root)			shift = XA_CHUNK_SHIFT;		entry = xa_head_locked(xa);		slot = &xa->xa_head;	} else if (xas_error(xas)) {		return NULL;	} else if (node) {		unsigned int offset = xas->xa_offset; 		shift = node->shift;		entry = xa_entry_locked(xa, node, offset);		slot = &node->slots[offset];	} else {		shift = 0;		entry = xa_head_locked(xa);		slot = &xa->xa_head;	} 	while (shift > order) {		shift -= XA_CHUNK_SHIFT;		if (!entry) {			node = xas_alloc(xas, shift);			if (!node)				break;			if (xa_track_free(xa))				node_mark_all(node, XA_FREE_MARK);			rcu_assign_pointer(*slot, xa_mk_node(node));		} else if (xa_is_node(entry)) {			node = xa_to_node(entry);		} else {			break;		}		entry = xas_descend(xas, node);		slot = &node->slots[xas->xa_offset];	} 	return entry;} /** * xas_create_range() - Ensure that stores to this range will succeed * @xas: XArray operation state. * * Creates all of the slots in the range covered by @xas.  Sets @xas to * create single-index entries and positions it at the beginning of the * range.  This is for the benefit of users which have not yet been * converted to use multi-index entries. */void xas_create_range(struct xa_state *xas){	unsigned long index = xas->xa_index;	unsigned char shift = xas->xa_shift;	unsigned char sibs = xas->xa_sibs; 	xas->xa_index |= ((sibs + 1UL) << shift) - 1;	if (xas_is_node(xas) && xas->xa_node->shift == xas->xa_shift)		xas->xa_offset |= sibs;	xas->xa_shift = 0;	xas->xa_sibs = 0; 	for (;;) {		xas_create(xas, true);		if (xas_error(xas))			goto restore;		if (xas->xa_index <= (index | XA_CHUNK_MASK))			goto success;		xas->xa_index -= XA_CHUNK_SIZE; 		for (;;) {			struct xa_node *node = xas->xa_node;			if (node->shift >= shift)				break;			xas->xa_node = xa_parent_locked(xas->xa, node);			xas->xa_offset = node->offset - 1;			if (node->offset != 0)				break;		}	} restore:	xas->xa_shift = shift;	xas->xa_sibs = sibs;	xas->xa_index = index;	return;success:	xas->xa_index = index;	if (xas->xa_node)		xas_set_offset(xas);}EXPORT_SYMBOL_GPL(xas_create_range); static void update_node(struct xa_state *xas, struct xa_node *node,		int count, int values){	if (!node || (!count && !values))		return; 	node->count += count;	node->nr_values += values;	XA_NODE_BUG_ON(node, node->count > XA_CHUNK_SIZE);	XA_NODE_BUG_ON(node, node->nr_values > XA_CHUNK_SIZE);	xas_update(xas, node);	if (count < 0)		xas_delete_node(xas);} /** * xas_store() - Store this entry in the XArray. * @xas: XArray operation state. * @entry: New entry. * * If @xas is operating on a multi-index entry, the entry returned by this * function is essentially meaningless (it may be an internal entry or it * may be %NULL, even if there are non-NULL entries at some of the indices * covered by the range).  This is not a problem for any current users, * and can be changed if needed. * * Return: The old entry at this index. */void *xas_store(struct xa_state *xas, void *entry){	struct xa_node *node;	void __rcu **slot = &xas->xa->xa_head;	unsigned int offset, max;	int count = 0;	int values = 0;	void *first, *next;	bool value = xa_is_value(entry); 	if (entry) {		bool allow_root = !xa_is_node(entry) && !xa_is_zero(entry);		first = xas_create(xas, allow_root);	} else {		first = xas_load(xas);	} 	if (xas_invalid(xas))		return first;	node = xas->xa_node;	if (node && (xas->xa_shift < node->shift))		xas->xa_sibs = 0;	if ((first == entry) && !xas->xa_sibs)		return first; 	next = first;	offset = xas->xa_offset;	max = xas->xa_offset + xas->xa_sibs;	if (node) {		slot = &node->slots[offset];		if (xas->xa_sibs)			xas_squash_marks(xas);	}	if (!entry)		xas_init_marks(xas); 	for (;;) {		/*		 * Must clear the marks before setting the entry to NULL,		 * otherwise xas_for_each_marked may find a NULL entry and		 * stop early.  rcu_assign_pointer contains a release barrier		 * so the mark clearing will appear to happen before the		 * entry is set to NULL.		 */		rcu_assign_pointer(*slot, entry);		if (xa_is_node(next) && (!node || node->shift))			xas_free_nodes(xas, xa_to_node(next));		if (!node)			break;		count += !next - !entry;		values += !xa_is_value(first) - !value;		if (entry) {			if (offset == max)				break;			if (!xa_is_sibling(entry))				entry = xa_mk_sibling(xas->xa_offset);		} else {			if (offset == XA_CHUNK_MASK)				break;		}		next = xa_entry_locked(xas->xa, node, ++offset);		if (!xa_is_sibling(next)) {			if (!entry && (offset > max))				break;			first = next;		}		slot++;	} 	update_node(xas, node, count, values);	return first;}EXPORT_SYMBOL_GPL(xas_store); /** * xas_get_mark() - Returns the state of this mark. * @xas: XArray operation state. * @mark: Mark number. * * Return: true if the mark is set, false if the mark is clear or @xas * is in an error state. */bool xas_get_mark(const struct xa_state *xas, xa_mark_t mark){	if (xas_invalid(xas))		return false;	if (!xas->xa_node)		return xa_marked(xas->xa, mark);	return node_get_mark(xas->xa_node, xas->xa_offset, mark);}EXPORT_SYMBOL_GPL(xas_get_mark); /** * xas_set_mark() - Sets the mark on this entry and its parents. * @xas: XArray operation state. * @mark: Mark number. * * Sets the specified mark on this entry, and walks up the tree setting it * on all the ancestor entries.  Does nothing if @xas has not been walked to * an entry, or is in an error state. */void xas_set_mark(const struct xa_state *xas, xa_mark_t mark){	struct xa_node *node = xas->xa_node;	unsigned int offset = xas->xa_offset; 	if (xas_invalid(xas))		return; 	while (node) {		if (node_set_mark(node, offset, mark))			return;		offset = node->offset;		node = xa_parent_locked(xas->xa, node);	} 	if (!xa_marked(xas->xa, mark))		xa_mark_set(xas->xa, mark);}EXPORT_SYMBOL_GPL(xas_set_mark); /** * xas_clear_mark() - Clears the mark on this entry and its parents. * @xas: XArray operation state. * @mark: Mark number. * * Clears the specified mark on this entry, and walks back to the head * attempting to clear it on all the ancestor entries.  Does nothing if * @xas has not been walked to an entry, or is in an error state. */void xas_clear_mark(const struct xa_state *xas, xa_mark_t mark){	struct xa_node *node = xas->xa_node;	unsigned int offset = xas->xa_offset; 	if (xas_invalid(xas))		return; 	while (node) {		if (!node_clear_mark(node, offset, mark))			return;		if (node_any_mark(node, mark))			return; 		offset = node->offset;		node = xa_parent_locked(xas->xa, node);	} 	if (xa_marked(xas->xa, mark))		xa_mark_clear(xas->xa, mark);}EXPORT_SYMBOL_GPL(xas_clear_mark); /** * xas_init_marks() - Initialise all marks for the entry * @xas: Array operations state. * * Initialise all marks for the entry specified by @xas.  If we're tracking * free entries with a mark, we need to set it on all entries.  All other * marks are cleared. * * This implementation is not as efficient as it could be; we may walk * up the tree multiple times. */void xas_init_marks(const struct xa_state *xas){	xa_mark_t mark = 0; 	for (;;) {		if (xa_track_free(xas->xa) && mark == XA_FREE_MARK)			xas_set_mark(xas, mark);		else			xas_clear_mark(xas, mark);		if (mark == XA_MARK_MAX)			break;		mark_inc(mark);	}}EXPORT_SYMBOL_GPL(xas_init_marks); #ifdef CONFIG_XARRAY_MULTIstatic unsigned int node_get_marks(struct xa_node *node, unsigned int offset){	unsigned int marks = 0;	xa_mark_t mark = XA_MARK_0; 	for (;;) {		if (node_get_mark(node, offset, mark))			marks |= 1 << (__force unsigned int)mark;		if (mark == XA_MARK_MAX)			break;		mark_inc(mark);	} 	return marks;} static inline void node_mark_slots(struct xa_node *node, unsigned int sibs,		xa_mark_t mark){	int i; 	if (sibs == 0)		node_mark_all(node, mark);	else {		for (i = 0; i < XA_CHUNK_SIZE; i += sibs + 1)			node_set_mark(node, i, mark);	}} static void node_set_marks(struct xa_node *node, unsigned int offset,			struct xa_node *child, unsigned int sibs,			unsigned int marks){	xa_mark_t mark = XA_MARK_0; 	for (;;) {		if (marks & (1 << (__force unsigned int)mark)) {

Lines 501–1000 of 2481. Previous lines