diff mbox

[ovs-dev,ovs,V1,1/9] dpif-hw-acc: New dpif provider

Message ID 1478012010-32494-2-git-send-email-paulb@mellanox.com
State Changes Requested
Headers show

Commit Message

Paul Blakey Nov. 1, 2016, 2:53 p.m. UTC
Added infrastructure for a new provider that will be able
to send some flows to supporting HW for offloading.

Signed-off-by: Paul Blakey <paulb@mellanox.com>
Signed-off-by: Shahar Klein <shahark@mellanox.com>
---
 lib/automake.mk     |   2 +
 lib/dpif-hw-acc.c   | 444 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/dpif-hw-acc.h   |  14 ++
 lib/dpif-provider.h |   1 +
 lib/dpif.c          |   1 +
 5 files changed, 462 insertions(+)
 create mode 100644 lib/dpif-hw-acc.c
 create mode 100644 lib/dpif-hw-acc.h

Comments

Joe Stringer Nov. 14, 2016, 8:44 p.m. UTC | #1
On 1 November 2016 at 07:53, Paul Blakey <paulb@mellanox.com> wrote:
> Added infrastructure for a new provider that will be able
> to send some flows to supporting HW for offloading.
>
> Signed-off-by: Paul Blakey <paulb@mellanox.com>
> Signed-off-by: Shahar Klein <shahark@mellanox.com>

The amount of boilerplate makes me wonder if this could be structured
a bit better.

At minimum, I think that there is a bunch of functions that could be
directly used from dpif-netlink.c if they were exported by a private
header file, eg lib/dpif-netlink-priv.h which is included from
dpif-netlink and dpif-hw-acc.

A more invasive question is whether it makes more sense to push the
flow hardware offloads down to the netdev layer? As far as I follow,
the Linux netdev community has decided that there is supposed to be
one way to configure these offloads, and it is device-centric.
Furthermore it has impacts on how things like QoS are configured. So,
if the netdev interface provided a set of 'hardware flow offload' APIs
(similar to the flow APIs in dpif today), then the netdev-linux
implementation should be able to reason about how the ordering of
these QoS and offload pieces work.

What I'm proposing is something like:
netdev_flow_flush(netdev *)
netdev_flow_dump_create(netdev *)
netdev_flow_dump_destroy(netdev *)
netdev_flow_dump_next(netdev *, struct match *, int max_flows)
netdev_flow_put(netdev *, struct match *, struct ufid *)
netdev_flow_del(netdev *, struct match *, struct ufid *)

Part of the argument is from a perspective of sharing code. Each
dpif-provider roughly handles 5 things today:
* Port management
* Upcalls
* Downcall / execution
* Flow management
* Conntrack management

From what I can tell, there is maybe a few differences for dpif-hw-acc
here in port management and primarily flow management. If the netdev
layer allowed insertion, fetch, dump and delete of some variation on
"struct match" (plus maybe UFID), what would be left in this file?
Then my question becomes, is there a specific reason to force users to
set the bridge datapath_type, or can OVS just intelligently probe for
offloads at startup then attempt to use them if available?

To explore the above question a little further, I noticed four maps in
this implementation:
* port_to_netdev - Used to translate ifindex to netdev (could be
a generic library function in lib/netdev.c); Also used for collecting
all of the ports so they can be iterated over to flush/dump/etc. This
part I'm not so sure how to handle but I'm sure something can be
figured out.
* ufid_to_handle/handle_to_ufid - I'm a little confused by these, UFID
is supposed to uniquely identify a flow and that's what a handle does
(at least for a particular device, right?) so at a naive first look I
would have though that these can be statelessly translated between
without map storage. There's a couple of different places where
puthandle() is called so I wasn't exactly sure how the handle is
determined.
* mask_to_prio - I think this is just a cache to avoid malloc() calls?

