+ return get_upcall_pid_port__(dpif, port);
+}
+
+static void
+set_upcall_pids(struct dpif_linux *dpif)
+{
+ struct dpif_port port;
+ struct dpif_port_dump port_dump;
+ struct dpif_flow_dump flow_dump;
+ const struct nlattr *key;
+ size_t key_len;
+ int error;
+
+ DPIF_PORT_FOR_EACH (&port, &port_dump, &dpif->dpif) {
+ 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 = get_upcall_pid_port(dpif,
+ vport_request.port_no);
+ 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,
+ vport_request.upcall_pid);
+ } else {
+ VLOG_WARN_RL(&error_rl, "%s: failed to set upcall pid on port: %s",
+ dpif_name(&dpif->dpif), strerror(error));
+ }
+ }
+
+ dpif_flow_dump_start(&flow_dump, &dpif->dpif);
+ while (dpif_flow_dump_next(&flow_dump, &key, &key_len,
+ NULL, NULL, NULL)) {
+ struct dpif_linux_flow flow_request;
+
+ dpif_linux_flow_init(&flow_request);
+ flow_request.cmd = OVS_FLOW_CMD_SET;
+ flow_request.dp_ifindex = dpif->dp_ifindex;
+ flow_request.key = key;
+ flow_request.key_len = key_len;
+ flow_request.upcall_pid = get_upcall_pid_flow(dpif, key, key_len);
+ error = dpif_linux_flow_transact(&flow_request, NULL, NULL);
+ if (error) {
+ VLOG_WARN_RL(&error_rl, "%s: failed to set upcall pid on flow: %s",
+ dpif_name(&dpif->dpif), strerror(error));
+ }
+ }
+ dpif_flow_dump_done(&flow_dump);
+}
+
+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_get_sflow_probability(const struct dpif *dpif_,
+ uint32_t *probability)
+{
+ struct dpif_linux_dp dp;
+ struct ofpbuf *buf;
+ int error;
+
+ error = dpif_linux_dp_get(dpif_, &dp, &buf);
+ if (!error) {
+ *probability = dp.sampling ? *dp.sampling : 0;
+ ofpbuf_delete(buf);
+ }
+ return error;
+}
+
+static int
+dpif_linux_set_sflow_probability(struct dpif *dpif_, uint32_t probability)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ struct dpif_linux_dp dp;
+
+ dpif_linux_dp_init(&dp);
+ dp.cmd = OVS_DP_CMD_SET;
+ dp.dp_ifindex = dpif->dp_ifindex;
+ dp.sampling = &probability;
+ return dpif_linux_dp_transact(&dp, NULL, NULL);
+}
+
+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 },
+
+ /* OVS_PACKET_CMD_SAMPLE only. */
+ [OVS_PACKET_ATTR_SAMPLE_POOL] = { .type = NL_A_U32, .optional = true },
+ [OVS_PACKET_ATTR_ACTIONS] = { .type = NL_A_NESTED, .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
+ : genl->cmd == OVS_PACKET_CMD_SAMPLE ? DPIF_UC_SAMPLE
+ : -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);
+ upcall->sample_pool = (a[OVS_PACKET_ATTR_SAMPLE_POOL]
+ ? nl_attr_get_u32(a[OVS_PACKET_ATTR_SAMPLE_POOL])
+ : 0);
+ if (a[OVS_PACKET_ATTR_ACTIONS]) {
+ upcall->actions = (void *) nl_attr_get(a[OVS_PACKET_ATTR_ACTIONS]);
+ upcall->actions_len = nl_attr_get_size(a[OVS_PACKET_ATTR_ACTIONS]);
+ }
+
+ *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;