rcuja range: add private data to allocated ranges
[userspace-rcu.git] / rcuja / rcuja-range.c
index e8214e2f6b1b8e6e244c9b78ac8d8fafe0399e1a..cb5b78b8b8e63365c79a72fae76b2e8f6177f237 100644 (file)
  *   succeed (still assuming no concurrent deletion).
  */
 
+/*
+ * Discussion: concurrent deletion of contiguous allocated ranges.
+ *
+ * Ensuring that merge of contiguous free ranges is always performed, we
+ * need to ensure locking of concurrent removal of contiguous allocated
+ * ranges one with respect to another. This is done by locking the
+ * ranges prior to and after the range to remove, even if that range is
+ * allocated. This serializes removal of contiguous ranges. The only
+ * cases for which there is no range to lock is when removing an
+ * allocated range starting at 0, and/or ending at the end of the key
+ * space.
+ */
+
 /*
  * Discussion of the type state transitions.
  *
  * A range type never changes otherwise.
  */
 
+#define CDS_JA_RANGE_KEY_BITS  64
+
+enum cds_ja_range_type {
+       CDS_JA_RANGE_ALLOCATED,
+       CDS_JA_RANGE_FREE,
+       CDS_JA_RANGE_REMOVED,
+};
+
+/*
+ * Range goes from start (inclusive) to end (inclusive).
+ * Range start is used as node key in the Judy array.
+ */
+struct cds_ja_range {
+       uint64_t end;
+       struct cds_ja_node ja_node;
+       pthread_mutex_t lock;
+       void *priv;
+       enum cds_ja_range_type type;
+
+       /* not required on lookup fast-path */
+       uint64_t start;
+       struct rcu_head head;
+};
 
 struct cds_ja_range *cds_ja_range_lookup(struct cds_ja *ja, uint64_t key)
 {
@@ -105,7 +141,7 @@ struct cds_ja_range *cds_ja_range_lookup(struct cds_ja *ja, uint64_t key)
                return NULL;
        /*
         * We found an allocated range. We can return it for use with
-        * RCU read-side prototection for existence. However, we have no
+        * RCU read-side protection for existence. However, we have no
         * mutual exclusion against removal at this point.
         */
        return range;
@@ -136,6 +172,7 @@ static
 struct cds_ja_range *range_create(
                uint64_t start,         /* inclusive */
                uint64_t end,           /* inclusive */
+               void *priv,
                enum cds_ja_range_type type)
 {
        struct cds_ja_range *range;
@@ -145,6 +182,7 @@ struct cds_ja_range *range_create(
                return NULL;
        range->start = start;
        range->end = end;
+       range->priv = priv;
        range->type = type;
        pthread_mutex_init(&range->lock, NULL);
        return range;
@@ -173,7 +211,8 @@ void rcu_free_range(struct cds_ja *ja, struct cds_ja_range *range)
 
 struct cds_ja_range *cds_ja_range_add(struct cds_ja *ja,
                uint64_t start,         /* inclusive */
-               uint64_t end)           /* inclusive */
+               uint64_t end,           /* inclusive */
+               void *priv)
 {
        struct cds_ja_node *old_node, *old_node_end;
        struct cds_ja_range *old_range, *old_range_end, *new_range, *ranges[3];
@@ -206,9 +245,6 @@ retry:
                switch (CMM_LOAD_SHARED(old_range->type)) {
                case CDS_JA_RANGE_ALLOCATED:
                case CDS_JA_RANGE_FREE:         /* fall-through */
-                       /*
-                        * TODO: think about free range merge.
-                        */
                        return NULL;
                case CDS_JA_RANGE_REMOVED:
                        goto retry;
@@ -227,32 +263,32 @@ retry:
                if (end == old_range->end) {
                        /* 1 range */
                        ranges[0] = new_range = range_create(start, end,
-                               CDS_JA_RANGE_ALLOCATED);
+                               priv, CDS_JA_RANGE_ALLOCATED);
                        nr_ranges = 1;
                } else {
                        /* 2 ranges */
                        ranges[0] = new_range = range_create(start, end,
-                               CDS_JA_RANGE_ALLOCATED);
+                               priv, CDS_JA_RANGE_ALLOCATED);
                        ranges[1] = range_create(end + 1, old_range->end,
-                               CDS_JA_RANGE_FREE);
+                               NULL, CDS_JA_RANGE_FREE);
                        nr_ranges = 2;
                }
        } else {
                if (end == old_range->end) {
                        /* 2 ranges */
                        ranges[0] = range_create(old_range->start, start - 1,
-                               CDS_JA_RANGE_FREE);
+                               NULL, CDS_JA_RANGE_FREE);
                        ranges[1] = new_range = range_create(start, end,
-                               CDS_JA_RANGE_ALLOCATED);
+                               priv, CDS_JA_RANGE_ALLOCATED);
                        nr_ranges = 2;
                } else {
                        /* 3 ranges */
                        ranges[0] = range_create(old_range->start, start - 1,
-                               CDS_JA_RANGE_FREE);
+                               NULL, CDS_JA_RANGE_FREE);
                        ranges[1] = new_range = range_create(start, end,
-                               CDS_JA_RANGE_ALLOCATED);
+                               priv, CDS_JA_RANGE_ALLOCATED);
                        ranges[2] = range_create(end + 1, old_range->end,
-                               CDS_JA_RANGE_FREE);
+                               NULL, CDS_JA_RANGE_FREE);
                        nr_ranges = 3;
                }
        }
