]> git.openfabrics.org - ~shefty/libibverbs.git/commitdiff
Make fork() work for verbs consumers
authorRoland Dreier <rolandd@cisco.com>
Thu, 3 Aug 2006 16:45:03 +0000 (16:45 +0000)
committerRoland Dreier <rolandd@cisco.com>
Thu, 9 Nov 2006 19:36:01 +0000 (11:36 -0800)
Add code to libibvers that uses madvise(..., MADV_DONTFORK) to make
fork() work for verbs consumers.

Signed-off-by: Roland Dreier <rolandd@cisco.com>
ChangeLog
README
include/infiniband/driver.h
include/infiniband/verbs.h
src/ibverbs.h
src/init.c
src/libibverbs.map
src/memory.c
src/verbs.c

index 9ce79696f6a63456bb645b2c6d33fa4c8b0a8655..b1edd458b37caab47c132b2dfce6a46f784180d6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2006-07-26  Roland Dreier  <rdreier@cisco.com>
+
+       * src/verbs.c (ibv_reg_mr, ibv_dereg_mr): Add calls to
+       ibv_dontfork_range() and ibv_dofork_range() for memory regions
+       registered by library consumers.
+
+       * include/infiniband/verbs.h: Add declaration of ibv_fork_init().
+
+       * include/infiniband/driver.h: Add declarations of
+       ibv_dontfork_range() and ibv_dofork_range().
+
+       * src/memory.c: Rewrite to use a red-black tree instead of a
+       linked list.  Change from doing mlock()/munlock() to
+       madvise(..., MADV_DONTFORK) and madvise(..., MADV_DOFORK), and
+       change the name of the entry points to ibv_dontfork_range() and
+       ibv_dofork_range().  Add ibv_fork_init() for applications to
+       request fork-safe behavior.
+
+       * src/ibverbs.h: Kill off unused declarations.
+
+       * src/init.c (ibverbs_init): Get rid of call to ibv_init_mem_map().
+
+       * include/infiniband/verbs.h: Add addr and length field to struct
+       ibv_mr so that memory regions can be madvised().  This changes the
+       ABI, since the layout of struct ibv_mr is changed.
+
 2006-07-04  Roland Dreier  <rdreier@cisco.com>
 
        * include/infiniband/arch.h: Fix typo in sparc mb()
diff --git a/README b/README
index 2e1c00c8fe70fdea6241f3b8288e0f4ed6edd35d..38bf61419e8f2710bc0e80fcbc8d4d4bc1a396d8 100644 (file)
--- a/README
+++ b/README
@@ -101,12 +101,6 @@ necessary permissions to release your work.
 TODO
 ====
 
-1.0 series
-----------
-
- * Use the MADV_DONTFORK advice for madvise(2) to make applications
-   that use fork(2) work better.
-
 1.1 series
 ----------
 
index 8db8bdbb11cfb85110b99e1a49a2df3cebe1f79d..494f906a04a760b74c0084f7cc5acf628fc45324 100644 (file)
@@ -135,6 +135,9 @@ int ibv_cmd_destroy_ah(struct ibv_ah *ah);
 int ibv_cmd_attach_mcast(struct ibv_qp *qp, union ibv_gid *gid, uint16_t lid);
 int ibv_cmd_detach_mcast(struct ibv_qp *qp, union ibv_gid *gid, uint16_t lid);
 
+int ibv_dontfork_range(void *base, size_t size);
+int ibv_dofork_range(void *base, size_t size);
+
 /*
  * sysfs helper functions
  */
