openvswitch: Allow each vport to have an array of 'port_id's.
In order to allow handlers directly read upcalls from datapath, we need to support per-handler netlink socket for each vport in datapath. This commit makes this happen. Also, it is guaranteed to be backward compatible with previous branch. Signed-off-by: Alex Wang <alexw@nicira.com> Acked-by: Thomas Graf <tgraf@redhat.com> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
This commit is contained in:
		
					parent
					
						
							
								f6e6753244
							
						
					
				
			
			
				commit
				
					
						5cd667b0a4
					
				
			
		
					 4 changed files with 148 additions and 16 deletions
				
			
		|  | @ -118,6 +118,9 @@ struct ovs_vport_stats { | ||||||
| /* Allow last Netlink attribute to be unaligned */ | /* Allow last Netlink attribute to be unaligned */ | ||||||
| #define OVS_DP_F_UNALIGNED	(1 << 0) | #define OVS_DP_F_UNALIGNED	(1 << 0) | ||||||
| 
 | 
 | ||||||
|  | /* Allow datapath to associate multiple Netlink PIDs to each vport */ | ||||||
|  | #define OVS_DP_F_VPORT_PIDS	(1 << 1) | ||||||
|  | 
 | ||||||
| /* Fixed logical ports. */ | /* Fixed logical ports. */ | ||||||
| #define OVSP_LOCAL      ((__u32)0) | #define OVSP_LOCAL      ((__u32)0) | ||||||
| 
 | 
 | ||||||
|  | @ -203,9 +206,10 @@ enum ovs_vport_type { | ||||||
|  * this is the name of the network device.  Maximum length %IFNAMSIZ-1 bytes |  * this is the name of the network device.  Maximum length %IFNAMSIZ-1 bytes | ||||||
|  * plus a null terminator. |  * plus a null terminator. | ||||||
|  * @OVS_VPORT_ATTR_OPTIONS: Vport-specific configuration information. |  * @OVS_VPORT_ATTR_OPTIONS: Vport-specific configuration information. | ||||||
|  * @OVS_VPORT_ATTR_UPCALL_PID: The Netlink socket in userspace that |  * @OVS_VPORT_ATTR_UPCALL_PID: The array of Netlink socket pids in userspace | ||||||
|  * OVS_PACKET_CMD_MISS upcalls will be directed to for packets received on |  * among which OVS_PACKET_CMD_MISS upcalls will be distributed for packets | ||||||
|  * this port.  A value of zero indicates that upcalls should not be sent. |  * received on this port.  If this is a single-element array of value 0, | ||||||
|  |  * upcalls should not be sent. | ||||||
|  * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for |  * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for | ||||||
|  * packets sent or received through the vport. |  * packets sent or received through the vport. | ||||||
|  * |  * | ||||||
|  | @ -228,7 +232,8 @@ enum ovs_vport_attr { | ||||||
| 	OVS_VPORT_ATTR_TYPE,	/* u32 OVS_VPORT_TYPE_* constant. */ | 	OVS_VPORT_ATTR_TYPE,	/* u32 OVS_VPORT_TYPE_* constant. */ | ||||||
| 	OVS_VPORT_ATTR_NAME,	/* string name, up to IFNAMSIZ bytes long */ | 	OVS_VPORT_ATTR_NAME,	/* string name, up to IFNAMSIZ bytes long */ | ||||||
| 	OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */ | 	OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */ | ||||||
| 	OVS_VPORT_ATTR_UPCALL_PID, /* u32 Netlink PID to receive upcalls */ | 	OVS_VPORT_ATTR_UPCALL_PID, /* array of u32 Netlink socket PIDs for */ | ||||||
|  | 				/* receiving upcalls */ | ||||||
| 	OVS_VPORT_ATTR_STATS,	/* struct ovs_vport_stats */ | 	OVS_VPORT_ATTR_STATS,	/* struct ovs_vport_stats */ | ||||||
| 	__OVS_VPORT_ATTR_MAX | 	__OVS_VPORT_ATTR_MAX | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -266,7 +266,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) | ||||||
| 		upcall.cmd = OVS_PACKET_CMD_MISS; | 		upcall.cmd = OVS_PACKET_CMD_MISS; | ||||||
| 		upcall.key = &key; | 		upcall.key = &key; | ||||||
| 		upcall.userdata = NULL; | 		upcall.userdata = NULL; | ||||||
| 		upcall.portid = p->upcall_portid; | 		upcall.portid = ovs_vport_find_upcall_portid(p, skb); | ||||||
| 		ovs_dp_upcall(dp, skb, &upcall); | 		ovs_dp_upcall(dp, skb, &upcall); | ||||||
| 		consume_skb(skb); | 		consume_skb(skb); | ||||||
| 		stats_counter = &stats->n_missed; | 		stats_counter = &stats->n_missed; | ||||||
|  | @ -1373,7 +1373,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | ||||||
| 	parms.options = NULL; | 	parms.options = NULL; | ||||||
| 	parms.dp = dp; | 	parms.dp = dp; | ||||||
| 	parms.port_no = OVSP_LOCAL; | 	parms.port_no = OVSP_LOCAL; | ||||||
| 	parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]); | 	parms.upcall_portids = a[OVS_DP_ATTR_UPCALL_PID]; | ||||||
| 
 | 
 | ||||||
