+ 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 read_tries = 0;
+
+ if (dpif->epoll_fd < 0) {
+ return EAGAIN;
+ }
+
+ if (!dpif->ready_mask) {
+ struct epoll_event events[N_UPCALL_SOCKS];
+ int retval;
+ int i;
+
+ do {
+ retval = epoll_wait(dpif->epoll_fd, events, N_UPCALL_SOCKS, 0);
+ } while (retval < 0 && errno == EINTR);
+ if (retval < 0) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_WARN_RL(&rl, "epoll_wait failed (%s)", strerror(errno));
+ }
+
+ for (i = 0; i < retval; i++) {
+ dpif->ready_mask |= 1u << events[i].data.u32;
+ }
+ }
+
+ while (dpif->ready_mask) {
+ int indx = ffs(dpif->ready_mask) - 1;
+ struct nl_sock *upcall_sock = dpif->upcall_socks[indx];
+
+ dpif->ready_mask &= ~(1u << indx);
+
+ for (;;) {
+ struct ofpbuf *buf;
+ int dp_ifindex;
+ 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) {
+ 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_);
+
+ if (dpif->epoll_fd < 0) {
+ return;
+ }
+
+ poll_fd_wait(dpif->epoll_fd, POLLIN);
+}
+
+static void
+dpif_linux_recv_purge(struct dpif *dpif_)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ int i;
+
+ if (dpif->epoll_fd < 0) {
+ return;
+ }
+
+ for (i = 0; i < N_UPCALL_SOCKS; i++) {
+ nl_sock_drain(dpif->upcall_socks[i]);
+ }
+}
+
+const struct dpif_class dpif_linux_class = {
+ "system",
+ dpif_linux_enumerate,
+ dpif_linux_open,
+ dpif_linux_close,
+ dpif_linux_destroy,
+ dpif_linux_run,
+ dpif_linux_wait,
+ dpif_linux_get_stats,
+ dpif_linux_port_add,
+ dpif_linux_port_del,
+ dpif_linux_port_query_by_number,
+ dpif_linux_port_query_by_name,
+ dpif_linux_get_max_ports,
+ dpif_linux_port_get_pid,
+ dpif_linux_port_dump_start,
+ dpif_linux_port_dump_next,
+ dpif_linux_port_dump_done,
+ dpif_linux_port_poll,
+ dpif_linux_port_poll_wait,
+ dpif_linux_flow_get,
+ dpif_linux_flow_put,
+ dpif_linux_flow_del,
+ dpif_linux_flow_flush,
+ dpif_linux_flow_dump_start,
+ dpif_linux_flow_dump_next,
+ dpif_linux_flow_dump_done,
+ dpif_linux_execute,
+ dpif_linux_operate,
+ dpif_linux_recv_set,
+ dpif_linux_queue_to_priority,
+ dpif_linux_recv,
+ dpif_linux_recv_wait,
+ dpif_linux_recv_purge,
+};
+\f
+static int
+dpif_linux_init(void)
+{
+ static int error = -1;
+
+ if (error < 0) {
+ unsigned int ovs_vport_mcgroup;
+
+ error = nl_lookup_genl_family(OVS_DATAPATH_FAMILY,
+ &ovs_datapath_family);
+ if (error) {
+ VLOG_ERR("Generic Netlink family '%s' does not exist. "
+ "The Open vSwitch kernel module is probably not loaded.",
+ OVS_DATAPATH_FAMILY);
+ }