+ /*
+ * From the last internal level node going up, get the node
+ * lock, check if the node has only one child left. If it is the
+ * case, we continue iterating upward. When we reach a node
+ * which has more that one child left, we lock the parent, and
+ * proceed to the node deletion (removing its children too).
+ */
+ for (i = nr_snapshot - 1; i >= 1; i--) {
+ struct cds_ja_shadow_node *shadow_node;
+
+ shadow_node = rcuja_shadow_lookup_lock(ja->ht,
+ ja_node_ptr(snapshot[i]));
+ if (!shadow_node) {
+ ret = -EAGAIN;
+ goto end;
+ }
+ assert(shadow_node->nr_child > 0);
+ shadow_nodes[nr_shadow++] = shadow_node;
+ nr_clear++;
+ if (i == nr_snapshot - 1) {
+ /*
+ * Re-check that last internal node level has
+ * only one child, else trigger a retry.
+ */
+ if (shadow_node->nr_child != 1) {
+ ret = -EAGAIN;
+ goto end;
+ }
+ }
+ if (shadow_node->nr_child > 1 || i == 1) {
+ /* Lock parent and break */
+ shadow_node = rcuja_shadow_lookup_lock(ja->ht,
+ ja_node_ptr(snapshot[i - 1]));
+ if (!shadow_node) {
+ ret = -EAGAIN;
+ goto end;
+ }
+ shadow_nodes[nr_shadow++] = shadow_node;
+ node_flag_ptr = snapshot_ptr[i];
+ n = snapshot_n[i];
+ parent_node_flag_ptr = snapshot_ptr[i - 1];
+ parent_node_flag = snapshot[i - 1];
+ if (i > 1) {
+ /*
+ * Lock parent's parent, in case we need
+ * to recompact parent.
+ */
+ shadow_node = rcuja_shadow_lookup_lock(ja->ht,
+ ja_node_ptr(snapshot[i - 2]));
+ if (!shadow_node) {
+ ret = -EAGAIN;
+ goto end;
+ }
+ shadow_nodes[nr_shadow++] = shadow_node;
+ }
+ break;
+ }
+ }
+
+ /*
+ * At this point, we want to delete all nodes in shadow_nodes
+ * (except the last one, which is either the root or the parent
+ * of the upmost node with 1 child). OK to as to free lock here,
+ * because RCU read lock is held, and free only performed in
+ * call_rcu.
+ */
+
+ for (i = 0; i < nr_clear; i++) {
+ ret = rcuja_shadow_clear(ja->ht,
+ shadow_nodes[i]->node,
+ shadow_nodes[i],
+ RCUJA_SHADOW_CLEAR_FREE_NODE
+ | RCUJA_SHADOW_CLEAR_FREE_LOCK);
+ assert(!ret);
+ }
+
+ iter_node_flag = parent_node_flag;
+ /* Remove from parent */
+ ret = ja_node_clear_ptr(ja,
+ node_flag_ptr, /* Pointer to location to nullify */
+ &iter_node_flag, /* Old new parent ptr in its parent */
+ shadow_nodes[nr_clear], /* of parent */
+ n);
+
+ /* Update address of parent ptr in its parent */
+ rcu_assign_pointer(*parent_node_flag_ptr, iter_node_flag);
+
+end:
+ for (i = 0; i < nr_shadow; i++)
+ rcuja_shadow_unlock(shadow_nodes[i]);
+ return ret;
+}
+
+static
+int ja_unchain_node(struct cds_ja *ja,
+ struct cds_ja_inode_flag *parent_node_flag,
+ struct cds_ja_node *node)
+{
+ struct cds_ja_shadow_node *shadow_node;
+ int ret = 0;
+
+ shadow_node = rcuja_shadow_lookup_lock(ja->ht,
+ ja_node_ptr(parent_node_flag));
+ if (!shadow_node)
+ return -EAGAIN;
+ /*
+ * Retry if another thread removed all but one of duplicates
+ * since check.
+ */
+ if (shadow_node->nr_child == 1) {
+ ret = -EAGAIN;
+ goto end;
+ }
+ cds_hlist_del_rcu(&node->list);
+end:
+ rcuja_shadow_unlock(shadow_node);