@@ -285,39 +321,53 @@ retry:
 int cds_ja_range_del(struct cds_ja *ja, struct cds_ja_range *range)
 {
        struct cds_ja_node *prev_node, *next_node;
-       struct cds_ja_range *prev_range, *next_range, *new_range;
-       struct cds_ja_range *merge_ranges[3];
-       unsigned int nr_merge, i;
+       struct cds_ja_range *new_range;
+       struct cds_ja_range *merge_ranges[3], *lock_ranges[3];
+       unsigned int nr_merge, nr_lock, i;
        uint64_t start, end;
        int ret;
 
 retry:
        nr_merge = 0;
+       nr_lock = 0;
        prev_node = cds_ja_lookup_below_equal(ja, range->start - 1, NULL);
-       prev_range = caa_container_of(prev_node, struct cds_ja_range, ja_node);
-       if (prev_node && prev_range->type != CDS_JA_RANGE_ALLOCATED)
-               merge_ranges[nr_merge++] = prev_range;
+       if (prev_node) {
+               struct cds_ja_range *prev_range;
 
+               prev_range = caa_container_of(prev_node,
+                       struct cds_ja_range, ja_node);
+               lock_ranges[nr_lock++] = prev_range;
+               if (prev_range->type != CDS_JA_RANGE_ALLOCATED)
+                       merge_ranges[nr_merge++] = prev_range;
+       }
+
+       lock_ranges[nr_lock++] = range;
        merge_ranges[nr_merge++] = range;
 
        next_node = cds_ja_lookup_above_equal(ja, range->end + 1, NULL);
-       next_range = caa_container_of(next_node, struct cds_ja_range, ja_node);
-       if (next_node && next_range->type != CDS_JA_RANGE_ALLOCATED)
-               merge_ranges[nr_merge++] = next_range;
+       if (next_node) {
+               struct cds_ja_range *next_range;
+
+               next_range = caa_container_of(next_node,
+                       struct cds_ja_range, ja_node);
+               lock_ranges[nr_lock++] = next_range;
+               if (next_range->type != CDS_JA_RANGE_ALLOCATED)
+                       merge_ranges[nr_merge++] = next_range;
+       }
 
        /* Acquire locks in increasing key order for range merge */
-       for (i = 0; i < nr_merge; i++)
-               pthread_mutex_lock(&merge_ranges[i]->lock);
+       for (i = 0; i < nr_lock; i++)
+               pthread_mutex_lock(&lock_ranges[i]->lock);
        /* Ensure they are valid */
-       for (i = 0; i < nr_merge; i++) {
-               if (merge_ranges[i]->type == CDS_JA_RANGE_REMOVED)
+       for (i = 0; i < nr_lock; i++) {
+               if (lock_ranges[i]->type == CDS_JA_RANGE_REMOVED)
                        goto unlock_retry;
        }
 
        /* Create new free range */
        start = merge_ranges[0]->start;
        end = merge_ranges[nr_merge - 1]->end;
-       new_range = range_create(start, end, CDS_JA_RANGE_FREE);
+       new_range = range_create(start, end, NULL, CDS_JA_RANGE_FREE);
        ret = cds_ja_add(ja, start, &new_range->ja_node);
        assert(!ret);
 
@@ -327,39 +377,49 @@ retry:
                                &merge_ranges[i]->ja_node);
                assert(!ret);
                merge_ranges[i]->type = CDS_JA_RANGE_REMOVED;
-               pthread_mutex_unlock(&merge_ranges[i]->lock);
-               rcu_free_range(ja, merge_ranges[i]);
        }
