diff options
Diffstat (limited to 'net/openvswitch')
| -rw-r--r-- | net/openvswitch/Kconfig | 1 | ||||
| -rw-r--r-- | net/openvswitch/conntrack.c | 251 | ||||
| -rw-r--r-- | net/openvswitch/datapath.c | 45 | ||||
| -rw-r--r-- | net/openvswitch/flow_netlink.c | 2 | ||||
| -rw-r--r-- | net/openvswitch/flow_table.c | 9 | ||||
| -rw-r--r-- | net/openvswitch/vport-geneve.c | 2 | ||||
| -rw-r--r-- | net/openvswitch/vport-gre.c | 2 | ||||
| -rw-r--r-- | net/openvswitch/vport-netdev.c | 2 | ||||
| -rw-r--r-- | net/openvswitch/vport-vxlan.c | 2 | ||||
| -rw-r--r-- | net/openvswitch/vport.c | 50 | ||||
| -rw-r--r-- | net/openvswitch/vport.h | 16 | 
11 files changed, 138 insertions, 244 deletions
| diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig index 15bd287f5cbd..747d537a3f06 100644 --- a/net/openvswitch/Kconfig +++ b/net/openvswitch/Kconfig @@ -15,6 +15,7 @@ config OPENVSWITCH  	select NET_MPLS_GSO  	select DST_CACHE  	select NET_NSH +	select NF_NAT_OVS if NF_NAT  	help  	  Open vSwitch is a multilayer Ethernet switch targeted at virtualized  	  environments.  In addition to supporting a variety of features diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index c8eaf4234b2e..c8b137649ca4 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -434,65 +434,6 @@ static int ovs_ct_set_labels(struct nf_conn *ct, struct sw_flow_key *key,  	return 0;  } -/* 'skb' should already be pulled to nh_ofs. */ -static int ovs_ct_helper(struct sk_buff *skb, u16 proto) -{ -	const struct nf_conntrack_helper *helper; -	const struct nf_conn_help *help; -	enum ip_conntrack_info ctinfo; -	unsigned int protoff; -	struct nf_conn *ct; -	int err; - -	ct = nf_ct_get(skb, &ctinfo); -	if (!ct || ctinfo == IP_CT_RELATED_REPLY) -		return NF_ACCEPT; - -	help = nfct_help(ct); -	if (!help) -		return NF_ACCEPT; - -	helper = rcu_dereference(help->helper); -	if (!helper) -		return NF_ACCEPT; - -	switch (proto) { -	case NFPROTO_IPV4: -		protoff = ip_hdrlen(skb); -		break; -	case NFPROTO_IPV6: { -		u8 nexthdr = ipv6_hdr(skb)->nexthdr; -		__be16 frag_off; -		int ofs; - -		ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, -				       &frag_off); -		if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { -			pr_debug("proto header not found\n"); -			return NF_ACCEPT; -		} -		protoff = ofs; -		break; -	} -	default: -		WARN_ONCE(1, "helper invoked on non-IP family!"); -		return NF_DROP; -	} - -	err = helper->help(skb, protoff, ct, ctinfo); -	if (err != NF_ACCEPT) -		return err; - -	/* Adjust seqs after helper.  This is needed due to some helpers (e.g., -	 * FTP with NAT) adusting the TCP payload size when mangling IP -	 * addresses and/or port numbers in the text-based control connection. -	 */ -	if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && -	    !nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) -		return NF_DROP; -	return NF_ACCEPT; -} -  /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero   * value if 'skb' is freed.   */ @@ -785,147 +726,27 @@ static void ovs_nat_update_key(struct sw_flow_key *key,  	}  } -/* Modelled after nf_nat_ipv[46]_fn(). - * range is only used for new, uninitialized NAT state. - * Returns either NF_ACCEPT or NF_DROP. - */ -static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, -			      enum ip_conntrack_info ctinfo, -			      const struct nf_nat_range2 *range, -			      enum nf_nat_manip_type maniptype, struct sw_flow_key *key) -{ -	int hooknum, nh_off, err = NF_ACCEPT; - -	nh_off = skb_network_offset(skb); -	skb_pull_rcsum(skb, nh_off); - -	/* See HOOK2MANIP(). */ -	if (maniptype == NF_NAT_MANIP_SRC) -		hooknum = NF_INET_LOCAL_IN; /* Source NAT */ -	else -		hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */ - -	switch (ctinfo) { -	case IP_CT_RELATED: -	case IP_CT_RELATED_REPLY: -		if (IS_ENABLED(CONFIG_NF_NAT) && -		    skb->protocol == htons(ETH_P_IP) && -		    ip_hdr(skb)->protocol == IPPROTO_ICMP) { -			if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, -							   hooknum)) -				err = NF_DROP; -			goto push; -		} else if (IS_ENABLED(CONFIG_IPV6) && -			   skb->protocol == htons(ETH_P_IPV6)) { -			__be16 frag_off; -			u8 nexthdr = ipv6_hdr(skb)->nexthdr; -			int hdrlen = ipv6_skip_exthdr(skb, -						      sizeof(struct ipv6hdr), -						      &nexthdr, &frag_off); - -			if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { -				if (!nf_nat_icmpv6_reply_translation(skb, ct, -								     ctinfo, -								     hooknum, -								     hdrlen)) -					err = NF_DROP; -				goto push; -			} -		} -		/* Non-ICMP, fall thru to initialize if needed. */ -		fallthrough; -	case IP_CT_NEW: -		/* Seen it before?  This can happen for loopback, retrans, -		 * or local packets. -		 */ -		if (!nf_nat_initialized(ct, maniptype)) { -			/* Initialize according to the NAT action. */ -			err = (range && range->flags & NF_NAT_RANGE_MAP_IPS) -				/* Action is set up to establish a new -				 * mapping. -				 */ -				? nf_nat_setup_info(ct, range, maniptype) -				: nf_nat_alloc_null_binding(ct, hooknum); -			if (err != NF_ACCEPT) -				goto push; -		} -		break; - -	case IP_CT_ESTABLISHED: -	case IP_CT_ESTABLISHED_REPLY: -		break; - -	default: -		err = NF_DROP; -		goto push; -	} - -	err = nf_nat_packet(ct, ctinfo, hooknum, skb); -push: -	skb_push_rcsum(skb, nh_off); - -	/* Update the flow key if NAT successful. */ -	if (err == NF_ACCEPT) -		ovs_nat_update_key(key, skb, maniptype); - -	return err; -} -  /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */  static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,  		      const struct ovs_conntrack_info *info,  		      struct sk_buff *skb, struct nf_conn *ct,  		      enum ip_conntrack_info ctinfo)  { -	enum nf_nat_manip_type maniptype; -	int err; +	int err, action = 0; -	/* Add NAT extension if not confirmed yet. */ -	if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) -		return NF_ACCEPT;   /* Can't NAT. */ +	if (!(info->nat & OVS_CT_NAT)) +		return NF_ACCEPT; +	if (info->nat & OVS_CT_SRC_NAT) +		action |= BIT(NF_NAT_MANIP_SRC); +	if (info->nat & OVS_CT_DST_NAT) +		action |= BIT(NF_NAT_MANIP_DST); -	/* Determine NAT type. -	 * Check if the NAT type can be deduced from the tracked connection. -	 * Make sure new expected connections (IP_CT_RELATED) are NATted only -	 * when committing. -	 */ -	if (info->nat & OVS_CT_NAT && ctinfo != IP_CT_NEW && -	    ct->status & IPS_NAT_MASK && -	    (ctinfo != IP_CT_RELATED || info->commit)) { -		/* NAT an established or related connection like before. */ -		if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) -			/* This is the REPLY direction for a connection -			 * for which NAT was applied in the forward -			 * direction.  Do the reverse NAT. -			 */ -			maniptype = ct->status & IPS_SRC_NAT -				? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC; -		else -			maniptype = ct->status & IPS_SRC_NAT -				? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST; -	} else if (info->nat & OVS_CT_SRC_NAT) { -		maniptype = NF_NAT_MANIP_SRC; -	} else if (info->nat & OVS_CT_DST_NAT) { -		maniptype = NF_NAT_MANIP_DST; -	} else { -		return NF_ACCEPT; /* Connection is not NATed. */ -	} -	err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype, key); - -	if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { -		if (ct->status & IPS_SRC_NAT) { -			if (maniptype == NF_NAT_MANIP_SRC) -				maniptype = NF_NAT_MANIP_DST; -			else -				maniptype = NF_NAT_MANIP_SRC; - -			err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, -						 maniptype, key); -		} else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { -			err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL, -						 NF_NAT_MANIP_SRC, key); -		} -	} +	err = nf_ct_nat(skb, ct, ctinfo, &action, &info->range, info->commit); + +	if (action & BIT(NF_NAT_MANIP_SRC)) +		ovs_nat_update_key(key, skb, NF_NAT_MANIP_SRC); +	if (action & BIT(NF_NAT_MANIP_DST)) +		ovs_nat_update_key(key, skb, NF_NAT_MANIP_DST);  	return err;  } @@ -1038,7 +859,7 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key,  		 */  		if ((nf_ct_is_confirmed(ct) ? !cached || add_helper :  					      info->commit) && -		    ovs_ct_helper(skb, info->family) != NF_ACCEPT) { +		    nf_ct_helper(skb, ct, ctinfo, info->family) != NF_ACCEPT) {  			return -EINVAL;  		} @@ -1350,43 +1171,6 @@ int ovs_ct_clear(struct sk_buff *skb, struct sw_flow_key *key)  	return 0;  } -static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, -			     const struct sw_flow_key *key, bool log) -{ -	struct nf_conntrack_helper *helper; -	struct nf_conn_help *help; -	int ret = 0; - -	helper = nf_conntrack_helper_try_module_get(name, info->family, -						    key->ip.proto); -	if (!helper) { -		OVS_NLERR(log, "Unknown helper \"%s\"", name); -		return -EINVAL; -	} - -	help = nf_ct_helper_ext_add(info->ct, GFP_KERNEL); -	if (!help) { -		nf_conntrack_helper_put(helper); -		return -ENOMEM; -	} - -#if IS_ENABLED(CONFIG_NF_NAT) -	if (info->nat) { -		ret = nf_nat_helper_try_module_get(name, info->family, -						   key->ip.proto); -		if (ret) { -			nf_conntrack_helper_put(helper); -			OVS_NLERR(log, "Failed to load \"%s\" NAT helper, error: %d", -				  name, ret); -			return ret; -		} -	} -#endif -	rcu_assign_pointer(help->helper, helper); -	info->helper = helper; -	return ret; -} -  #if IS_ENABLED(CONFIG_NF_NAT)  static int parse_nat(const struct nlattr *attr,  		     struct ovs_conntrack_info *info, bool log) @@ -1720,9 +1504,12 @@ int ovs_ct_copy_action(struct net *net, const struct nlattr *attr,  	}  	if (helper) { -		err = ovs_ct_add_helper(&ct_info, helper, key, log); -		if (err) +		err = nf_ct_add_helper(ct_info.ct, helper, ct_info.family, +				       key->ip.proto, ct_info.nat, &ct_info.helper); +		if (err) { +			OVS_NLERR(log, "Failed to add %s helper %d", helper, err);  			goto err_free_ct; +		}  	}  	err = ovs_nla_add_action(sfa, OVS_ACTION_ATTR_CT, &ct_info, diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 8b84869eb2ac..932bcf766d63 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -209,6 +209,26 @@ static struct vport *new_vport(const struct vport_parms *parms)  	return vport;  } +static void ovs_vport_update_upcall_stats(struct sk_buff *skb, +					  const struct dp_upcall_info *upcall_info, +					  bool upcall_result) +{ +	struct vport *p = OVS_CB(skb)->input_vport; +	struct vport_upcall_stats_percpu *stats; + +	if (upcall_info->cmd != OVS_PACKET_CMD_MISS && +	    upcall_info->cmd != OVS_PACKET_CMD_ACTION) +		return; + +	stats = this_cpu_ptr(p->upcall_stats); +	u64_stats_update_begin(&stats->syncp); +	if (upcall_result) +		u64_stats_inc(&stats->n_success); +	else +		u64_stats_inc(&stats->n_fail); +	u64_stats_update_end(&stats->syncp); +} +  void ovs_dp_detach_port(struct vport *p)  {  	ASSERT_OVSL(); @@ -216,6 +236,9 @@ void ovs_dp_detach_port(struct vport *p)  	/* First drop references to device. */  	hlist_del_rcu(&p->dp_hash_node); +	/* Free percpu memory */ +	free_percpu(p->upcall_stats); +  	/* Then destroy it. */  	ovs_vport_del(p);  } @@ -305,6 +328,8 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,  		err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);  	else  		err = queue_gso_packets(dp, skb, key, upcall_info, cutlen); + +	ovs_vport_update_upcall_stats(skb, upcall_info, !err);  	if (err)  		goto err; @@ -716,9 +741,9 @@ static void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats,  		percpu_stats = per_cpu_ptr(dp->stats_percpu, i);  		do { -			start = u64_stats_fetch_begin_irq(&percpu_stats->syncp); +			start = u64_stats_fetch_begin(&percpu_stats->syncp);  			local_stats = *percpu_stats; -		} while (u64_stats_fetch_retry_irq(&percpu_stats->syncp, start)); +		} while (u64_stats_fetch_retry(&percpu_stats->syncp, start));  		stats->n_hit += local_stats.n_hit;  		stats->n_missed += local_stats.n_missed; @@ -1826,6 +1851,12 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)  		goto err_destroy_portids;  	} +	vport->upcall_stats = netdev_alloc_pcpu_stats(struct vport_upcall_stats_percpu); +	if (!vport->upcall_stats) { +		err = -ENOMEM; +		goto err_destroy_portids; +	} +  	err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,  				   info->snd_seq, 0, OVS_DP_CMD_NEW);  	BUG_ON(err < 0); @@ -2098,6 +2129,9 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,  			  OVS_VPORT_ATTR_PAD))  		goto nla_put_failure; +	if (ovs_vport_get_upcall_stats(vport, skb)) +		goto nla_put_failure; +  	if (ovs_vport_get_upcall_portids(vport, skb))  		goto nla_put_failure; @@ -2279,6 +2313,12 @@ restart:  		goto exit_unlock_free;  	} +	vport->upcall_stats = netdev_alloc_pcpu_stats(struct vport_upcall_stats_percpu); +	if (!vport->upcall_stats) { +		err = -ENOMEM; +		goto exit_unlock_free; +	} +  	err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),  				      info->snd_portid, info->snd_seq, 0,  				      OVS_VPORT_CMD_NEW, GFP_KERNEL); @@ -2508,6 +2548,7 @@ static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {  	[OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },  	[OVS_VPORT_ATTR_IFINDEX] = { .type = NLA_U32 },  	[OVS_VPORT_ATTR_NETNSID] = { .type = NLA_S32 }, +	[OVS_VPORT_ATTR_UPCALL_STATS] = { .type = NLA_NESTED },  };  static const struct genl_small_ops dp_vport_genl_ops[] = { diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 4a07ab094a84..ead5418c126e 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2309,7 +2309,7 @@ static struct sw_flow_actions *nla_alloc_flow_actions(int size)  	WARN_ON_ONCE(size > MAX_ACTIONS_BUFSIZE); -	sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL); +	sfa = kmalloc(kmalloc_size_roundup(sizeof(*sfa) + size), GFP_KERNEL);  	if (!sfa)  		return ERR_PTR(-ENOMEM); diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index d4a2db0b2299..0a0e4c283f02 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c @@ -205,9 +205,9 @@ static void tbl_mask_array_reset_counters(struct mask_array *ma)  			stats = per_cpu_ptr(ma->masks_usage_stats, cpu);  			do { -				start = u64_stats_fetch_begin_irq(&stats->syncp); +				start = u64_stats_fetch_begin(&stats->syncp);  				counter = stats->usage_cntrs[i]; -			} while (u64_stats_fetch_retry_irq(&stats->syncp, start)); +			} while (u64_stats_fetch_retry(&stats->syncp, start));  			ma->masks_usage_zero_cntr[i] += counter;  		} @@ -1136,10 +1136,9 @@ void ovs_flow_masks_rebalance(struct flow_table *table)  			stats = per_cpu_ptr(ma->masks_usage_stats, cpu);  			do { -				start = u64_stats_fetch_begin_irq(&stats->syncp); +				start = u64_stats_fetch_begin(&stats->syncp);  				counter = stats->usage_cntrs[i]; -			} while (u64_stats_fetch_retry_irq(&stats->syncp, -							   start)); +			} while (u64_stats_fetch_retry(&stats->syncp, start));  			masks_and_count[i].counter += counter;  		} diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c index 89a8e1501809..b10e1602c6b1 100644 --- a/net/openvswitch/vport-geneve.c +++ b/net/openvswitch/vport-geneve.c @@ -91,7 +91,7 @@ static struct vport *geneve_tnl_create(const struct vport_parms *parms)  	err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);  	if (err < 0) { -		rtnl_delete_link(dev); +		rtnl_delete_link(dev, 0, NULL);  		rtnl_unlock();  		ovs_vport_free(vport);  		goto error; diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index e6b5e76a962a..4014c9b5eb79 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -57,7 +57,7 @@ static struct vport *gre_tnl_create(const struct vport_parms *parms)  	err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);  	if (err < 0) { -		rtnl_delete_link(dev); +		rtnl_delete_link(dev, 0, NULL);  		rtnl_unlock();  		ovs_vport_free(vport);  		return ERR_PTR(err); diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 2f61d5bdce1a..903537a5da22 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -172,7 +172,7 @@ void ovs_netdev_tunnel_destroy(struct vport *vport)  	 * if it's not already shutting down.  	 */  	if (vport->dev->reg_state == NETREG_REGISTERED) -		rtnl_delete_link(vport->dev); +		rtnl_delete_link(vport->dev, 0, NULL);  	netdev_put(vport->dev, &vport->dev_tracker);  	vport->dev = NULL;  	rtnl_unlock(); diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 188e9c1360a1..0b881b043bcf 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -120,7 +120,7 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms)  	err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);  	if (err < 0) { -		rtnl_delete_link(dev); +		rtnl_delete_link(dev, 0, NULL);  		rtnl_unlock();  		ovs_vport_free(vport);  		goto error; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 82a74f998966..7e0f5c45b512 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -285,6 +285,56 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)  }  /** + *	ovs_vport_get_upcall_stats - retrieve upcall stats + * + * @vport: vport from which to retrieve the stats. + * @skb: sk_buff where upcall stats should be appended. + * + * Retrieves upcall stats for the given device. + * + * Must be called with ovs_mutex or rcu_read_lock. + */ +int ovs_vport_get_upcall_stats(struct vport *vport, struct sk_buff *skb) +{ +	struct nlattr *nla; +	int i; + +	__u64 tx_success = 0; +	__u64 tx_fail = 0; + +	for_each_possible_cpu(i) { +		const struct vport_upcall_stats_percpu *stats; +		unsigned int start; + +		stats = per_cpu_ptr(vport->upcall_stats, i); +		do { +			start = u64_stats_fetch_begin(&stats->syncp); +			tx_success += u64_stats_read(&stats->n_success); +			tx_fail += u64_stats_read(&stats->n_fail); +		} while (u64_stats_fetch_retry(&stats->syncp, start)); +	} + +	nla = nla_nest_start_noflag(skb, OVS_VPORT_ATTR_UPCALL_STATS); +	if (!nla) +		return -EMSGSIZE; + +	if (nla_put_u64_64bit(skb, OVS_VPORT_UPCALL_ATTR_SUCCESS, tx_success, +			      OVS_VPORT_ATTR_PAD)) { +		nla_nest_cancel(skb, nla); +		return -EMSGSIZE; +	} + +	if (nla_put_u64_64bit(skb, OVS_VPORT_UPCALL_ATTR_FAIL, tx_fail, +			      OVS_VPORT_ATTR_PAD)) { +		nla_nest_cancel(skb, nla); +		return -EMSGSIZE; +	} +	nla_nest_end(skb, nla); + +	return 0; +} + +/**   *	ovs_vport_get_options - retrieve device options   *   * @vport: vport from which to retrieve the options. diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 6ff45e8a0868..3e71ca8ad8a7 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -32,6 +32,8 @@ struct vport *ovs_vport_locate(const struct net *net, const char *name);  void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *); +int ovs_vport_get_upcall_stats(struct vport *vport, struct sk_buff *skb); +  int ovs_vport_set_options(struct vport *, struct nlattr *options);  int ovs_vport_get_options(const struct vport *, struct sk_buff *); @@ -65,6 +67,7 @@ struct vport_portids {   * @hash_node: Element in @dev_table hash table in vport.c.   * @dp_hash_node: Element in @datapath->ports hash table in datapath.c.   * @ops: Class structure. + * @upcall_stats: Upcall stats of every ports.   * @detach_list: list used for detaching vport in net-exit call.   * @rcu: RCU callback head for deferred destruction.   */ @@ -78,6 +81,7 @@ struct vport {  	struct hlist_node hash_node;  	struct hlist_node dp_hash_node;  	const struct vport_ops *ops; +	struct vport_upcall_stats_percpu __percpu *upcall_stats;  	struct list_head detach_list;  	struct rcu_head rcu; @@ -137,6 +141,18 @@ struct vport_ops {  	struct list_head list;  }; +/** + * struct vport_upcall_stats_percpu - per-cpu packet upcall statistics for + * a given vport. + * @n_success: Number of packets that upcall to userspace succeed. + * @n_fail:    Number of packets that upcall to userspace failed. + */ +struct vport_upcall_stats_percpu { +	struct u64_stats_sync syncp; +	u64_stats_t n_success; +	u64_stats_t n_fail; +}; +  struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *,  			      const struct vport_parms *);  void ovs_vport_free(struct vport *); |