@@ -522,12 +522,43 @@ static struct ibv_mem_node *undo_node(struct ibv_mem_node *node,
return node;
}
+/*
+ * This function is being called if madvise() fails.
+ * The node which caused madvise() to fail may contain
+ * just a sub range of [start-end].
+ * So we need to undo all the successful changes (if any)
+ * already performed on a range [start - (node->prev)->end].
+ * This function finds the node to begin rescanning from,
+ * finds the end of the range to rescan and invert
+ * the operation type.
+ */
+static struct ibv_mem_node *prepare_to_roll_back(struct ibv_mem_node *node,
+ uintptr_t start,
+ uintptr_t *end,
+ int *inc,
+ int *advice)
+{
+ struct ibv_mem_node *tmp = NULL;
+
+ *inc *= -1;
+ *advice = *inc == 1 ? MADV_DONTFORK : MADV_DOFORK;
+ tmp = __mm_prev(node);
+ node = NULL;
+ if (tmp) {
+ *end = tmp->end;
+ if (start <= *end)
+ node = get_start_node(start, *end, *inc);
+ }
+ return node;
+}
+
static int ibv_madvise_range(void *base, size_t size, int advice)
{
uintptr_t start, end;
struct ibv_mem_node *node, *tmp;
int inc;
int ret = 0;
+ int rolling_back = 0;
if (!size)
return 0;
@@ -576,7 +607,13 @@ static int ibv_madvise_range(void *base, size_t size, int advice)
advice);
if (ret) {
node = undo_node(node, start, inc);
- goto out;
+ if (rolling_back || !node)
+ goto out;
+
+ node = prepare_to_roll_back(node, start, &end,
+ &inc, &advice);
+ rolling_back = 1;
+ continue;
}
}