| 	ovs_dp_change(dp, a); | 	ovs_dp_change(dp, a); | ||||||
| 
 | 
 | ||||||
|  | @ -1632,8 +1632,8 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, | ||||||
| 
 | 
 | ||||||
| 	if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || | 	if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || | ||||||
| 	    nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) || | 	    nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) || | ||||||
| 	    nla_put_string(skb, OVS_VPORT_ATTR_NAME, vport->ops->get_name(vport)) || | 	    nla_put_string(skb, OVS_VPORT_ATTR_NAME, | ||||||
| 	    nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_portid)) | 			   vport->ops->get_name(vport))) | ||||||
| 		goto nla_put_failure; | 		goto nla_put_failure; | ||||||
| 
 | 
 | ||||||
| 	ovs_vport_get_stats(vport, &vport_stats); | 	ovs_vport_get_stats(vport, &vport_stats); | ||||||
|  | @ -1641,6 +1641,9 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, | ||||||
| 		    &vport_stats)) | 		    &vport_stats)) | ||||||
| 		goto nla_put_failure; | 		goto nla_put_failure; | ||||||
| 
 | 
 | ||||||
|  | 	if (ovs_vport_get_upcall_portids(vport, skb)) | ||||||
|  | 		goto nla_put_failure; | ||||||
|  | 
 | ||||||
| 	err = ovs_vport_get_options(vport, skb); | 	err = ovs_vport_get_options(vport, skb); | ||||||
| 	if (err == -EMSGSIZE) | 	if (err == -EMSGSIZE) | ||||||
| 		goto error; | 		goto error; | ||||||
|  | @ -1762,7 +1765,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) | ||||||
| 	parms.options = a[OVS_VPORT_ATTR_OPTIONS]; | 	parms.options = a[OVS_VPORT_ATTR_OPTIONS]; | ||||||
| 	parms.dp = dp; | 	parms.dp = dp; | ||||||
| 	parms.port_no = port_no; | 	parms.port_no = port_no; | ||||||
| 	parms.upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); | 	parms.upcall_portids = a[OVS_VPORT_ATTR_UPCALL_PID]; | ||||||
| 
 | 
 | ||||||
| 	vport = new_vport(&parms); | 	vport = new_vport(&parms); | ||||||
| 	err = PTR_ERR(vport); | 	err = PTR_ERR(vport); | ||||||
|  | @ -1812,8 +1815,14 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) | ||||||
| 			goto exit_unlock_free; | 			goto exit_unlock_free; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (a[OVS_VPORT_ATTR_UPCALL_PID]) | 
 | ||||||
