linux-rdma.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Yishai Hadas <yishaih@nvidia.com>
To: <linux-rdma@vger.kernel.org>
Cc: <jgg@nvidia.com>, <yishaih@nvidia.com>, <maorg@nvidia.com>,
	<markzhang@nvidia.com>, <edwards@nvidia.com>
Subject: [PATCH rdma-core 04/27] util: Add interval_set support
Date: Tue, 20 Jul 2021 11:16:24 +0300	[thread overview]
Message-ID: <20210720081647.1980-5-yishaih@nvidia.com> (raw)
In-Reply-To: <20210720081647.1980-1-yishaih@nvidia.com>

From: Mark Zhang <markzhang@nvidia.com>

Add interval_set functionality to support range management.

This functionality enables to insert/get valid ranges of values and
maintains the contiguity of them internally.

This will be used in down stream patches from this series to set/get
valid iova ranges from this data structure.

Signed-off-by: Mark Zhang <markzhang@nvidia.com>
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
---
 util/CMakeLists.txt |   2 +
 util/interval_set.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 util/interval_set.h |  77 +++++++++++++++++++
 3 files changed, 287 insertions(+)
 create mode 100644 util/interval_set.c
 create mode 100644 util/interval_set.h

diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt
index e8646bf..d8a66be 100644
--- a/util/CMakeLists.txt
+++ b/util/CMakeLists.txt
@@ -1,6 +1,7 @@
 publish_internal_headers(util
   cl_qmap.h
   compiler.h
+  interval_set.h
   node_name_map.h
   rdma_nl.h
   symver.h
@@ -9,6 +10,7 @@ publish_internal_headers(util
 
 set(C_FILES
   cl_map.c
+  interval_set.c
   node_name_map.c
   open_cdev.c
   rdma_nl.c
diff --git a/util/interval_set.c b/util/interval_set.c
new file mode 100644
index 0000000..9fb9bde
--- /dev/null
+++ b/util/interval_set.c
@@ -0,0 +1,208 @@
+/* GPLv2 or OpenIB.org BSD (MIT) See COPYING file */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <ccan/list.h>
+#include <util/interval_set.h>
+#include <util/util.h>
+
+struct iset {
+	struct list_head head;
+	pthread_mutex_t lock;
+};
+
+struct iset_range {
+	struct list_node entry;
+	uint64_t start;
+	uint64_t length;
+};
+
+struct iset *iset_create(void)
+{
+	struct iset *iset;
+
+	iset = calloc(1, sizeof(*iset));
+	if (!iset) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	pthread_mutex_init(&iset->lock, NULL);
+	list_head_init(&iset->head);
+	return iset;
+}
+
+void iset_destroy(struct iset *iset)
+{
+	struct iset_range *range, *tmp;
+
+	list_for_each_safe(&iset->head, range, tmp, entry)
+		free(range);
+
+	free(iset);
+}
+
+static int
+range_overlap(uint64_t s1, uint64_t len1, uint64_t s2, uint64_t len2)
+{
+	if (((s1 < s2) && (s1 + len1 - 1 < s2)) ||
+	    ((s1 > s2) && (s1 > s2 + len2 - 1)))
+		return 0;
+
+	return 1;
+}
+
+static struct iset_range *create_range(uint64_t start, uint64_t length)
+{
+	struct iset_range *range;
+
+	range = calloc(1, sizeof(*range));
+	if (!range) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	range->start = start;
+	range->length = length;
+	return range;
+}
+
+static void delete_range(struct iset_range *r)
+{
+	list_del(&r->entry);
+	free(r);
+}
+
+static bool check_do_combine(struct iset *iset,
+			     struct iset_range *p, struct iset_range *n,
+			     uint64_t start, uint64_t length)
+{
+	bool combined2prev = false, combined2next = false;
+
+	if (p && (p->start + p->length == start)) {
+		p->length += length;
+		combined2prev = true;
+	}
+
+	if (n && (start + length == n->start)) {
+		if (combined2prev) {
+			p->length += n->length;
+			delete_range(n);
+		} else {
+			n->start = start;
+			n->length += length;
+		}
+		combined2next = true;
+	}
+
+	return combined2prev || combined2next;
+}
+
+int iset_insert_range(struct iset *iset, uint64_t start, uint64_t length)
+{
+	struct iset_range *prev = NULL, *r, *rnew;
+	bool found = false, combined;
+	int ret = 0;
+
+	if (!length || (start + length - 1 < start)) {
+		errno = EINVAL;
+		return errno;
+	}
+
+	pthread_mutex_lock(&iset->lock);
+	list_for_each(&iset->head, r, entry) {
+		if (range_overlap(r->start, r->length, start, length)) {
+			errno = EINVAL;
+			ret = errno;
+			goto out;
+		}
+
+		if (r->start > start) {
+			found = true;
+			break;
+		}
+
+		prev = r;
+	}
+
+	combined = check_do_combine(iset, prev, found ? r : NULL,
+				    start, length);
+	if (!combined) {
+		rnew = create_range(start, length);
+		if (!rnew) {
+			ret = errno;
+			goto out;
+		}
+
+		if (!found)
+			list_add_tail(&iset->head, &rnew->entry);
+		else
+			list_add_before(&iset->head, &r->entry, &rnew->entry);
+	}
+
+out:
+	pthread_mutex_unlock(&iset->lock);
+	return ret;
+}
+
+static int power_of_two(uint64_t x)
+{
+	return ((x != 0) && !(x & (x - 1)));
+}
+
+int iset_alloc_range(struct iset *iset, uint64_t length, uint64_t *start)
+{
+	struct iset_range *r, *rnew;
+	uint64_t astart, rend;
+	bool found = false;
+	int ret = 0;
+
+	if (!power_of_two(length)) {
+		errno = EINVAL;
+		return errno;
+	}
+
+	pthread_mutex_lock(&iset->lock);
+	list_for_each(&iset->head, r, entry) {
+		astart = align(r->start, length);
+		/* Check for wrap around */
+		if ((astart + length - 1 >= astart) &&
+		    (astart + length - 1 <= r->start + r->length - 1)) {
+			found = true;
+			break;
+		}
+	}
+	if (!found) {
+		errno = ENOSPC;
+		ret = errno;
+		goto out;
+	}
+
+	if (r->start == astart) {
+		if (r->length == length) { /* Case #1 */
+			delete_range(r);
+		} else {	/* Case #2 */
+			r->start += length;
+			r->length -= length;
+		}
+	} else {
+		rend = r->start + r->length;
+		if (astart + length != rend) { /* Case #4 */
+			rnew = create_range(astart + length,
+					    rend - astart - length);
+			if (!rnew) {
+				ret = errno;
+				goto out;
+			}
+			list_add_after(&iset->head, &r->entry, &rnew->entry);
+		}
+		r->length = astart - r->start; /* Case #3 & #4 */
+	}
+
+	*start = astart;
+out:
+	pthread_mutex_unlock(&iset->lock);
+	return ret;
+}
diff --git a/util/interval_set.h b/util/interval_set.h
new file mode 100644
index 0000000..d5b1f56
--- /dev/null
+++ b/util/interval_set.h
@@ -0,0 +1,77 @@
+/* GPLv2 or OpenIB.org BSD (MIT) See COPYING file */
+
+#include <stdint.h>
+
+struct iset;
+
+/**
+ * iset_create - Create an interval set
+ *
+ * Return the created iset if succeeded, NULL otherwise, with errno set
+ */
+struct iset *iset_create(void);
+
+/**
+ * iset_destroy - Destroy an interval set
+ * @iset: The set to be destroyed
+ */
+void iset_destroy(struct iset *iset);
+
+/**
+ * iset_insert_range - Insert a range to the set
+ * @iset: The set to be operated
+ * @start: The start address of the range
+ * @length: The length of the range
+ *
+ * If this range is continuous to the adjacent ranges (before and/or after),
+ * then they will be combined to a larger one.
+ *
+ * Return 0 if succeeded, errno otherwise
+ */
+int iset_insert_range(struct iset *iset, uint64_t start, uint64_t length);
+
+/**
+ * iset_alloc_range - Allocate a range from the set
+ *
+ * @iset: The set to be operated
+ * @length: The length of the range, must be power of two
+ * @start: The start address of the allocated range, aligned with @length
+ *
+ * Return 0 if succeeded, errno otherwise
+ *
+ * Note: There are these cases:
+ *
+Case 1: Original range is fully taken
++------------------+
+|XXXXXXXXXXXXXXXXXX|
++------------------+
+=>  (NULL)
+
+Case 2: Original range shrunk
++------------------+
+|XXXXX             |
++------------------+
+=>
+      +------------+
+      |            |
+      +------------+
+
+Case 3: Original range shrunk
++------------------+
+|             XXXXX|
++------------------+
+=>
++------------+
+|            |
++------------+
+
+Case 4: Original range splited
++------------------+
+|      XXXXX       |
++------------------+
+=>
++-----+     +------+
+|     |     |      |
++-----+     +------+
+*/
+int iset_alloc_range(struct iset *iset, uint64_t length, uint64_t *start);
-- 
1.8.3.1


  parent reply	other threads:[~2021-07-20  8:17 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-20  8:16 [PATCH rdma-core 00/27] Introduce mlx5 user space driver over VFIO Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 01/27] Update kernel headers Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 02/27] mlx5: Introduce mlx5dv_get_vfio_device_list() Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 03/27] mlx5: Enable debug functionality for vfio Yishai Hadas
2021-07-20  8:51   ` Leon Romanovsky
2021-07-20  9:27     ` Yishai Hadas
2021-07-20 12:27       ` Leon Romanovsky
2021-07-20 14:57         ` Yishai Hadas
2021-07-21  7:05           ` Gal Pressman
2021-07-21  7:58             ` Yishai Hadas
2021-07-21  8:51               ` Gal Pressman
2021-07-20  8:16 ` Yishai Hadas [this message]
2021-07-20  8:16 ` [PATCH rdma-core 05/27] verbs: Enable verbs_open_device() to work over non sysfs devices Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 06/27] mlx5: Setup mlx5 vfio context Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 07/27] mlx5: Add mlx5_vfio_cmd_exec() support Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 08/27] mlx5: vfio setup function support Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 09/27] mlx5: vfio setup basic caps Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 10/27] mlx5: Support fast teardown over vfio Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 11/27] mlx5: Enable interrupt command mode " Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 12/27] mlx5: Introduce vfio APIs to process events Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 13/27] mlx5: VFIO poll_health support Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 14/27] mlx5: Implement basic verbs operation for PD and MR over vfio Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 15/27] mlx5: Set DV context ops Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 16/27] mlx5: Support initial DEVX/DV APIs over vfio Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 17/27] mlx5: Implement mlx5dv devx_obj " Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 18/27] pyverbs: Support DevX UMEM registration Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 19/27] pyverbs/mlx5: Support EQN querying Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 20/27] pyverbs/mlx5: Support more DevX objects Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 21/27] pyverbs: Add auxiliary memory functions Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 22/27] pyverbs/mlx5: Add support to extract mlx5dv objects Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 23/27] pyverbs/mlx5: Wrap mlx5_cqe64 struct and add enums Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 24/27] tests: Add MAC address to the tests' args Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 25/27] tests: Add mlx5 DevX data path test Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 26/27] pyverbs/mlx5: Support mlx5 devices over VFIO Yishai Hadas
2021-07-20  8:16 ` [PATCH rdma-core 27/27] tests: Add a test for mlx5 " Yishai Hadas
2021-08-01  8:00 ` [PATCH rdma-core 00/27] Introduce mlx5 user space driver " Yishai Hadas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210720081647.1980-5-yishaih@nvidia.com \
    --to=yishaih@nvidia.com \
    --cc=edwards@nvidia.com \
    --cc=jgg@nvidia.com \
    --cc=linux-rdma@vger.kernel.org \
    --cc=maorg@nvidia.com \
    --cc=markzhang@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).