162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <asm/bug.h>
362306a36Sopenharmony_ci#include <linux/rbtree_augmented.h>
462306a36Sopenharmony_ci#include "drbd_interval.h"
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/*
762306a36Sopenharmony_ci * interval_end  -  return end of @node
862306a36Sopenharmony_ci */
962306a36Sopenharmony_cistatic inline
1062306a36Sopenharmony_cisector_t interval_end(struct rb_node *node)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	struct drbd_interval *this = rb_entry(node, struct drbd_interval, rb);
1362306a36Sopenharmony_ci	return this->end;
1462306a36Sopenharmony_ci}
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define NODE_END(node) ((node)->sector + ((node)->size >> 9))
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciRB_DECLARE_CALLBACKS_MAX(static, augment_callbacks,
1962306a36Sopenharmony_ci			 struct drbd_interval, rb, sector_t, end, NODE_END);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * drbd_insert_interval  -  insert a new interval into a tree
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_cibool
2562306a36Sopenharmony_cidrbd_insert_interval(struct rb_root *root, struct drbd_interval *this)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct rb_node **new = &root->rb_node, *parent = NULL;
2862306a36Sopenharmony_ci	sector_t this_end = this->sector + (this->size >> 9);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	BUG_ON(!IS_ALIGNED(this->size, 512));
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	while (*new) {
3362306a36Sopenharmony_ci		struct drbd_interval *here =
3462306a36Sopenharmony_ci			rb_entry(*new, struct drbd_interval, rb);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		parent = *new;
3762306a36Sopenharmony_ci		if (here->end < this_end)
3862306a36Sopenharmony_ci			here->end = this_end;
3962306a36Sopenharmony_ci		if (this->sector < here->sector)
4062306a36Sopenharmony_ci			new = &(*new)->rb_left;
4162306a36Sopenharmony_ci		else if (this->sector > here->sector)
4262306a36Sopenharmony_ci			new = &(*new)->rb_right;
4362306a36Sopenharmony_ci		else if (this < here)
4462306a36Sopenharmony_ci			new = &(*new)->rb_left;
4562306a36Sopenharmony_ci		else if (this > here)
4662306a36Sopenharmony_ci			new = &(*new)->rb_right;
4762306a36Sopenharmony_ci		else
4862306a36Sopenharmony_ci			return false;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	this->end = this_end;
5262306a36Sopenharmony_ci	rb_link_node(&this->rb, parent, new);
5362306a36Sopenharmony_ci	rb_insert_augmented(&this->rb, root, &augment_callbacks);
5462306a36Sopenharmony_ci	return true;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/**
5862306a36Sopenharmony_ci * drbd_contains_interval  -  check if a tree contains a given interval
5962306a36Sopenharmony_ci * @root:	red black tree root
6062306a36Sopenharmony_ci * @sector:	start sector of @interval
6162306a36Sopenharmony_ci * @interval:	may be an invalid pointer
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * Returns if the tree contains the node @interval with start sector @start.
6462306a36Sopenharmony_ci * Does not dereference @interval until @interval is known to be a valid object
6562306a36Sopenharmony_ci * in @tree.  Returns %false if @interval is in the tree but with a different
6662306a36Sopenharmony_ci * sector number.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cibool
6962306a36Sopenharmony_cidrbd_contains_interval(struct rb_root *root, sector_t sector,
7062306a36Sopenharmony_ci		       struct drbd_interval *interval)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct rb_node *node = root->rb_node;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	while (node) {
7562306a36Sopenharmony_ci		struct drbd_interval *here =
7662306a36Sopenharmony_ci			rb_entry(node, struct drbd_interval, rb);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		if (sector < here->sector)
7962306a36Sopenharmony_ci			node = node->rb_left;
8062306a36Sopenharmony_ci		else if (sector > here->sector)
8162306a36Sopenharmony_ci			node = node->rb_right;
8262306a36Sopenharmony_ci		else if (interval < here)
8362306a36Sopenharmony_ci			node = node->rb_left;
8462306a36Sopenharmony_ci		else if (interval > here)
8562306a36Sopenharmony_ci			node = node->rb_right;
8662306a36Sopenharmony_ci		else
8762306a36Sopenharmony_ci			return true;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	return false;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/*
9362306a36Sopenharmony_ci * drbd_remove_interval  -  remove an interval from a tree
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_civoid
9662306a36Sopenharmony_cidrbd_remove_interval(struct rb_root *root, struct drbd_interval *this)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	/* avoid endless loop */
9962306a36Sopenharmony_ci	if (drbd_interval_empty(this))
10062306a36Sopenharmony_ci		return;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	rb_erase_augmented(&this->rb, root, &augment_callbacks);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/**
10662306a36Sopenharmony_ci * drbd_find_overlap  - search for an interval overlapping with [sector, sector + size)
10762306a36Sopenharmony_ci * @root:	red black tree root
10862306a36Sopenharmony_ci * @sector:	start sector
10962306a36Sopenharmony_ci * @size:	size, aligned to 512 bytes
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * Returns an interval overlapping with [sector, sector + size), or NULL if
11262306a36Sopenharmony_ci * there is none.  When there is more than one overlapping interval in the
11362306a36Sopenharmony_ci * tree, the interval with the lowest start sector is returned, and all other
11462306a36Sopenharmony_ci * overlapping intervals will be on the right side of the tree, reachable with
11562306a36Sopenharmony_ci * rb_next().
11662306a36Sopenharmony_ci */
11762306a36Sopenharmony_cistruct drbd_interval *
11862306a36Sopenharmony_cidrbd_find_overlap(struct rb_root *root, sector_t sector, unsigned int size)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct rb_node *node = root->rb_node;
12162306a36Sopenharmony_ci	struct drbd_interval *overlap = NULL;
12262306a36Sopenharmony_ci	sector_t end = sector + (size >> 9);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	BUG_ON(!IS_ALIGNED(size, 512));
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	while (node) {
12762306a36Sopenharmony_ci		struct drbd_interval *here =
12862306a36Sopenharmony_ci			rb_entry(node, struct drbd_interval, rb);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		if (node->rb_left &&
13162306a36Sopenharmony_ci		    sector < interval_end(node->rb_left)) {
13262306a36Sopenharmony_ci			/* Overlap if any must be on left side */
13362306a36Sopenharmony_ci			node = node->rb_left;
13462306a36Sopenharmony_ci		} else if (here->sector < end &&
13562306a36Sopenharmony_ci			   sector < here->sector + (here->size >> 9)) {
13662306a36Sopenharmony_ci			overlap = here;
13762306a36Sopenharmony_ci			break;
13862306a36Sopenharmony_ci		} else if (sector >= here->sector) {
13962306a36Sopenharmony_ci			/* Overlap if any must be on right side */
14062306a36Sopenharmony_ci			node = node->rb_right;
14162306a36Sopenharmony_ci		} else
14262306a36Sopenharmony_ci			break;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci	return overlap;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistruct drbd_interval *
14862306a36Sopenharmony_cidrbd_next_overlap(struct drbd_interval *i, sector_t sector, unsigned int size)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	sector_t end = sector + (size >> 9);
15162306a36Sopenharmony_ci	struct rb_node *node;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	for (;;) {
15462306a36Sopenharmony_ci		node = rb_next(&i->rb);
15562306a36Sopenharmony_ci		if (!node)
15662306a36Sopenharmony_ci			return NULL;
15762306a36Sopenharmony_ci		i = rb_entry(node, struct drbd_interval, rb);
15862306a36Sopenharmony_ci		if (i->sector >= end)
15962306a36Sopenharmony_ci			return NULL;
16062306a36Sopenharmony_ci		if (sector < i->sector + (i->size >> 9))
16162306a36Sopenharmony_ci			return i;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci}
164