+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ int error;
+ int i;
+
+ if (listen_mask == dpif->listen_mask) {
+ return 0;
+ } else if (!listen_mask) {
+ nl_sock_destroy(dpif->mc_sock);
+ dpif->mc_sock = NULL;
+ dpif->listen_mask = 0;
+ return 0;
+ } else if (!dpif->mc_sock) {
+ error = nl_sock_create(NETLINK_GENERIC, &dpif->mc_sock);
+ if (error) {
+ return error;
+ }
+ }
+
+ /* Unsubscribe from old groups. */
+ for (i = 0; i < DPIF_N_UC_TYPES; i++) {
+ if (dpif->listen_mask & (1u << i)) {
+ nl_sock_leave_mcgroup(dpif->mc_sock, dpif->mcgroups[i]);
+ }
+ }
+
+ /* Update listen_mask. */
+ dpif->listen_mask = listen_mask;
+
+ /* Subscribe to new groups. */
+ error = 0;
+ for (i = 0; i < DPIF_N_UC_TYPES; i++) {
+ if (dpif->listen_mask & (1u << i)) {
+ int retval;
+
+ retval = nl_sock_join_mcgroup(dpif->mc_sock, dpif->mcgroups[i]);
+ if (retval) {
+ error = retval;
+ }
+ }
+ }
+ return error;
+}
+
+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;