+       for (i = 0; i < nr_lock; i++)
+               pthread_mutex_unlock(&lock_ranges[i]->lock);
+       /* Free old merged ranges */
+       for (i = 0; i < nr_merge; i++)
+               rcu_free_range(ja, merge_ranges[i]);
 
        return 0;
 
        /* retry paths */
 unlock_retry:
-       for (i = 0; i < nr_merge; i++)
-               pthread_mutex_unlock(&merge_ranges[i]->lock);
+       for (i = 0; i < nr_lock; i++)
+               pthread_mutex_unlock(&lock_ranges[i]->lock);
        goto retry;
 }
 
-int cds_ja_range_init(struct cds_ja *ja)
+struct cds_ja *_cds_ja_range_new(const struct rcu_flavor_struct *flavor)
 {
-       struct cds_ja_node *node;
        struct cds_ja_range *range;
+       struct cds_ja *ja;
        int ret;
 
-       /*
-        * Sanity check: should be empty.
-        */
-       node = cds_ja_lookup_above_equal(ja, 0, NULL);
-       if (node)
-               return -EINVAL;
-       range = range_create(0, UINT64_MAX, CDS_JA_RANGE_FREE);
+       ja = _cds_ja_new(CDS_JA_RANGE_KEY_BITS, flavor);
+       if (!ja)
+               return NULL;
+       range = range_create(0, UINT64_MAX, NULL, CDS_JA_RANGE_FREE);
        if (!range)
-               return -EINVAL;
+               goto free_ja;
        ret = cds_ja_add(ja, 0, &range->ja_node);
-       return ret;
+       if (ret)
+               goto free_range;
+       return ja;
+
+free_range:
+       free_range(range);
+free_ja:
+       ret = cds_ja_destroy(ja);
+       assert(!ret);
+       return NULL;
 }
 
-int cds_ja_range_fini(struct cds_ja *ja)
+int cds_ja_range_destroy(struct cds_ja *ja,
+               void (*free_priv)(void *ptr))
 {
        uint64_t key;
        struct cds_ja_node *ja_node;
@@ -374,13 +434,16 @@ int cds_ja_range_fini(struct cds_ja *ja)
                        range = caa_container_of(ja_node,
                                        struct cds_ja_range, ja_node);
                        ret = cds_ja_del(ja, key, &range->ja_node);
-                       if (ret) {
-                               goto end;
-                       }
+                       if (ret)
+                               goto error;
+                       if (free_priv)
+                               free_priv(range->priv);
                        /* Alone using Judy array, OK to free now */
                        free_range(range);
                }
        }
-end:
+       return cds_ja_destroy(ja);
+
+error:
        return ret;
 }
This page took 0.027507 seconds and 4 git commands to generate.