+ if (op->type == DPIF_OP_FLOW_PUT) {
+ struct dpif_flow_put *put = &op->flow_put;
+ int error = txn->error;
+
+ if (!error && put->stats) {
+ struct dpif_linux_flow reply;
+
+ error = dpif_linux_flow_from_ofpbuf(&reply, txn->reply);
+ if (!error) {
+ dpif_linux_flow_get_stats(&reply, put->stats);
+ }
+ }
+ put->error = error;
+ } else if (op->type == DPIF_OP_EXECUTE) {
+ struct dpif_execute *execute = &op->execute;
+
+ execute->error = txn->error;
+ } else {
+ NOT_REACHED();
+ }
+
+ ofpbuf_delete(txn->request);
+ ofpbuf_delete(txn->reply);
+ }
+ free(txns);
+}
+
+static int
+dpif_linux_recv_get_mask(const struct dpif *dpif_, int *listen_mask)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ *listen_mask = dpif->listen_mask;
+ return 0;
+}
+
+static void
+set_upcall_pids(struct dpif *dpif_)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ struct dpif_port_dump port_dump;
+ struct dpif_port port;
+ int error;
+
+ DPIF_PORT_FOR_EACH (&port, &port_dump, &dpif->dpif) {
+ uint32_t upcall_pid = dpif_linux_port_get_pid__(dpif_, port.port_no,
+ DPIF_UC_MISS);
+ struct dpif_linux_vport vport_request;
+
+ dpif_linux_vport_init(&vport_request);
+ vport_request.cmd = OVS_VPORT_CMD_SET;
+ vport_request.dp_ifindex = dpif->dp_ifindex;
+ vport_request.port_no = port.port_no;
+ vport_request.upcall_pid = &upcall_pid;
+ error = dpif_linux_vport_transact(&vport_request, NULL, NULL);
+ if (!error) {
+ VLOG_DBG("%s: assigning port %"PRIu32" to netlink pid %"PRIu32,
+ dpif_name(&dpif->dpif), vport_request.port_no,
+ upcall_pid);
+ } else {
+ VLOG_WARN_RL(&error_rl, "%s: failed to set upcall pid on port: %s",
+ dpif_name(&dpif->dpif), strerror(error));
+ }
+ }
+}
+
+static int
+dpif_linux_recv_set_mask(struct dpif *dpif_, int listen_mask)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+
+ if (listen_mask == dpif->listen_mask) {
+ return 0;
+ }
+
+ if (!listen_mask) {
+ destroy_upcall_socks(dpif);
+ } else if (!dpif->listen_mask) {
+ int i;
+ int error;
+
+ dpif->epoll_fd = epoll_create(N_UPCALL_SOCKS);
+ if (dpif->epoll_fd < 0) {
+ return errno;
+ }
+
+ for (i = 0; i < N_UPCALL_SOCKS; i++) {
+ struct epoll_event event;
+
+ error = nl_sock_create(NETLINK_GENERIC, &dpif->upcall_socks[i]);
+ if (error) {
+ destroy_upcall_socks(dpif);
+ return error;
+ }
+
+ memset(&event, 0, sizeof event);
+ event.events = EPOLLIN;
+ event.data.u32 = i;
+ if (epoll_ctl(dpif->epoll_fd, EPOLL_CTL_ADD,
+ nl_sock_fd(dpif->upcall_socks[i]), &event) < 0) {
+ error = errno;
+ destroy_upcall_socks(dpif);
+ return error;
+ }
+ }
+
+ dpif->ready_mask = 0;
+ }
+
+ dpif->listen_mask = listen_mask;
+ set_upcall_pids(dpif_);
+
+ return 0;
+}
+
+static int
+dpif_linux_queue_to_priority(const struct dpif *dpif OVS_UNUSED,
+ uint32_t queue_id, uint32_t *priority)
+{
+ if (queue_id < 0xf000) {
+ *priority = TC_H_MAKE(1 << 16, queue_id + 1);
+ return 0;
+ } else {
+ return EINVAL;
+ }
+}
+
+static int
+parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall *upcall,
+ int *dp_ifindex)
+{
+ static const struct nl_policy ovs_packet_policy[] = {
+ /* Always present. */
+ [OVS_PACKET_ATTR_PACKET] = { .type = NL_A_UNSPEC,
+ .min_len = ETH_HEADER_LEN },
+ [OVS_PACKET_ATTR_KEY] = { .type = NL_A_NESTED },
+
+ /* OVS_PACKET_CMD_ACTION only. */
+ [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_U64, .optional = true },
+ };
+
+ struct ovs_header *ovs_header;
+ struct nlattr *a[ARRAY_SIZE(ovs_packet_policy)];
+ struct nlmsghdr *nlmsg;
+ struct genlmsghdr *genl;
+ struct ofpbuf b;
+ int type;
+
+ ofpbuf_use_const(&b, buf->data, buf->size);
+
+ nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
+ genl = ofpbuf_try_pull(&b, sizeof *genl);
+ ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header);
+ if (!nlmsg || !genl || !ovs_header
+ || nlmsg->nlmsg_type != ovs_packet_family
+ || !nl_policy_parse(&b, 0, ovs_packet_policy, a,
+ ARRAY_SIZE(ovs_packet_policy))) {
+ return EINVAL;
+ }
+
+ type = (genl->cmd == OVS_PACKET_CMD_MISS ? DPIF_UC_MISS
+ : genl->cmd == OVS_PACKET_CMD_ACTION ? DPIF_UC_ACTION
+ : -1);
+ if (type < 0) {
+ return EINVAL;
+ }