| 		vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); | 	if (a[OVS_VPORT_ATTR_UPCALL_PID]) { | ||||||
|  | 		struct nlattr *ids = a[OVS_VPORT_ATTR_UPCALL_PID]; | ||||||
|  | 
 | ||||||
|  | 		err = ovs_vport_set_upcall_portids(vport, ids); | ||||||
|  | 		if (err) | ||||||
|  | 			goto exit_unlock_free; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, | 	err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, | ||||||
| 				      info->snd_seq, 0, OVS_VPORT_CMD_NEW); | 				      info->snd_seq, 0, OVS_VPORT_CMD_NEW); | ||||||
|  |  | ||||||
|  | @ -134,10 +134,12 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, | ||||||
| 
 | 
 | ||||||
| 	vport->dp = parms->dp; | 	vport->dp = parms->dp; | ||||||
| 	vport->port_no = parms->port_no; | 	vport->port_no = parms->port_no; | ||||||
| 	vport->upcall_portid = parms->upcall_portid; |  | ||||||
| 	vport->ops = ops; | 	vport->ops = ops; | ||||||
| 	INIT_HLIST_NODE(&vport->dp_hash_node); | 	INIT_HLIST_NODE(&vport->dp_hash_node); | ||||||
| 
 | 
 | ||||||
|  | 	if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids)) | ||||||
|  | 		return ERR_PTR(-EINVAL); | ||||||
|  | 
 | ||||||
