static int output_userspace(struct datapath *dp, struct sk_buff *skb, const struct nlattr *attr) { struct dp_upcall_info upcall; const struct nlattr *a; int rem; upcall.cmd = OVS_PACKET_CMD_ACTION; upcall.key = &OVS_CB(skb)->flow->key; upcall.userdata = NULL; upcall.pid = 0; for (a = nla_data(attr), rem = nla_len(attr); rem > 0; a = nla_next(a, &rem)) { switch (nla_type(a)) { case OVS_USERSPACE_ATTR_USERDATA: upcall.userdata = a; break; case OVS_USERSPACE_ATTR_PID: upcall.pid = nla_get_u32(a); break; } } return ovs_dp_upcall(dp, skb, &upcall); }
/* called from BPF program, therefore rcu_read_lock is held * bpf_check() verified that 'buf' pointer to BPF's stack * and it has 'len' bytes for us to read */ void bpf_channel_push_struct(struct bpf_context *pctx, u32 struct_id, const void *buf, u32 len) { struct bpf_dp_context *ctx = container_of(pctx, struct bpf_dp_context, context); struct dp_upcall_info upcall; struct plum *plum; struct nlattr *nla; if (unlikely(!ctx->skb)) return; plum = rcu_dereference(ctx->dp->plums[pctx->plum_id]); if (unlikely(!plum)) return; /* allocate temp nlattr to pass it into ovs_dp_upcall */ nla = kzalloc(nla_total_size(4 + len), GFP_ATOMIC); if (unlikely(!nla)) return; nla->nla_type = OVS_PACKET_ATTR_USERDATA; nla->nla_len = nla_attr_size(4 + len); memcpy(nla_data(nla), &struct_id, 4); memcpy(nla_data(nla) + 4, buf, len); upcall.cmd = OVS_PACKET_CMD_ACTION; upcall.key = NULL; upcall.userdata = nla; upcall.portid = plum->upcall_pid; ovs_dp_upcall(ctx->dp, NULL, &upcall); kfree(nla); }
/* Must be called with rcu_read_lock. */ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) { struct datapath *dp = p->dp; struct sw_flow *flow; struct dp_stats_percpu *stats; u64 *stats_counter; int error; stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id()); if (!OVS_CB(skb)->flow) { struct sw_flow_key key; int key_len; /* Extract flow from 'skb' into 'key'. */ error = ovs_flow_extract(skb, p->port_no, &key, &key_len); if (unlikely(error)) { kfree_skb(skb); return; } /* Look up flow. */ flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table), &key, key_len); if (unlikely(!flow)) { struct dp_upcall_info upcall; upcall.cmd = OVS_PACKET_CMD_MISS; upcall.key = &key; upcall.userdata = NULL; upcall.pid = p->upcall_pid; ovs_dp_upcall(dp, skb, &upcall); consume_skb(skb); stats_counter = &stats->n_missed; goto out; } OVS_CB(skb)->flow = flow; } stats_counter = &stats->n_hit; ovs_flow_used(OVS_CB(skb)->flow, skb); ovs_execute_actions(dp, skb); out: /* Update datapath statistics. */ u64_stats_update_begin(&stats->sync); (*stats_counter)++; u64_stats_update_end(&stats->sync); }
/* called from BPF program, therefore rcu_read_lock is held */ void bpf_channel_push_packet(struct bpf_context *pctx) { struct bpf_dp_context *ctx = container_of(pctx, struct bpf_dp_context, context); struct dp_upcall_info upcall; struct sk_buff *nskb; struct plum *plum; if (unlikely(!ctx->skb)) return; plum = rcu_dereference(ctx->dp->plums[pctx->plum_id]); if (unlikely(!plum)) return; /* queue_gso_packets() inside ovs_dp_upcall() changes skb, * so copy it here, since BPF program might still be using it */ nskb = skb_clone(ctx->skb, GFP_ATOMIC); if (unlikely(!nskb)) return; upcall.cmd = OVS_PACKET_CMD_ACTION; upcall.key = NULL; upcall.userdata = NULL; upcall.portid = plum->upcall_pid; /* don't exit earlier even if upcall_pid is invalid, * since we want 'lost' count to be incremented */ /* disable softirq to make sure that genlmsg_unicast()->gfp_any() picks * GFP_ATOMIC flag * note that bpf_channel_push_struct() doesn't need to do it, * since skb==NULL */ local_bh_disable(); ovs_dp_upcall(ctx->dp, nskb, &upcall); local_bh_enable(); consume_skb(nskb); }