diff --git a/lib/netlink-protocol.h b/lib/netlink-protocol.h
index 3009fc5..1d18e22 100644
--- a/lib/netlink-protocol.h
+++ b/lib/netlink-protocol.h
@@ -145,6 +145,39 @@ enum {
 };
 
 #define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+
+#define NETLINK_RX_RING                6
+#define NETLINK_TX_RING                7
+
+struct nl_mmap_req {
+        unsigned int    nm_block_size;
+        unsigned int    nm_block_nr;
+        unsigned int    nm_frame_size;
+        unsigned int    nm_frame_nr;
+};
+
+struct nl_mmap_hdr {
+        unsigned int    nm_status;
+        unsigned int    nm_len;
+        __u32           nm_group;
+        /* credentials */
+        __u32           nm_pid;
+        __u32           nm_uid;
+        __u32           nm_gid;
+};
+
+enum nl_mmap_status {
+        NL_MMAP_STATUS_UNUSED,
+        NL_MMAP_STATUS_RESERVED,
+        NL_MMAP_STATUS_VALID,
+        NL_MMAP_STATUS_COPY,
+        NL_MMAP_STATUS_SKIP,
+};
+
+#define NL_MMAP_MSG_ALIGNMENT           NLMSG_ALIGNTO
+#define NL_MMAP_MSG_ALIGN(sz)           __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
+#define NL_MMAP_HDRLEN                  NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
+
 #endif  /* !HAVE_NETLINK */
 
 /* These were introduced all together in 2.6.24. */
diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c
index da32284..576a532 100644
--- a/lib/netlink-socket.c
+++ b/lib/netlink-socket.c
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/uio.h>
+#include <sys/mman.h>
 #include <unistd.h>
 #include "coverage.h"
 #include "dynamic-string.h"
@@ -65,6 +66,8 @@ struct nl_sock {
     uint32_t pid;
     int protocol;
     unsigned int rcvbuf;        /* Receive buffer size (SO_RCVBUF). */
+    char *tx_ring;
+    char *rx_ring;
 };
 
 /* Compile-time limit on iovecs, so that we can allocate a maximum-size array
@@ -92,6 +95,14 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
     socklen_t local_size;
     int rcvbuf;
     int retval = 0;
+    unsigned int block_size = 16 * getpagesize();
+    struct nl_mmap_req req = {
+        .nm_block_size          = block_size,
+        .nm_block_nr            = 64,
+        .nm_frame_size          = 16384,
+        .nm_frame_nr            = 64 * block_size / 16384,
+    };
+    unsigned int ring_size;
 
     if (ovsthread_once_start(&once)) {
         int save_errno = errno;
@@ -121,6 +132,7 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
     }
     sock->protocol = protocol;
     sock->next_seq = 1;
+    sock->tx_ring = sock->rx_ring = NULL;
 
     rcvbuf = 1024 * 1024;
     if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUFFORCE,
@@ -162,6 +174,20 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
     }
     sock->pid = local.nl_pid;
 
+    if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_RX_RING, &req, sizeof(req)) < 0)
+        goto done;
+    if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_TX_RING, &req, sizeof(req)) < 0)
+        goto done;
+    /* Calculate size of each invididual ring */
+    ring_size = req.nm_block_nr * req.nm_block_size;
+    /* Map RX/TX rings. The TX ring is located after the RX ring */
+    sock->rx_ring = mmap(NULL, 2 * ring_size, PROT_READ | PROT_WRITE,
+                         MAP_SHARED, sock->fd, 0);
+    if ((long)sock->rx_ring == -1L)
+            goto error;
+    sock->tx_ring = sock->rx_ring + ring_size;
+
+done:
     *sockp = sock;
     return 0;
 