| 	vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); | 	vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); | ||||||
| 	if (!vport->percpu_stats) { | 	if (!vport->percpu_stats) { | ||||||
| 		kfree(vport); | 		kfree(vport); | ||||||
|  | @ -161,6 +163,10 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, | ||||||
|  */ |  */ | ||||||
| void ovs_vport_free(struct vport *vport) | void ovs_vport_free(struct vport *vport) | ||||||
| { | { | ||||||
|  | 	/* vport is freed from RCU callback or error path, Therefore
 | ||||||
|  | 	 * it is safe to use raw dereference. | ||||||
|  | 	 */ | ||||||
|  | 	kfree(rcu_dereference_raw(vport->upcall_portids)); | ||||||
| 	free_percpu(vport->percpu_stats); | 	free_percpu(vport->percpu_stats); | ||||||
| 	kfree(vport); | 	kfree(vport); | ||||||
| } | } | ||||||
|  | @ -326,6 +332,99 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  *	ovs_vport_set_upcall_portids - set upcall portids of @vport. | ||||||
|  |  * | ||||||
|  |  * @vport: vport to modify. | ||||||
|  |  * @ids: new configuration, an array of port ids. | ||||||
|  |  * | ||||||
|  |  * Sets the vport's upcall_portids to @ids. | ||||||
|  |  * | ||||||
|  |  * Returns 0 if successful, -EINVAL if @ids is zero length or cannot be parsed | ||||||
|  |  * as an array of U32. | ||||||
|  |  * | ||||||
|  |  * Must be called with ovs_mutex. | ||||||
|  |  */ | ||||||
|  | int ovs_vport_set_upcall_portids(struct vport *vport,  struct nlattr *ids) | ||||||
|  | { | ||||||
|  | 	struct vport_portids *old, *vport_portids; | ||||||
|  | 
 | ||||||
|  | 	if (!nla_len(ids) || nla_len(ids) % sizeof(u32)) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	old = ovsl_dereference(vport->upcall_portids); | ||||||
|  | 
 | ||||||
|  | 	vport_portids = kmalloc(sizeof(*vport_portids) + nla_len(ids), | ||||||
|  | 				GFP_KERNEL); | ||||||
|  | 	if (!vport_portids) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	vport_portids->n_ids = nla_len(ids) / sizeof(u32); | ||||||
|  | 	vport_portids->rn_ids = reciprocal_value(vport_portids->n_ids); | ||||||
|  | 	nla_memcpy(vport_portids->ids, ids, nla_len(ids)); | ||||||
|  | 
 | ||||||
|  | 	rcu_assign_pointer(vport->upcall_portids, vport_portids); | ||||||
|  | 
 | ||||||
|  | 	if (old) | ||||||
|  | 		kfree_rcu(old, rcu); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  *	ovs_vport_get_upcall_portids - get the upcall_portids of @vport. | ||||||
|  |  * | ||||||
|  |  * @vport: vport from which to retrieve the portids. | ||||||
|  |  * @skb: sk_buff where portids should be appended. | ||||||
|  |  * | ||||||
|  |  * Retrieves the configuration of the given vport, appending the | ||||||
|  |  * %OVS_VPORT_ATTR_UPCALL_PID attribute which is the array of upcall | ||||||
|  |  * portids to @skb. | ||||||
|  |  * | ||||||
|  |  * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room. | ||||||
|  |  * If an error occurs, @skb is left unmodified.  Must be called with | ||||||
|  |  * ovs_mutex or rcu_read_lock. | ||||||
|  |  */ | ||||||
|  | int ovs_vport_get_upcall_portids(const struct vport *vport, | ||||||
|  | 				 struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct vport_portids *ids; | ||||||
|  | 
 | ||||||
|  | 	ids = rcu_dereference_ovsl(vport->upcall_portids); | ||||||
|  | 
 | ||||||
|  | 	if (vport->dp->user_features & OVS_DP_F_VPORT_PIDS) | ||||||
|  | 		return nla_put(skb, OVS_VPORT_ATTR_UPCALL_PID, | ||||||
|  | 			       ids->n_ids * sizeof(u32), (void *)ids->ids); | ||||||
|  | 	else | ||||||
|  | 		return nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, ids->ids[0]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  *	ovs_vport_find_upcall_portid - find the upcall portid to send upcall. | ||||||
|  |  * | ||||||
|  |  * @vport: vport from which the missed packet is received. | ||||||
|  |  * @skb: skb that the missed packet was received. | ||||||
|  |  * | ||||||
|  |  * Uses the skb_get_hash() to select the upcall portid to send the | ||||||
|  |  * upcall. | ||||||
|  |  * | ||||||
|  |  * Returns the portid of the target socket.  Must be called with rcu_read_lock. | ||||||
|  |  */ | ||||||
|  | u32 ovs_vport_find_upcall_portid(const struct vport *p, struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct vport_portids *ids; | ||||||
|  | 	u32 ids_index; | ||||||
|  | 	u32 hash; | ||||||
|  | 
 | ||||||
|  | 	ids = rcu_dereference(p->upcall_portids); | ||||||
|  | 
 | ||||||
|  | 	if (ids->n_ids == 1 && ids->ids[0] == 0) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	hash = skb_get_hash(skb); | ||||||
|  | 	ids_index = hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids); | ||||||
|  | 	return ids->ids[ids_index]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  *	ovs_vport_receive - pass up received packet to the datapath for processing |  *	ovs_vport_receive - pass up received packet to the datapath for processing | ||||||
|  * |  * | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ | ||||||
| #include <linux/list.h> | #include <linux/list.h> | ||||||
| #include <linux/netlink.h> | #include <linux/netlink.h> | ||||||
| #include <linux/openvswitch.h> | #include <linux/openvswitch.h> | ||||||
|  | #include <linux/reciprocal_div.h> | ||||||
| #include <linux/skbuff.h> | #include <linux/skbuff.h> | ||||||
| #include <linux/spinlock.h> | #include <linux/spinlock.h> | ||||||
| #include <linux/u64_stats_sync.h> | #include <linux/u64_stats_sync.h> | ||||||
|  | @ -52,6 +53,10 @@ void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *); | ||||||
| int ovs_vport_set_options(struct vport *, struct nlattr *options); | int ovs_vport_set_options(struct vport *, struct nlattr *options); | ||||||
| int ovs_vport_get_options(const struct vport *, struct sk_buff *); | int ovs_vport_get_options(const struct vport *, struct sk_buff *); | ||||||
| 
 | 
 | ||||||
|  | int ovs_vport_set_upcall_portids(struct vport *, struct nlattr *pids); | ||||||
|  | int ovs_vport_get_upcall_portids(const struct vport *, struct sk_buff *); | ||||||
|  | u32 ovs_vport_find_upcall_portid(const struct vport *, struct sk_buff *); | ||||||
|  | 
 | ||||||
| int ovs_vport_send(struct vport *, struct sk_buff *); | int ovs_vport_send(struct vport *, struct sk_buff *); | ||||||
| 
 | 
 | ||||||
| /* The following definitions are for implementers of vport devices: */ | /* The following definitions are for implementers of vport devices: */ | ||||||
|  | @ -62,13 +67,27 @@ struct vport_err_stats { | ||||||
| 	u64 tx_dropped; | 	u64 tx_dropped; | ||||||
| 	u64 tx_errors; | 	u64 tx_errors; | ||||||
| }; | }; | ||||||
|  | /**
 | ||||||
|  |  * struct vport_portids - array of netlink portids of a vport. | ||||||
|  |  *                        must be protected by rcu. | ||||||
|  |  * @rn_ids: The reciprocal value of @n_ids. | ||||||
|  |  * @rcu: RCU callback head for deferred destruction. | ||||||
|  |  * @n_ids: Size of @ids array. | ||||||
|  |  * @ids: Array storing the Netlink socket pids to be used for packets received | ||||||
|  |  * on this port that miss the flow table. | ||||||
|  |  */ | ||||||
|  | struct vport_portids { | ||||||
|  | 	struct reciprocal_value rn_ids; | ||||||
|  | 	struct rcu_head rcu; | ||||||
|  | 	u32 n_ids; | ||||||
|  | 	u32 ids[]; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * struct vport - one port within a datapath |  * struct vport - one port within a datapath | ||||||
|  * @rcu: RCU callback head for deferred destruction. |  * @rcu: RCU callback head for deferred destruction. | ||||||
|  * @dp: Datapath to which this port belongs. |  * @dp: Datapath to which this port belongs. | ||||||
|  * @upcall_portid: The Netlink port to use for packets received on this port that |  * @upcall_portids: RCU protected 'struct vport_portids'. | ||||||
|  * miss the flow table. |  | ||||||
|  * @port_no: Index into @dp's @ports array. |  * @port_no: Index into @dp's @ports array. | ||||||
|  * @hash_node: Element in @dev_table hash table in vport.c. |  * @hash_node: Element in @dev_table hash table in vport.c. | ||||||
|  * @dp_hash_node: Element in @datapath->ports hash table in datapath.c. |  * @dp_hash_node: Element in @datapath->ports hash table in datapath.c. | ||||||
|  | @ -80,7 +99,7 @@ struct vport_err_stats { | ||||||
| struct vport { | struct vport { | ||||||
| 	struct rcu_head rcu; | 	struct rcu_head rcu; | ||||||
| 	struct datapath	*dp; | 	struct datapath	*dp; | ||||||
| 	u32 upcall_portid; | 	struct vport_portids __rcu *upcall_portids; | ||||||
| 	u16 port_no; | 	u16 port_no; | ||||||
| 
 | 
 | ||||||
| 	struct hlist_node hash_node; | 	struct hlist_node hash_node; | ||||||
|  | @ -111,7 +130,7 @@ struct vport_parms { | ||||||
| 	/* For ovs_vport_alloc(). */ | 	/* For ovs_vport_alloc(). */ | ||||||
| 	struct datapath *dp; | 	struct datapath *dp; | ||||||
| 	u16 port_no; | 	u16 port_no; | ||||||
| 	u32 upcall_portid; | 	struct nlattr *upcall_portids; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alex Wang
				Alex Wang