+static void
+dpif_linux_operate(struct dpif *dpif_, union dpif_op **ops, size_t n_ops)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ struct nl_transaction **txnsp;
+ struct nl_transaction *txns;
+ size_t i;
+
+ txns = xmalloc(n_ops * sizeof *txns);
+ for (i = 0; i < n_ops; i++) {
+ struct nl_transaction *txn = &txns[i];
+ union dpif_op *op = ops[i];
+
+ if (op->type == DPIF_OP_FLOW_PUT) {
+ struct dpif_flow_put *put = &op->flow_put;
+ struct dpif_linux_flow request;
+
+ dpif_linux_init_flow_put(dpif_, put->flags, put->key, put->key_len,
+ put->actions, put->actions_len,
+ &request);
+ if (put->stats) {
+ request.nlmsg_flags |= NLM_F_ECHO;
+ }
+ txn->request = ofpbuf_new(1024);
+ dpif_linux_flow_to_ofpbuf(&request, txn->request);
+ } else if (op->type == DPIF_OP_EXECUTE) {
+ struct dpif_execute *execute = &op->execute;
+
+ txn->request = dpif_linux_encode_execute(
+ dpif->dp_ifindex, execute->key, execute->key_len,
+ execute->actions, execute->actions_len, execute->packet);
+ } else {
+ NOT_REACHED();
+ }
+ }
+
+ txnsp = xmalloc(n_ops * sizeof *txnsp);
+ for (i = 0; i < n_ops; i++) {
+ txnsp[i] = &txns[i];
+ }
+
+ nl_sock_transact_multiple(genl_sock, txnsp, n_ops);
+
+ free(txnsp);
+
+ for (i = 0; i < n_ops; i++) {
+ struct nl_transaction *txn = &txns[i];
+ union dpif_op *op = ops[i];
+
+ 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;
+
+ for (i = 0; i < N_UPCALL_SOCKS; i++) {
+ error = nl_sock_create(NETLINK_GENERIC, &dpif->upcall_socks[i]);
+ if (error) {
+ destroy_upcall_socks(dpif);
+ return error;
+ }
+ }
+ }
+
+ 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;
+ }
+
+ memset(upcall, 0, sizeof *upcall);
+ upcall->type = type;
+ upcall->packet = buf;
+ upcall->packet->data = (void *) nl_attr_get(a[OVS_PACKET_ATTR_PACKET]);
+ upcall->packet->size = nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]);
+ upcall->key = (void *) nl_attr_get(a[OVS_PACKET_ATTR_KEY]);
+ upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]);
+ upcall->userdata = (a[OVS_PACKET_ATTR_USERDATA]
+ ? nl_attr_get_u64(a[OVS_PACKET_ATTR_USERDATA])
+ : 0);
+ *dp_ifindex = ovs_header->dp_ifindex;
+
+ return 0;
+}
+
+static int
+dpif_linux_recv(struct dpif *dpif_, struct dpif_upcall *upcall)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ int i;
+ int read_tries = 0;
+
+ if (!dpif->listen_mask) {
+ return EAGAIN;
+ }
+
+ for (i = 0; i < N_UPCALL_SOCKS; i++) {
+ struct nl_sock *upcall_sock;
+ dpif->last_read_upcall = (dpif->last_read_upcall + 1) &
+ (N_UPCALL_SOCKS - 1);
+ upcall_sock = dpif->upcall_socks[dpif->last_read_upcall];
+
+ if (nl_sock_woke(upcall_sock)) {
+ int dp_ifindex;
+
+ for (;;) {
+ struct ofpbuf *buf;
+ int error;
+
+ if (++read_tries > 50) {
+ return EAGAIN;
+ }
+
+ error = nl_sock_recv(upcall_sock, &buf, false);
+ if (error == EAGAIN) {
+ break;
+ } else if (error) {
+ return error;
+ }
+
+ error = parse_odp_packet(buf, upcall, &dp_ifindex);
+ if (!error
+ && dp_ifindex == dpif->dp_ifindex
+ && dpif->listen_mask & (1u << upcall->type)) {
+ return 0;
+ }
+
+ ofpbuf_delete(buf);
+ if (error) {
+ return error;
+ }
+ }
+ }
+ }
+
+ return EAGAIN;
+}
+
+static void
+dpif_linux_recv_wait(struct dpif *dpif_)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ int i;
+
+ if (!dpif->listen_mask) {
+ return;
+ }
+
+ for (i = 0; i < N_UPCALL_SOCKS; i++) {
+ nl_sock_wait(dpif->upcall_socks[i], POLLIN);
+ }
+}
+
+static void
+dpif_linux_recv_purge(struct dpif *dpif_)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ int i;
+
+ if (!dpif->listen_mask) {
+ return;
+ }
+
+ for (i = 0; i < N_UPCALL_SOCKS; i++) {
+ nl_sock_drain(dpif->upcall_socks[i]);
+ }
+}
+
+const struct dpif_class dpif_linux_class = {
+ "system",