@@ -193,6 +219,8 @@ void
 nl_sock_destroy(struct nl_sock *sock)
 {
     if (sock) {
+	if (sock->rx_ring)
+            munmap(sock->rx_ring, 2 * (sock->tx_ring - sock->rx_ring));
         close(sock->fd);
         free(sock);
     }
@@ -247,20 +275,57 @@ static int
 nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg,
                uint32_t nlmsg_seq, bool wait)
 {
-    struct nlmsghdr *nlmsg = nl_msg_nlmsghdr(msg);
     int error;
 
-    nlmsg->nlmsg_len = msg->size;
-    nlmsg->nlmsg_seq = nlmsg_seq;
-    nlmsg->nlmsg_pid = sock->pid;
-    do {
-        int retval;
-        retval = send(sock->fd, msg->data, msg->size, wait ? 0 : MSG_DONTWAIT);
-        error = retval < 0 ? errno : 0;
-    } while (error == EINTR);
-    log_nlmsg(__func__, error, msg->data, msg->size, sock->protocol);
-    if (!error) {
-        COVERAGE_INC(netlink_sent);
+    if (sock->tx_ring) {
+        struct nl_mmap_hdr *hdr;
+        struct nlmsghdr *nlh;
+        struct sockaddr_nl addr = {
+                .nl_family      = AF_NETLINK,
+        };
+        unsigned int frame_offset = 0, frame_size = 16384;
+        unsigned int ring_size = sock->tx_ring - sock->rx_ring;
+
+        while (1) {
+            int retval, last;
+
+            hdr = (struct nl_mmap_hdr *)sock->tx_ring + frame_offset;
+            if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
+                /* No frame available. Use poll() to avoid. */
+                return EBUSY;
+
+            nlh = (struct nlmsghdr *)((char *)hdr + NL_MMAP_HDRLEN);
+            last = (msg->size - frame_offset) < frame_size ? 1 : 0;
+            nlh->nlmsg_len = last ? (msg->size - frame_offset) : frame_size;
+            nlh->nlmsg_seq = nlmsg_seq;
+            nlh->nlmsg_pid = sock->pid;
+
+            /* Fill frame header: length and status need to be set */
+            hdr->nm_len     = nlh->nlmsg_len;
+            hdr->nm_status  = NL_MMAP_STATUS_VALID;
+
+            retval = sendto(sock->fd, NULL, 0, 0, (struct sockaddr *)&addr, sizeof(addr));
+            error = retval < 0 ? errno : 0;
+            if (error || last)
+                break;
+            /* Advance frame offset to next frame */
+            frame_offset = (frame_offset + frame_size) % ring_size;
+        }
+    } else {
+        struct nlmsghdr *nlmsg = nl_msg_nlmsghdr(msg);
+
+        nlmsg->nlmsg_len = msg->size;
+        nlmsg->nlmsg_seq = nlmsg_seq;
+        nlmsg->nlmsg_pid = sock->pid;
+        do {
+            int retval;
+            retval = send(sock->fd, msg->data, msg->size, wait ? 0 : MSG_DONTWAIT);
+            error = retval < 0 ? errno : 0;
+        } while (error == EINTR);
+        log_nlmsg(__func__, error, msg->data, msg->size, sock->protocol);
+        if (!error) {
+            COVERAGE_INC(netlink_sent);
+        }
     }
     return error;
 }
@@ -306,56 +371,112 @@ nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait)
      * 'tail' to allow Netlink messages to be up to 64 kB long (a reasonable
      * figure since that's the maximum length of a Netlink attribute). */
     struct nlmsghdr *nlmsghdr;
-    uint8_t tail[65536];
-    struct iovec iov[2];
-    struct msghdr msg;
     ssize_t retval;
+    uint8_t tail[65536];
 
     ovs_assert(buf->allocated >= sizeof *nlmsghdr);
     ofpbuf_clear(buf);
 
-    iov[0].iov_base = buf->base;
-    iov[0].iov_len = buf->allocated;
-    iov[1].iov_base = tail;
-    iov[1].iov_len = sizeof tail;
-
-    memset(&msg, 0, sizeof msg);
-    msg.msg_iov = iov;
-    msg.msg_iovlen = 2;
-
-    do {
-        retval = recvmsg(sock->fd, &msg, wait ? 0 : MSG_DONTWAIT);
-    } while (retval < 0 && errno == EINTR);
-
-    if (retval < 0) {
-        int error = errno;
-        if (error == ENOBUFS) {
-            /* Socket receive buffer overflow dropped one or more messages that
-             * the kernel tried to send to us. */
-            COVERAGE_INC(netlink_overflow);
+    if (sock->rx_ring) {
+        unsigned int frame_offset = 0, frame_size = 16384;
+        unsigned int ring_size = sock->tx_ring - sock->rx_ring;
+        struct nl_mmap_hdr *hdr;
+        ssize_t len;
+        int copied = 0;
+
+        while (1) {
+            /* Get next frame header */
+            hdr = (struct nl_mmap_hdr *)sock->rx_ring + frame_offset;
+
+            if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
+                /* Regular memory mapped frame */
+                nlmsghdr = (struct nlmsghdr *)((char *)hdr + NL_MMAP_HDRLEN);
+                len = hdr->nm_len;
+
+                /* Release empty message immediately. May happen
+                 * on error during message construction.
+                 */
+                if (len == 0)
+                    goto next;
+                if (!copied) {
+                    memcpy(buf->base, nlmsghdr, buf->allocated);
+                    copied = 1;
+                }
+                buf->size = MIN(len, buf->allocated);
+                if (len > buf->allocated) {
+                    COVERAGE_INC(netlink_recv_jumbo);
+                    ofpbuf_put(buf, (char *)hdr + buf->allocated, len - buf->allocated);
+                }
+            } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
+                /* Frame queued to socket receive queue */
+                len = recv(sock->fd, tail, sizeof tail, MSG_DONTWAIT);
+                if (len <= 0)
+                    break;
+                if (!copied) {
+                    memcpy(buf->base, tail, buf->allocated);
+                    copied = 1;
+                }
+                buf->size = MIN(len, buf->allocated);
+                if (len > buf->allocated) {
+                    COVERAGE_INC(netlink_recv_jumbo);
+                    ofpbuf_put(buf, tail + buf->allocated, len - buf->allocated);
+                }
+            } else
+                /* No more messages to process, continue polling */
+                break;
+next:
+            /* Release frame back to the kernel */
+            hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+            /* Advance frame offset to next frame */
+            frame_offset = (frame_offset + frame_size) % ring_size;
+        }
+    } else {
+        struct iovec iov[2];
+        struct msghdr msg;
+
+        iov[0].iov_base = buf->base;
+        iov[0].iov_len = buf->allocated;
+        iov[1].iov_base = tail;
+        iov[1].iov_len = sizeof tail;
+
+        memset(&msg, 0, sizeof msg);
+        msg.msg_iov = iov;
+        msg.msg_iovlen = 2;
+
+        do {
+            retval = recvmsg(sock->fd, &msg, wait ? 0 : MSG_DONTWAIT);
+        } while (retval < 0 && errno == EINTR);
+
+        if (retval < 0) {
+            int error = errno;
+            if (error == ENOBUFS) {
+                /* Socket receive buffer overflow dropped one or more messages that
+                 * the kernel tried to send to us. */
+                COVERAGE_INC(netlink_overflow);
+            }
+            return error;
         }
-        return error;
-    }
 
-    if (msg.msg_flags & MSG_TRUNC) {
-        VLOG_ERR_RL(&rl, "truncated message (longer than %zu bytes)",
-                    sizeof tail);
-        return E2BIG;
-    }
+        if (msg.msg_flags & MSG_TRUNC) {
+            VLOG_ERR_RL(&rl, "truncated message (longer than %zu bytes)",
+                        sizeof tail);
+            return E2BIG;
+        }
 
-    nlmsghdr = buf->data;
-    if (retval < sizeof *nlmsghdr
-        || nlmsghdr->nlmsg_len < sizeof *nlmsghdr
-        || nlmsghdr->nlmsg_len > retval) {
-        VLOG_ERR_RL(&rl, "received invalid nlmsg (%zd bytes < %zu)",
-                    retval, sizeof *nlmsghdr);
-        return EPROTO;
-    }
+        nlmsghdr = buf->data;
+        if (retval < sizeof *nlmsghdr
+            || nlmsghdr->nlmsg_len < sizeof *nlmsghdr
+            || nlmsghdr->nlmsg_len > retval) {
+            VLOG_ERR_RL(&rl, "received invalid nlmsg (%zd bytes < %zu)",
+                        retval, sizeof *nlmsghdr);
+            return EPROTO;
+        }
 
-    buf->size = MIN(retval, buf->allocated);
-    if (retval > buf->allocated) {
-        COVERAGE_INC(netlink_recv_jumbo);
-        ofpbuf_put(buf, tail, retval - buf->allocated);
+        buf->size = MIN(retval, buf->allocated);
+        if (retval > buf->allocated) {
+            COVERAGE_INC(netlink_recv_jumbo);
+            ofpbuf_put(buf, tail, retval - buf->allocated);
+        }
     }
 
     log_nlmsg(__func__, 0, buf->data, buf->size, sock->protocol);