This direction might suggest that each platform can choose how to do
its hardware offloads and we put that logic into the 'struct netdev'
in userspace, then we don't need an additional dpif just for tc flower
hardware offloads. This assumes that each platform comes up with one
way to do hardware offloads.

If particular platforms or implementations decide not to include a
separate API for hardware offloads, they can always do the offloads
transparently as they already do, and just ignore this interface.

I had some discussions with Johann offline and there was some concern
that we end up with, say, four dpifs with potentially arbitrary
ordering between them. This is a lot of permutations. If the above
logic makes sense, then the offloads is an attribute of the netdevice
and not a dedicated dpif, so there are less possible permutations. In
terms of handling the different possible configurations of two dpifs
like shadowing or duplicating flows etc, this logic could exist within
the hw-offload-policy module.

The last part would be to extend the dpif-netlink a bit so that during
startup there is an attempt to query the hardware offload capabilities
so that, eg, if there is no offloads available there is a simple
switch to turn this logic off and there is minimal change for that
path; and ideally it gathers enough information so that the typical
offloads case doesn't involve attempting + failing to install hardware
flow followed by installing the software flow.

Thoughts?
Rony Efraim Nov. 17, 2016, 6:12 p.m. UTC | #2
> On  Monday, November 14, 2016 10:44 PM,  Joe Stringer [mailto:joe@ovn.org] worte:
> On 1 November 2016 at 07:53, Paul Blakey <paulb@mellanox.com> wrote:
> > Added infrastructure for a new provider that will be able to send 
> > some flows to supporting HW for offloading.
> >
> > Signed-off-by: Paul Blakey <paulb@mellanox.com>
> > Signed-off-by: Shahar Klein <shahark@mellanox.com>
> 
> The amount of boilerplate makes me wonder if this could be structured 
> a bit better.
> 
> At minimum, I think that there is a bunch of functions that could be 
> directly used from dpif-netlink.c if they were exported by a private 
> header file, eg lib/dpif- netlink-priv.h which is included from dpif-netlink and dpif-hw-acc.
[RONY] sure it can be done, as the new kid on the block we tried to keep changes as minimal.

> 
> A more invasive question is whether it makes more sense to push the 
> flow hardware offloads down to the netdev layer? As far as I follow, 
> the Linux netdev community has decided that there is supposed to be 
> one way to configure these offloads, and it is device-centric.
> Furthermore it has impacts on how things like QoS are configured. So, 
> if the netdev interface provided a set of 'hardware flow offload' APIs 
> (similar to the flow APIs in dpif today), then the netdev-linux 
> implementation should be able to reason about how the ordering of these QoS and offload pieces work.
> 
> What I'm proposing is something like:
> netdev_flow_flush(netdev *)
> netdev_flow_dump_create(netdev *)
> netdev_flow_dump_destroy(netdev *)
> netdev_flow_dump_next(netdev *, struct match *, int max_flows) 
> netdev_flow_put(netdev *, struct match *, struct ufid *) 
> netdev_flow_del(netdev *, struct match *, struct ufid *)
> 
> Part of the argument is from a perspective of sharing code. Each 
> dpif-provider roughly handles 5 things today:
> * Port management
> * Upcalls
> * Downcall / execution
> * Flow management
> * Conntrack management
> 
> From what I can tell, there is maybe a few differences for dpif-hw-acc 
> here in port management and primarily flow management. If the netdev 
> layer allowed insertion, fetch, dump and delete of some variation on 
> "struct match" (plus maybe UFID), what would be left in this file?
> Then my question becomes, is there a specific reason to force users to 
> set the bridge datapath_type, or can OVS just intelligently probe for 
> offloads at startup then attempt to use them if available?
[RONY] sure, less (auto) configuration is always preferred.
> 
> To explore the above question a little further, I noticed four maps in 
> this
> implementation:
> * port_to_netdev - Used to translate ifindex to netdev (could be a 
> generic library function in lib/netdev.c); Also used for collecting 
> all of the ports so they can be iterated over to flush/dump/etc. This 
> part I'm not so sure how to handle but I'm sure something can be figured out.
[RONY] yes , this is for sure not proprietary code of netlink and can be as a lib