index 9898f84a8350394fa86bc3355ea82bcce1b56aad..26a7291ca52f3e6712cb969b2a238eaf27ef7896 100644 (file)
@@ -285,6 +285,8 @@ struct ibv_pd {
 struct ibv_mr {
        struct ibv_context     *context;
        struct ibv_pd          *pd;
+       void                   *addr;
+       size_t                  length;
        uint32_t                handle;
        uint32_t                lkey;
        uint32_t                rkey;
@@ -1016,6 +1018,14 @@ int ibv_attach_mcast(struct ibv_qp *qp, union ibv_gid *gid, uint16_t lid);
  */
 int ibv_detach_mcast(struct ibv_qp *qp, union ibv_gid *gid, uint16_t lid);
 
+/**
+ * ibv_fork_init - Prepare data structures so that fork() may be used
+ * safely.  If this function is not called or returns a non-zero
+ * status, then libibverbs data structures are not fork()-safe and the
+ * effect of an application calling fork() is undefined.
+ */
+int ibv_fork_init(void);
+
 END_C_DECLS
 
 #  undef __attribute_const
index 2849da770b546a7ab166c74a96514788cac940d3..02fae7cf5e8021cb3b6ddb93e5491f651cf279b9 100644 (file)
@@ -58,11 +58,7 @@ struct ibv_abi_compat_v2 {
 
 extern HIDDEN int abi_ver;
 
-extern HIDDEN int ibverbs_init(struct ibv_device ***list);
-
-extern HIDDEN int ibv_init_mem_map(void);
-extern HIDDEN int ibv_lock_range(void *base, size_t size);
-extern HIDDEN int ibv_unlock_range(void *base, size_t size);
+HIDDEN int ibverbs_init(struct ibv_device ***list);
 
 #define IBV_INIT_CMD(cmd, size, opcode)                                        \
        do {                                                            \
index 04dfd9b23d7d1445b23afe201d93928325fc3ee2..448624fb3416baa20e05c0334601b1e2aa927163 100644 (file)
@@ -205,8 +205,10 @@ HIDDEN int ibverbs_init(struct ibv_device ***list)
 
        *list = NULL;
 
-       if (ibv_init_mem_map())
-               return 0;
+       if (getenv("RDMAV_FORK_SAFE") || getenv("IBV_FORK_SAFE"))
+               if (ibv_fork_init())
+                       fprintf(stderr, PFX "Warning: fork()-safety requested "
+                               "but init failed\n");
 
        find_drivers(default_path);
 
index 9c7e48b13db16911b0d6c8a9ed451ca38ff15ed1..aeb707a5f5e6f7b14945f8a2734b908ba5d4a9cd 100644 (file)
@@ -74,6 +74,9 @@ IBVERBS_1.0 {
                mult_to_ibv_rate;
                ibv_get_sysfs_path;
                ibv_read_sysfs_file;
+               ibv_fork_init;
+               ibv_dontfork_range;
+               ibv_dofork_range;
 
        local: *;
 };
index 81b1e757db9b918178b1ad97488c517341a179b2..8b09781c6529d816f8418c70e277452e8ba0e3ad 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ * Copyright (c) 2006 Cisco Systems, Inc.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -36,6 +37,7 @@
 #  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
+#include <errno.h>
 #include <sys/mman.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include "ibverbs.h"
 
 /*
- * We keep a linked list of page ranges that have been locked along with a
- * reference count to manage overlapping registrations, etc.
- *
- * Eventually we should turn this into an RB-tree or something similar
- * to avoid the O(n) cost of registering/unregistering memory.
+ * Most distro's headers don't have these yet.
  */
+#ifndef MADV_DONTFORK
+#define MADV_DONTFORK  10
+#endif
+
+#ifndef MADV_DOFORK
+#define MADV_DOFORK    11
+#endif
 
 struct ibv_mem_node {
-       struct ibv_mem_node *prev, *next;
-       uintptr_t            start, end;
-       int                  refcnt;
+       enum {
+               IBV_RED,
+               IBV_BLACK
+       }                       color;
+       struct ibv_mem_node    *parent;
+       struct ibv_mem_node    *left, *right;
+       uintptr_t               start, end;
+       int                     refcnt;
 };
 
-static struct {
-       struct ibv_mem_node *first;
-       pthread_mutex_t      mutex;
-       uintptr_t            page_size;
-} mem_map;
+static struct ibv_mem_node *mm_root;
+static pthread_mutex_t mm_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int page_size;
+static int too_late;
 
-int ibv_init_mem_map(void)
+int ibv_fork_init(void)
 {
-       struct ibv_mem_node *node = NULL;
+       void *tmp;
 
-       node = malloc(sizeof *node);
-       if (!node)
-               goto fail;
+       if (mm_root)
+               return 0;
 
-       node->prev   = node->next = NULL;
-       node->start  = 0;
-       node->end    = UINTPTR_MAX;
-       node->refcnt = 0;
+       if (too_late)
+               return EINVAL;
 
-       mem_map.first = node;
+       page_size = sysconf(_SC_PAGESIZE);
+       if (page_size < 0)
+               return errno;
 
-       mem_map.page_size = sysconf(_SC_PAGESIZE);
-       if (mem_map.page_size < 0)
-               goto fail;
+       if (posix_memalign(&tmp, page_size, page_size))
+               return ENOMEM;
 
-       if (pthread_mutex_init(&mem_map.mutex, NULL))
-               goto fail;
+       if (madvise(tmp, page_size, MADV_DONTFORK) ||
+           madvise(tmp, page_size, MADV_DOFORK))
+               return ENOSYS;
 
-       return 0;
+       free(tmp);
+
+       mm_root = malloc(sizeof *mm_root);
+       if (!mm_root)
+               return ENOMEM;
 
-fail:
-       if (node)
-               free(node);
+       mm_root->parent = NULL;
+       mm_root->left   = NULL;
+       mm_root->right  = NULL;
+       mm_root->color  = IBV_BLACK;
+       mm_root->start  = 0;
+       mm_root->end    = UINTPTR_MAX;
+       mm_root->refcnt = 0;
 
-       return -1;
+       return 0;
 }
 
-static struct ibv_mem_node *__mm_find_first(uintptr_t start, uintptr_t end)
+static struct ibv_mem_node *__mm_prev(struct ibv_mem_node *node)
 {
-       struct ibv_mem_node *node = mem_map.first;
+       if (node->left) {
+               node = node->left;
+               while (node->right)
+                       node = node->right;
+       } else {
+               while (node->parent && node == node->parent->left)
+                       node = node->parent;
+
+               node = node->parent;
+       }
 
-       while (node) {
-               if ((node->start <= start && node->end >= start) ||
-                   (node->start <= end   && node->end >= end))
-                       break;
-               node = node->next;
+       return node;
+}
+
+static struct ibv_mem_node *__mm_next(struct ibv_mem_node *node)
+{
+       if (node->right) {
+               node = node->right;
+               while (node->left)
+                       node = node->left;
+       } else {
+               while (node->parent && node == node->parent->right)
+                       node = node->parent;
+
+               node = node->parent;
        }
 
        return node;
 }
 
-static struct ibv_mem_node *__mm_prev(struct ibv_mem_node *node)
+static void __mm_rotate_right(struct ibv_mem_node *node)
 {
-       return node->prev;
+       struct ibv_mem_node *tmp;
+
+       tmp = node->left;
+
+       node->left = tmp->right;
+       if (node->left)
+               node->left->parent = node;
+
+       if (node->parent) {
+               if (node->parent->right == node)
+                       node->parent->right = tmp;
+               else
+                       node->parent->left = tmp;
+       } else
+               mm_root = tmp;
+
+       tmp->parent = node->parent;
+
+       tmp->right = node;
+       node->parent = tmp;
 }
 
-static struct ibv_mem_node *__mm_next(struct ibv_mem_node *node)
+static void __mm_rotate_left(struct ibv_mem_node *node)
+{
+       struct ibv_mem_node *tmp;
+
+       tmp = node->right;
+
+       node->right = tmp->left;
+       if (node->right)
+               node->right->parent = node;
+
+       if (node->parent) {
+               if (node->parent->right == node)
+                       node->parent->right = tmp;
+               else
+                       node->parent->left = tmp;
+       } else
+               mm_root = tmp;
+
+       tmp->parent = node->parent;
+
+       tmp->left = node;
+       node->parent = tmp;
+}
+
+static int verify(struct ibv_mem_node *node)
 {
-       return node->next;
+       int hl, hr;
+
+       if (!node)
+               return 1;
+
+       hl = verify(node->left);
+       hr = verify(node->left);
+
+       if (!hl || !hr)
+               return 0;
+       if (hl != hr)
+               return 0;
+
+       if (node->color == IBV_RED) {
+               if (node->left && node->left->color != IBV_BLACK)
+                       return 0;
+               if (node->right && node->right->color != IBV_BLACK)
+                       return 0;
+               return hl;
+       }
+
+       return hl + 1;
 }
 
-static void __mm_add(struct ibv_mem_node *node,
-                    struct ibv_mem_node *new)
+static void __mm_add_rebalance(struct ibv_mem_node *node)
 {
-       new->prev  = node;
-       new->next  = node->next;
-       node->next = new;
-       if (new->next)
-               new->next->prev = new;
+       struct ibv_mem_node *parent, *gp, *uncle;
+
+       while (node->parent && node->parent->color == IBV_RED) {
+               parent = node->parent;
+               gp     = node->parent->parent;
+
+               if (parent == gp->left) {
+                       uncle = gp->right;
+
+                       if (uncle && uncle->color == IBV_RED) {
+                               parent->color = IBV_BLACK;
+                               uncle->color  = IBV_BLACK;
+                               gp->color     = IBV_RED;
+
+                               node = gp;
+                       } else {
+                               if (node == parent->right) {
+                                       __mm_rotate_left(parent);
+                                       node   = parent;
+                                       parent = node->parent;
+                               }
+
+                               parent->color = IBV_BLACK;
+                               gp->color     = IBV_RED;
+
+                               __mm_rotate_right(gp);
+                       }
+               } else {
+                       uncle = gp->left;
+
+                       if (uncle && uncle->color == IBV_RED) {
+                               parent->color = IBV_BLACK;
+                               uncle->color  = IBV_BLACK;
+                               gp->color     = IBV_RED;
+
+                               node = gp;
+                       } else {
+                               if (node == parent->left) {
+                                       __mm_rotate_right(parent);
+                                       node   = parent;
+                                       parent = node->parent;
+                               }
+
+                               parent->color = IBV_BLACK;
+                               gp->color     = IBV_RED;
+
+                               __mm_rotate_left(gp);
+                       }
+               }
+       }
+
+       mm_root->color = IBV_BLACK;
+}
+
+static void __mm_add(struct ibv_mem_node *new)
+{
+       struct ibv_mem_node *node, *parent = NULL;
+
+       node = mm_root;
+       while (node) {
+               parent = node;
+               if (node->start < new->start)
+                       node = node->right;
+               else
+                       node = node->left;
+       }
+
+       if (parent->start < new->start)
+               parent->right = new;
+       else
+               parent->left = new;
+
+       new->parent = parent;
+       new->left   = NULL;
+       new->right  = NULL;
+
+       new->color = IBV_RED;
+       __mm_add_rebalance(new);
 }
 
 static void __mm_remove(struct ibv_mem_node *node)
 {
-       /* Never have to remove the first node, so we can use prev */
-       node->prev->next = node->next;
-       if (node->next)
-               node->next->prev = node->prev;
+       struct ibv_mem_node *child, *parent, *sib, *tmp;
+       int nodecol;
+
+       if (node->left && node->right) {
+               tmp = node->left;
+               while (tmp->right)
+                       tmp = tmp->right;
+
+               nodecol    = tmp->color;
+               child      = tmp->left;
+               tmp->color = node->color;
+
+               if (tmp->parent != node) {
+                       parent        = tmp->parent;
+                       parent->right = tmp->left;
+                       if (tmp->left)
+                               tmp->left->parent = parent;
+
+                       tmp->left          = node->left;
+                       node->left->parent = tmp;
+               } else
+                       parent = tmp;
+
+               tmp->right          = node->right;
+               node->right->parent = tmp;
+
+               tmp->parent = node->parent;
+               if (node->parent) {
+                       if (node->parent->left == node)
+                               node->parent->left = tmp;
+                       else
+                               node->parent->right = tmp;
+               } else
+                       mm_root = tmp;
+       } else {
+               nodecol = node->color;
+
+               child  = node->left ? node->left : node->right;
+               parent = node->parent;
+
+               if (child)
+                       child->parent = parent;
+               if (parent) {
+                       if (parent->left == node)
+                               parent->left = child;
+                       else
+                               parent->right = child;
+               } else
+                       mm_root = child;
+       }
+
+       free(node);
+
+       if (nodecol == IBV_RED)
+               return;
+
+       while ((!child || child->color == IBV_BLACK) && child != mm_root) {
+               if (parent->left == child) {
+                       sib = parent->right;
+
+                       if (sib->color == IBV_RED) {
+                               parent->color = IBV_RED;
+                               sib->color    = IBV_BLACK;
+                               __mm_rotate_left(parent);
+                               sib = parent->right;
+                       }
+
+                       if ((!sib->left  || sib->left->color  == IBV_BLACK) &&
+                           (!sib->right || sib->right->color == IBV_BLACK)) {
+                               sib->color = IBV_RED;
+                               child  = parent;
+                               parent = child->parent;
+                       } else {
+                               if (!sib->right || sib->right->color == IBV_BLACK) {
+                                       if (sib->left)
+                                               sib->left->color = IBV_BLACK;
+                                       sib->color = IBV_RED;
+                                       __mm_rotate_right(sib);
+                                       sib = parent->right;
+                               }
+
+                               sib->color    = parent->color;
+                               parent->color = IBV_BLACK;
+                               if (sib->right)
+                                       sib->right->color = IBV_BLACK;
+                               __mm_rotate_left(parent);
+                               child = mm_root;
+                               break;
+                       }
+               } else {
+                       sib = parent->left;
+
+                       if (sib->color == IBV_RED) {
+                               parent->color = IBV_RED;
+                               sib->color    = IBV_BLACK;
+                               __mm_rotate_right(parent);
+                               sib = parent->left;
+                       }
+
+                       if ((!sib->left  || sib->left->color  == IBV_BLACK) &&
+                           (!sib->right || sib->right->color == IBV_BLACK)) {
+                               sib->color = IBV_RED;
+                               child  = parent;
+                               parent = child->parent;
+                       } else {
+                               if (!sib->left || sib->left->color == IBV_BLACK) {
+                                       if (sib->right)
+                                               sib->right->color = IBV_BLACK;
+                                       sib->color = IBV_RED;
+                                       __mm_rotate_left(sib);
+                                       sib = parent->left;
+                               }
+
+                               sib->color    = parent->color;
+                               parent->color = IBV_BLACK;
+                               if (sib->left)
+                                       sib->left->color = IBV_BLACK;
+                               __mm_rotate_right(parent);
+                               child = mm_root;
+                               break;
+                       }
+               }
+       }
+
+       if (child)
+               child->color = IBV_BLACK;
 }
 
-int ibv_lock_range(void *base, size_t size)
+static struct ibv_mem_node *__mm_find_start(uintptr_t start, uintptr_t end)
+{
+       struct ibv_mem_node *node = mm_root;
+
+       while (node) {
+               if (node->start <= start && node->end >= start)
+                       break;
+
+               if (node->start < start)
+                       node = node->right;
+               else
+                       node = node->left;
+       }
+
+       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;
 
        if (!size)
                return 0;
 
-       start = (uintptr_t) base & ~(mem_map.page_size - 1);
-       end   = ((uintptr_t) (base + size + mem_map.page_size - 1) &
-                ~(mem_map.page_size - 1)) - 1;
+       inc = advice == MADV_DONTFORK ? 1 : -1;
 
-       pthread_mutex_lock(&mem_map.mutex);
+       start = (uintptr_t) base & ~(page_size - 1);
+       end   = ((uintptr_t) (base + size + page_size - 1) &
+                ~(page_size - 1)) - 1;
 
-       node = __mm_find_first(start, end);
+       pthread_mutex_lock(&mm_mutex);
+
+       node = __mm_find_start(start, end);
 
        if (node->start < start) {
                tmp = malloc(sizeof *tmp);
@@ -165,11 +477,19 @@ int ibv_lock_range(void *base, size_t size)
                tmp->refcnt = node->refcnt;
                node->end   = start - 1;
 
-               __mm_add(node, tmp);
+               __mm_add(tmp);
                node = tmp;
+       } else {
+               tmp = __mm_prev(node);
+               if (tmp && tmp->refcnt == node->refcnt + inc) {
+                       tmp->end = node->end;
+                       tmp->refcnt = node->refcnt;
+                       __mm_remove(node);
+                       node = tmp;
+               }
        }
 
-       while (node->start <= end) {
+       while (node && node->start <= end) {
                if (node->end > end) {
                        tmp = malloc(sizeof *tmp);
                        if (!tmp) {
@@ -182,13 +502,16 @@ int ibv_lock_range(void *base, size_t size)
                        tmp->refcnt = node->refcnt;
                        node->end   = end;
 
-                       __mm_add(node, tmp);
+                       __mm_add(tmp);
                }
 
+               node->refcnt += inc;
 
-               if (node->refcnt++ == 0) {
-                       ret = mlock((void *) node->start,
-                                   node->end - node->start + 1);
+               if ((inc == -1 && node->refcnt == 0) ||
+                   (inc ==  1 && node->refcnt == 1)) {
+                       ret = madvise((void *) node->start,
+                                     node->end - node->start + 1,
+                                     advice);
                        if (ret)
                                goto out;
                }
@@ -196,63 +519,36 @@ int ibv_lock_range(void *base, size_t size)
                node = __mm_next(node);
        }
 
+       if (node) {
+               tmp = __mm_prev(node);
+               if (tmp && node->refcnt == tmp->refcnt) {
+                       tmp->end = node->end;
+                       __mm_remove(node);
+               }
+       }
+
 out:
-       pthread_mutex_unlock(&mem_map.mutex);
+       pthread_mutex_unlock(&mm_mutex);
 
        return ret;
 }
 
-int ibv_unlock_range(void *base, size_t size)
+int ibv_dontfork_range(void *base, size_t size)
 {
-       uintptr_t start, end;
-       struct ibv_mem_node *node, *tmp;
-       int ret = 0;
-
-       if (!size)
+       if (mm_root)
+               return ibv_madvise_range(base, size, MADV_DONTFORK);
+       else {
+               too_late = 1;
                return 0;
-
-       start = (uintptr_t) base & ~(mem_map.page_size - 1);
-       end   = ((uintptr_t) (base + size + mem_map.page_size - 1) &
-                ~(mem_map.page_size - 1)) - 1;
-
-       pthread_mutex_lock(&mem_map.mutex);
-
-       node = __mm_find_first(start, end);
-
-       if (node->start != start) {
-               ret = -1;
-               goto out;
-       }
-
-       while (node && node->end <= end) {
-               if (--node->refcnt == 0) {
-                       ret = munlock((void *) node->start,
-                                     node->end - node->start + 1);
-               }
-
-               if (__mm_prev(node) && node->refcnt == __mm_prev(node)->refcnt) {
-                       __mm_prev(node)->end = node->end;
-                       tmp = __mm_prev(node);
-                       __mm_remove(node);
-                       node = tmp;
-               }
-
-               node = __mm_next(node);
-       }
-
-       if (node && node->refcnt == __mm_prev(node)->refcnt) {
-               __mm_prev(node)->end = node->end;
-               tmp = __mm_prev(node);
-               __mm_remove(node);
        }
+}
 
-       if (node->end != end) {
-               ret = -1;
-               goto out;
+int ibv_dofork_range(void *base, size_t size)
+{
+       if (mm_root)
+               return ibv_madvise_range(base, size, MADV_DOFORK);
+       else {
+               too_late = 1;
+               return 0;
        }
-
-out:
-       pthread_mutex_unlock(&mem_map.mutex);
-
-       return ret;
 }
index 1ab5879fc35fbe8a145267a3cc3ece8713effbb9..adf8fe329ae62902ead0a92824dbf18544aeadc9 100644 (file)
@@ -155,18 +155,32 @@ struct ibv_mr *ibv_reg_mr(struct ibv_pd *pd, void *addr,
 {
        struct ibv_mr *mr;
 
+       if (ibv_dontfork_range(addr, length))
+               return NULL;
+
        mr = pd->context->ops.reg_mr(pd, addr, length, access);
        if (mr) {
                mr->context = pd->context;
                mr->pd      = pd;
-       }
+               mr->addr    = addr;
+               mr->length  = length;
+       } else
+               ibv_dofork_range(addr, length);
 
        return mr;
 }
 
 int ibv_dereg_mr(struct ibv_mr *mr)
 {
-       return mr->context->ops.dereg_mr(mr);
+       int ret;
+       void *addr      = mr->addr;
+       size_t length   = mr->length;
+
+       ret = mr->context->ops.dereg_mr(mr);
+       if (!ret)
+               ibv_dofork_range(addr, length);
+
+       return ret;
 }
 
 static struct ibv_comp_channel *ibv_create_comp_channel_v2(struct ibv_context *context)