18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <asm/bug.h> 38c2ecf20Sopenharmony_ci#include <linux/rbtree_augmented.h> 48c2ecf20Sopenharmony_ci#include "drbd_interval.h" 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/** 78c2ecf20Sopenharmony_ci * interval_end - return end of @node 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_cistatic inline 108c2ecf20Sopenharmony_cisector_t interval_end(struct rb_node *node) 118c2ecf20Sopenharmony_ci{ 128c2ecf20Sopenharmony_ci struct drbd_interval *this = rb_entry(node, struct drbd_interval, rb); 138c2ecf20Sopenharmony_ci return this->end; 148c2ecf20Sopenharmony_ci} 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define NODE_END(node) ((node)->sector + ((node)->size >> 9)) 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciRB_DECLARE_CALLBACKS_MAX(static, augment_callbacks, 198c2ecf20Sopenharmony_ci struct drbd_interval, rb, sector_t, end, NODE_END); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/** 228c2ecf20Sopenharmony_ci * drbd_insert_interval - insert a new interval into a tree 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cibool 258c2ecf20Sopenharmony_cidrbd_insert_interval(struct rb_root *root, struct drbd_interval *this) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct rb_node **new = &root->rb_node, *parent = NULL; 288c2ecf20Sopenharmony_ci sector_t this_end = this->sector + (this->size >> 9); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci BUG_ON(!IS_ALIGNED(this->size, 512)); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci while (*new) { 338c2ecf20Sopenharmony_ci struct drbd_interval *here = 348c2ecf20Sopenharmony_ci rb_entry(*new, struct drbd_interval, rb); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci parent = *new; 378c2ecf20Sopenharmony_ci if (here->end < this_end) 388c2ecf20Sopenharmony_ci here->end = this_end; 398c2ecf20Sopenharmony_ci if (this->sector < here->sector) 408c2ecf20Sopenharmony_ci new = &(*new)->rb_left; 418c2ecf20Sopenharmony_ci else if (this->sector > here->sector) 428c2ecf20Sopenharmony_ci new = &(*new)->rb_right; 438c2ecf20Sopenharmony_ci else if (this < here) 448c2ecf20Sopenharmony_ci new = &(*new)->rb_left; 458c2ecf20Sopenharmony_ci else if (this > here) 468c2ecf20Sopenharmony_ci new = &(*new)->rb_right; 478c2ecf20Sopenharmony_ci else 488c2ecf20Sopenharmony_ci return false; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci this->end = this_end; 528c2ecf20Sopenharmony_ci rb_link_node(&this->rb, parent, new); 538c2ecf20Sopenharmony_ci rb_insert_augmented(&this->rb, root, &augment_callbacks); 548c2ecf20Sopenharmony_ci return true; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/** 588c2ecf20Sopenharmony_ci * drbd_contains_interval - check if a tree contains a given interval 598c2ecf20Sopenharmony_ci * @sector: start sector of @interval 608c2ecf20Sopenharmony_ci * @interval: may not be a valid pointer 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * Returns if the tree contains the node @interval with start sector @start. 638c2ecf20Sopenharmony_ci * Does not dereference @interval until @interval is known to be a valid object 648c2ecf20Sopenharmony_ci * in @tree. Returns %false if @interval is in the tree but with a different 658c2ecf20Sopenharmony_ci * sector number. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cibool 688c2ecf20Sopenharmony_cidrbd_contains_interval(struct rb_root *root, sector_t sector, 698c2ecf20Sopenharmony_ci struct drbd_interval *interval) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct rb_node *node = root->rb_node; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci while (node) { 748c2ecf20Sopenharmony_ci struct drbd_interval *here = 758c2ecf20Sopenharmony_ci rb_entry(node, struct drbd_interval, rb); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (sector < here->sector) 788c2ecf20Sopenharmony_ci node = node->rb_left; 798c2ecf20Sopenharmony_ci else if (sector > here->sector) 808c2ecf20Sopenharmony_ci node = node->rb_right; 818c2ecf20Sopenharmony_ci else if (interval < here) 828c2ecf20Sopenharmony_ci node = node->rb_left; 838c2ecf20Sopenharmony_ci else if (interval > here) 848c2ecf20Sopenharmony_ci node = node->rb_right; 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci return true; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci return false; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/** 928c2ecf20Sopenharmony_ci * drbd_remove_interval - remove an interval from a tree 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_civoid 958c2ecf20Sopenharmony_cidrbd_remove_interval(struct rb_root *root, struct drbd_interval *this) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci rb_erase_augmented(&this->rb, root, &augment_callbacks); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/** 1018c2ecf20Sopenharmony_ci * drbd_find_overlap - search for an interval overlapping with [sector, sector + size) 1028c2ecf20Sopenharmony_ci * @sector: start sector 1038c2ecf20Sopenharmony_ci * @size: size, aligned to 512 bytes 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * Returns an interval overlapping with [sector, sector + size), or NULL if 1068c2ecf20Sopenharmony_ci * there is none. When there is more than one overlapping interval in the 1078c2ecf20Sopenharmony_ci * tree, the interval with the lowest start sector is returned, and all other 1088c2ecf20Sopenharmony_ci * overlapping intervals will be on the right side of the tree, reachable with 1098c2ecf20Sopenharmony_ci * rb_next(). 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_cistruct drbd_interval * 1128c2ecf20Sopenharmony_cidrbd_find_overlap(struct rb_root *root, sector_t sector, unsigned int size) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct rb_node *node = root->rb_node; 1158c2ecf20Sopenharmony_ci struct drbd_interval *overlap = NULL; 1168c2ecf20Sopenharmony_ci sector_t end = sector + (size >> 9); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci BUG_ON(!IS_ALIGNED(size, 512)); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci while (node) { 1218c2ecf20Sopenharmony_ci struct drbd_interval *here = 1228c2ecf20Sopenharmony_ci rb_entry(node, struct drbd_interval, rb); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (node->rb_left && 1258c2ecf20Sopenharmony_ci sector < interval_end(node->rb_left)) { 1268c2ecf20Sopenharmony_ci /* Overlap if any must be on left side */ 1278c2ecf20Sopenharmony_ci node = node->rb_left; 1288c2ecf20Sopenharmony_ci } else if (here->sector < end && 1298c2ecf20Sopenharmony_ci sector < here->sector + (here->size >> 9)) { 1308c2ecf20Sopenharmony_ci overlap = here; 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci } else if (sector >= here->sector) { 1338c2ecf20Sopenharmony_ci /* Overlap if any must be on right side */ 1348c2ecf20Sopenharmony_ci node = node->rb_right; 1358c2ecf20Sopenharmony_ci } else 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci return overlap; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistruct drbd_interval * 1428c2ecf20Sopenharmony_cidrbd_next_overlap(struct drbd_interval *i, sector_t sector, unsigned int size) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci sector_t end = sector + (size >> 9); 1458c2ecf20Sopenharmony_ci struct rb_node *node; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (;;) { 1488c2ecf20Sopenharmony_ci node = rb_next(&i->rb); 1498c2ecf20Sopenharmony_ci if (!node) 1508c2ecf20Sopenharmony_ci return NULL; 1518c2ecf20Sopenharmony_ci i = rb_entry(node, struct drbd_interval, rb); 1528c2ecf20Sopenharmony_ci if (i->sector >= end) 1538c2ecf20Sopenharmony_ci return NULL; 1548c2ecf20Sopenharmony_ci if (sector < i->sector + (i->size >> 9)) 1558c2ecf20Sopenharmony_ci return i; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci} 158