> * ufid_to_handle/handle_to_ufid - I'm a little confused by these, UFID 
> is supposed to uniquely identify a flow and that's what a handle does 
> (at least for a particular device, right?) so at a naive first look I 
> would have though that these can be statelessly translated between 
> without map storage. There's a couple of different places where
> puthandle() is called so I wasn't exactly sure how the handle is determined.
[RONY] the UFID is unique but the tc use 32 bit as a handle per priority and may not have all the fields That make unimpossible to have a direct translate.

> * mask_to_prio - I think this is just a cache to avoid malloc() calls?
> 
> This direction might suggest that each platform can choose how to do 
> its hardware offloads and we put that logic into the 'struct netdev'
> in userspace, then we don't need an additional dpif just for tc flower 
> hardware offloads. This assumes that each platform comes up with one 
> way to do hardware offloads.
> 
> If particular platforms or implementations decide not to include a 
> separate API for hardware offloads, they can always do the offloads 
> transparently as they already do, and just ignore this interface.
> 
> I had some discussions with Johann offline and there was some concern 
> that we end up with, say, four dpifs with potentially arbitrary ordering between them.
> This is a lot of permutations. If the above logic makes sense, then 
> the offloads is an attribute of the netdevice and not a dedicated 
> dpif, so there are less possible permutations. In terms of handling 
> the different possible configurations of two dpifs like shadowing or 
> duplicating flows etc, this logic could exist within the hw- offload-policy module.
[RONY] yes, it will be simpler. And take same code to a library and reuse it if needed.
> 
> The last part would be to extend the dpif-netlink a bit so that during 
> startup there is an attempt to query the hardware offload capabilities 
> so that, eg, if there is no offloads available there is a simple 
> switch to turn this logic off and there is minimal change for that 
> path; and ideally it gathers enough information so that the typical 
> offloads case doesn't involve attempting + failing to install hardware flow followed by installing the software flow.
[RONY] yes, we need to have a state per port, because there can be mix of devices in the system.
We need to keep in mind that we also want to have partial offload ( like HW classification) Then the dpif should call the HW to classify and use the flow id in the datapath.
> 
> Thoughts?
Joe Stringer Nov. 17, 2016, 10:08 p.m. UTC | #3
On 17 November 2016 at 10:12, Rony Efraim <ronye@mellanox.com> wrote:
<snip>

>> * ufid_to_handle/handle_to_ufid - I'm a little confused by these, UFID
>> is supposed to uniquely identify a flow and that's what a handle does
>> (at least for a particular device, right?) so at a naive first look I
>> would have though that these can be statelessly translated between
>> without map storage. There's a couple of different places where
>> puthandle() is called so I wasn't exactly sure how the handle is determined.
> [RONY] the UFID is unique but the tc use 32 bit as a handle per priority and may not have all the fields That make unimpossible to have a direct translate.

OK, I think that part of the issue is that with the OVS netlink
datapath, the UFID is generated by userspace during upcall processing;
whereas for the TC you have to install the flow before you get a TC
handle.

>> * mask_to_prio - I think this is just a cache to avoid malloc() calls?

By the way, I'm also curious why you use different priorities? The
code above dpif never passes down overlapping flows so there's no need
to prioritize them.

>> This direction might suggest that each platform can choose how to do
>> its hardware offloads and we put that logic into the 'struct netdev'
>> in userspace, then we don't need an additional dpif just for tc flower
>> hardware offloads. This assumes that each platform comes up with one
>> way to do hardware offloads.
>>
>> If particular platforms or implementations decide not to include a
>> separate API for hardware offloads, they can always do the offloads
>> transparently as they already do, and just ignore this interface.
>>
>> I had some discussions with Johann offline and there was some concern
>> that we end up with, say, four dpifs with potentially arbitrary ordering between them.
>> This is a lot of permutations. If the above logic makes sense, then
>> the offloads is an attribute of the netdevice and not a dedicated
>> dpif, so there are less possible permutations. In terms of handling
>> the different possible configurations of two dpifs like shadowing or
>> duplicating flows etc, this logic could exist within the hw- offload-policy module.
> [RONY] yes, it will be simpler. And take same code to a library and reuse it if needed.
>>
>> The last part would be to extend the dpif-netlink a bit so that during
>> startup there is an attempt to query the hardware offload capabilities
>> so that, eg, if there is no offloads available there is a simple
>> switch to turn this logic off and there is minimal change for that
>> path; and ideally it gathers enough information so that the typical
>> offloads case doesn't involve attempting + failing to install hardware flow followed by installing the software flow.
> [RONY] yes, we need to have a state per port, because there can be mix of devices in the system.
> We need to keep in mind that we also want to have partial offload ( like HW classification) Then the dpif should call the HW to classify and use the flow id in the datapath.
>>
>> Thoughts?
>
diff mbox

Patch

diff --git a/lib/automake.mk b/lib/automake.mk
index 5387d51..f00c8ae 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -341,6 +341,8 @@  if LINUX
 lib_libopenvswitch_la_SOURCES += \
 	lib/dpif-netlink.c \
 	lib/dpif-netlink.h \
+	lib/dpif-hw-acc.c \
+	lib/dpif-hw-acc.h \
 	lib/if-notifier.c \
 	lib/if-notifier.h \
 	lib/netdev-linux.c \
diff --git a/lib/dpif-hw-acc.c b/lib/dpif-hw-acc.c
new file mode 100644
index 0000000..252a90f
--- /dev/null
+++ b/lib/dpif-hw-acc.c
@@ -0,0 +1,444 @@ 
+
+#include <config.h>
+
+#include "dpif-netlink.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <net/if.h>
+#include <linux/types.h>
+#include <linux/pkt_sched.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "bitmap.h"
+#include "dpif-provider.h"
+#include "dynamic-string.h"
+#include "flow.h"
+#include "fat-rwlock.h"
+#include "netdev.h"
+#include "netdev-linux.h"
+#include "netdev-vport.h"
+#include "netlink-conntrack.h"
+#include "netlink-notifier.h"
+#include "netlink-socket.h"
+#include "netlink.h"
+#include "odp-util.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "random.h"
+#include "shash.h"
+#include "sset.h"
+#include "timeval.h"
+#include "unaligned.h"
+#include "util.h"
+#include "openvswitch/vlog.h"
+#include "dpif-hw-acc.h"
+
+VLOG_DEFINE_THIS_MODULE(dpif_hw_acc);
+
+static struct dpif_hw_acc *
+dpif_hw_acc_cast(const struct dpif *dpif)
+{
+    dpif_assert_class(dpif, &dpif_hw_acc_class);
+    return CONTAINER_OF(dpif, struct dpif_hw_acc, dpif);
+}
+
+static int
+dpif_hw_acc_open(const struct dpif_class *class OVS_UNUSED,
+                     const char *name, bool create, struct dpif **dpifp)
+{
+    struct dpif_hw_acc *dpif;
+    struct dpif *lp_dpif_netlink;
+    struct netdev *netdev;
+    struct dpif_port dpif_port;
+    struct dpif_port_dump dump;
+    int error = 0;
+
+    VLOG_DBG("%s %d %s: parameters name %s, create: %s\n", __FILE__, __LINE__,
+             __func__, name, (create ? "yes" : "no"));
+    if (create) {
+        if ((error = dpif_create(name, "system", &lp_dpif_netlink))) {
+            return error;
+        }
+    } else {
+        if ((error = dpif_open(name, "system", &lp_dpif_netlink))) {
+            return error;
+        }
+    }
+    dpif = xzalloc(sizeof *dpif);
+
+    *CONST_CAST(const char **, &dpif->name) = xstrdup(name);
+    uint16_t netflow_id = hash_string(dpif->name, 0);
+
+    dpif->lp_dpif_netlink = lp_dpif_netlink;
+
+    dpif_init(&dpif->dpif, &dpif_hw_acc_class, dpif->name, netflow_id >> 8,
+              netflow_id);
+
+    *dpifp = &dpif->dpif;
+
+    return 0;
+}
+
+static void
+dpif_hw_acc_close(struct dpif *dpif_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->close(dpif->lp_dpif_netlink);
+}
+
+static int
+dpif_hw_acc_destroy(struct dpif *dpif_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->destroy(dpif->lp_dpif_netlink);
+}
+
+static bool
+dpif_hw_acc_run(struct dpif *dpif_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->run(dpif->lp_dpif_netlink);
+}
+
+static int
+dpif_hw_acc_get_stats(const struct dpif *dpif_,
+                          struct dpif_dp_stats *stats)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->get_stats(dpif->lp_dpif_netlink,
+                                                        stats);
+}
+
+static int
+dpif_hw_acc_port_add(struct dpif *dpif_, struct netdev *netdev,
+                         odp_port_t * port_nop)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->port_add(dpif->lp_dpif_netlink,
+                                                       netdev, port_nop);
+}
+
+static int
+dpif_hw_acc_port_del(struct dpif *dpif_, odp_port_t port_no)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->port_del(dpif->lp_dpif_netlink,
+                                                       port_no);
+}
+
+static int
+dpif_hw_acc_port_query_by_number(const struct dpif *dpif_,
+                                     odp_port_t port_no,
+                                     struct dpif_port *dpif_port)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        port_query_by_number(dpif->lp_dpif_netlink, port_no, dpif_port);
+}
+
+static int
+dpif_hw_acc_port_query_by_name(const struct dpif *dpif_,
+                                   const char *devname,
+                                   struct dpif_port *dpif_port)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        port_query_by_name(dpif->lp_dpif_netlink, devname, dpif_port);
+}
+
+static uint32_t
+dpif_hw_acc_port_get_pid(const struct dpif *dpif_, odp_port_t port_no,
+                             uint32_t hash)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        port_get_pid(dpif->lp_dpif_netlink, port_no, hash);
+}
+
+static int
+dpif_hw_acc_port_dump_start(const struct dpif *dpif_, void **statep)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        port_dump_start(dpif->lp_dpif_netlink, statep);
+}
+
+static int
+dpif_hw_acc_port_dump_next(const struct dpif *dpif_, void *state_,
+                               struct dpif_port *dpif_port)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        port_dump_next(dpif->lp_dpif_netlink, state_, dpif_port);
+}
+
+static int
+dpif_hw_acc_port_dump_done(const struct dpif *dpif_ OVS_UNUSED,
+                               void *state_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        port_dump_done(dpif->lp_dpif_netlink, state_);
+}
+
+static int
+dpif_hw_acc_port_poll(const struct dpif *dpif_, char **devnamep)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->port_poll(dpif->lp_dpif_netlink,
+                                                        devnamep);
+}
+
+static void
+dpif_hw_acc_port_poll_wait(const struct dpif *dpif_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        port_poll_wait(dpif->lp_dpif_netlink);
+}
+
+static int
+dpif_hw_acc_flow_flush(struct dpif *dpif_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        flow_flush(dpif->lp_dpif_netlink);
+}
+
+static struct dpif_flow_dump *
+dpif_hw_acc_flow_dump_create(const struct dpif *dpif_, bool terse)
+{
+    struct dpif_flow_dump *dump;
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    dump =
+        dpif->lp_dpif_netlink->dpif_class->
+        flow_dump_create(dpif->lp_dpif_netlink, terse);
+    dump->dpif = CONST_CAST(struct dpif *, dpif_);
+
+    return dump;
+
+}
+
+static int
+dpif_hw_acc_flow_dump_destroy(struct dpif_flow_dump *dump_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dump_->dpif);
+
+    dump_->dpif = dpif->lp_dpif_netlink;
+    return dpif->lp_dpif_netlink->dpif_class->flow_dump_destroy(dump_);
+}
+
+static struct dpif_flow_dump_thread *
+dpif_hw_acc_flow_dump_thread_create(struct dpif_flow_dump *dump_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dump_->dpif);
+
+    return dpif->lp_dpif_netlink->dpif_class->flow_dump_thread_create(dump_);
+
+}
+
+static void
+dpif_hw_acc_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(thread_->dpif);
+
+    thread_->dpif = dpif->lp_dpif_netlink;
+    return dpif->lp_dpif_netlink->
+        dpif_class->flow_dump_thread_destroy(thread_);
+}
+
+static int
+dpif_hw_acc_flow_dump_next(struct dpif_flow_dump_thread *thread_,
+                               struct dpif_flow *flows, int max_flows)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(thread_->dpif);
+
+    thread_->dpif = dpif->lp_dpif_netlink;
+    return dpif->lp_dpif_netlink->dpif_class->flow_dump_next(thread_, flows,
+                                                             max_flows);
+}
+
+static void
+dpif_hw_acc_operate(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->operate(dpif->lp_dpif_netlink,
+                                                      ops, n_ops);
+}
+
+static int
+dpif_hw_acc_recv_set(struct dpif *dpif_, bool enable)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->recv_set(dpif->lp_dpif_netlink,
+                                                       enable);
+}
+
+static int
+dpif_hw_acc_handlers_set(struct dpif *dpif_, uint32_t n_handlers)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        handlers_set(dpif->lp_dpif_netlink, n_handlers);
+}
+
+static int
+dpif_hw_acc_queue_to_priority(const struct dpif *dpif_,
+                                  uint32_t queue_id, uint32_t * priority)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        queue_to_priority(dpif->lp_dpif_netlink, queue_id, priority);
+}
+
+static int
+dpif_hw_acc_recv(struct dpif *dpif_, uint32_t handler_id,
+                     struct dpif_upcall *upcall, struct ofpbuf *buf)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->recv(dpif->lp_dpif_netlink,
+                                                   handler_id, upcall, buf);
+
+}
+
+static void
+dpif_hw_acc_recv_wait(struct dpif *dpif_, uint32_t handler_id)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->recv_wait(dpif->lp_dpif_netlink,
+                                                        handler_id);
+}
+
+static void
+dpif_hw_acc_recv_purge(struct dpif *dpif_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        recv_purge(dpif->lp_dpif_netlink);
+}
+
+static int
+dpif_hw_acc_ct_dump_start(struct dpif *dpif_,
+                              struct ct_dpif_dump_state **dump_,
+                              const uint16_t * zone)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        ct_dump_start(dpif->lp_dpif_netlink, dump_, zone);
+}
+
+static int
+dpif_hw_acc_ct_dump_next(struct dpif *dpif_,
+                             struct ct_dpif_dump_state *dump_,
+                             struct ct_dpif_entry *entry)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        ct_dump_next(dpif->lp_dpif_netlink, dump_, entry);
+}
+
+static int
+dpif_hw_acc_ct_dump_done(struct dpif *dpif_,
+                             struct ct_dpif_dump_state *dump_)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->
+        ct_dump_done(dpif->lp_dpif_netlink, dump_);
+}
+
+static int
+dpif_hw_acc_ct_flush(struct dpif *dpif_, const uint16_t * zone)
+{
+    struct dpif_hw_acc *dpif = dpif_hw_acc_cast(dpif_);
+
+    return dpif->lp_dpif_netlink->dpif_class->ct_flush(dpif->lp_dpif_netlink,
+                                                       zone);
+}
+
+const struct dpif_class dpif_hw_acc_class = {
+    "hw_netlink",
+    NULL,                       /* init */
+    NULL,
+    NULL,
+    dpif_hw_acc_open,
+    dpif_hw_acc_close,
+    dpif_hw_acc_destroy,
+    dpif_hw_acc_run,
+    NULL,                       /* wait */
+    dpif_hw_acc_get_stats,
+    dpif_hw_acc_port_add,
+    dpif_hw_acc_port_del,
+    dpif_hw_acc_port_query_by_number,
+    dpif_hw_acc_port_query_by_name,
+    dpif_hw_acc_port_get_pid,
+    dpif_hw_acc_port_dump_start,
+    dpif_hw_acc_port_dump_next,
+    dpif_hw_acc_port_dump_done,
+    dpif_hw_acc_port_poll,
+    dpif_hw_acc_port_poll_wait,
+    dpif_hw_acc_flow_flush,
+    dpif_hw_acc_flow_dump_create,
+    dpif_hw_acc_flow_dump_destroy,
+    dpif_hw_acc_flow_dump_thread_create,
+    dpif_hw_acc_flow_dump_thread_destroy,
+    dpif_hw_acc_flow_dump_next,
+    dpif_hw_acc_operate,
+    dpif_hw_acc_recv_set,
+    dpif_hw_acc_handlers_set,
+    NULL,                       /* poll_thread_set */
+    dpif_hw_acc_queue_to_priority,
+    dpif_hw_acc_recv,
+    dpif_hw_acc_recv_wait,
+    dpif_hw_acc_recv_purge,
+    NULL,                       /* register_dp_purge_cb */
+    NULL,                       /* register_upcall_cb */
+    NULL,                       /* enable_upcall */
+    NULL,                       /* disable_upcall */
+    NULL,                       /* get_datapath_version */
+#ifdef __linux__
+    dpif_hw_acc_ct_dump_start,
+    dpif_hw_acc_ct_dump_next,
+    dpif_hw_acc_ct_dump_done,
+    dpif_hw_acc_ct_flush,
+#else
+    NULL,                       /* ct_dump_start */
+    NULL,                       /* ct_dump_next */
+    NULL,                       /* ct_dump_done */
+    NULL,                       /* ct_flush */
+#endif
+};
diff --git a/lib/dpif-hw-acc.h b/lib/dpif-hw-acc.h
new file mode 100644
index 0000000..5559b41
--- /dev/null
+++ b/lib/dpif-hw-acc.h
@@ -0,0 +1,14 @@ 
+#ifndef DPIF_HW_NETLINK_H
+#define DPIF_HW_NETLINK_H 1
+
+#include "ovs-thread.h"
+#include "dpif-provider.h"
+
+/* Datapath interface for the openvswitch Linux kernel module. */
+struct dpif_hw_acc {
+    struct dpif dpif;
+    struct dpif *lp_dpif_netlink;
+    const char *const name;
+};
+
+#endif
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index a9844be..b086356 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -423,6 +423,7 @@  struct dpif_class {
 
 extern const struct dpif_class dpif_netlink_class;
 extern const struct dpif_class dpif_netdev_class;
+extern const struct dpif_class dpif_hw_acc_class;
 
 #ifdef  __cplusplus
 }
diff --git a/lib/dpif.c b/lib/dpif.c
index 38e40ba..adef6ef 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -67,6 +67,7 @@  COVERAGE_DEFINE(dpif_execute_with_help);
 static const struct dpif_class *base_dpif_classes[] = {
 #if defined(__linux__) || defined(_WIN32)
     &dpif_netlink_class,
+    &dpif_hw_acc_class,
 #endif
     &dpif_netdev_class,
 };