Skip to content

Commit 5cd667b

Browse files
yew011Pravin B Shelar
authored andcommitted
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 <[email protected]> Acked-by: Thomas Graf <[email protected]> Signed-off-by: Pravin B Shelar <[email protected]>
1 parent f6e6753 commit 5cd667b

File tree

4 files changed

+148
-16
lines changed

4 files changed

+148
-16
lines changed

include/uapi/linux/openvswitch.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ struct ovs_vport_stats {
118118
/* Allow last Netlink attribute to be unaligned */
119119
#define OVS_DP_F_UNALIGNED (1 << 0)
120120

121+
/* Allow datapath to associate multiple Netlink PIDs to each vport */
122+
#define OVS_DP_F_VPORT_PIDS (1 << 1)
123+
121124
/* Fixed logical ports. */
122125
#define OVSP_LOCAL ((__u32)0)
123126

@@ -203,9 +206,10 @@ enum ovs_vport_type {
203206
* this is the name of the network device. Maximum length %IFNAMSIZ-1 bytes
204207
* plus a null terminator.
205208
* @OVS_VPORT_ATTR_OPTIONS: Vport-specific configuration information.
206-
* @OVS_VPORT_ATTR_UPCALL_PID: The Netlink socket in userspace that
207-
* OVS_PACKET_CMD_MISS upcalls will be directed to for packets received on
208-
* this port. A value of zero indicates that upcalls should not be sent.
209+
* @OVS_VPORT_ATTR_UPCALL_PID: The array of Netlink socket pids in userspace
210+
* among which OVS_PACKET_CMD_MISS upcalls will be distributed for packets
211+
* received on this port. If this is a single-element array of value 0,
212+
* upcalls should not be sent.
209213
* @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for
210214
* packets sent or received through the vport.
211215
*
@@ -228,7 +232,8 @@ enum ovs_vport_attr {
228232
OVS_VPORT_ATTR_TYPE, /* u32 OVS_VPORT_TYPE_* constant. */
229233
OVS_VPORT_ATTR_NAME, /* string name, up to IFNAMSIZ bytes long */
230234
OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */
231-
OVS_VPORT_ATTR_UPCALL_PID, /* u32 Netlink PID to receive upcalls */
235+
OVS_VPORT_ATTR_UPCALL_PID, /* array of u32 Netlink socket PIDs for */
236+
/* receiving upcalls */
232237
OVS_VPORT_ATTR_STATS, /* struct ovs_vport_stats */
233238
__OVS_VPORT_ATTR_MAX
234239
};

net/openvswitch/datapath.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
266266
upcall.cmd = OVS_PACKET_CMD_MISS;
267267
upcall.key = &key;
268268
upcall.userdata = NULL;
269-
upcall.portid = p->upcall_portid;
269+
upcall.portid = ovs_vport_find_upcall_portid(p, skb);
270270
ovs_dp_upcall(dp, skb, &upcall);
271271
consume_skb(skb);
272272
stats_counter = &stats->n_missed;
@@ -1373,7 +1373,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
13731373
parms.options = NULL;
13741374
parms.dp = dp;
13751375
parms.port_no = OVSP_LOCAL;
1376-
parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]);
1376+
parms.upcall_portids = a[OVS_DP_ATTR_UPCALL_PID];
13771377

13781378
ovs_dp_change(dp, a);
13791379

@@ -1632,15 +1632,18 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
16321632

16331633
if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) ||
16341634
nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) ||
1635-
nla_put_string(skb, OVS_VPORT_ATTR_NAME, vport->ops->get_name(vport)) ||
1636-
nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_portid))
1635+
nla_put_string(skb, OVS_VPORT_ATTR_NAME,
1636+
vport->ops->get_name(vport)))
16371637
goto nla_put_failure;
16381638

16391639
ovs_vport_get_stats(vport, &vport_stats);
16401640
if (nla_put(skb, OVS_VPORT_ATTR_STATS, sizeof(struct ovs_vport_stats),
16411641
&vport_stats))
16421642
goto nla_put_failure;
16431643

1644+
if (ovs_vport_get_upcall_portids(vport, skb))
1645+
goto nla_put_failure;
1646+
16441647
err = ovs_vport_get_options(vport, skb);
16451648
if (err == -EMSGSIZE)
16461649
goto error;
@@ -1762,7 +1765,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
17621765
parms.options = a[OVS_VPORT_ATTR_OPTIONS];
17631766
parms.dp = dp;
17641767
parms.port_no = port_no;
1765-
parms.upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
1768+
parms.upcall_portids = a[OVS_VPORT_ATTR_UPCALL_PID];
17661769

17671770
vport = new_vport(&parms);
17681771
err = PTR_ERR(vport);
@@ -1812,8 +1815,14 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
18121815
goto exit_unlock_free;
18131816
}
18141817

1815-
if (a[OVS_VPORT_ATTR_UPCALL_PID])
1816-
vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
1818+
1819+
if (a[OVS_VPORT_ATTR_UPCALL_PID]) {
1820+
struct nlattr *ids = a[OVS_VPORT_ATTR_UPCALL_PID];
1821+
1822+
err = ovs_vport_set_upcall_portids(vport, ids);
1823+
if (err)
1824+
goto exit_unlock_free;
1825+
}
18171826

18181827
err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
18191828
info->snd_seq, 0, OVS_VPORT_CMD_NEW);

net/openvswitch/vport.c

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,12 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
134134

135135
vport->dp = parms->dp;
136136
vport->port_no = parms->port_no;
137-
vport->upcall_portid = parms->upcall_portid;
138137
vport->ops = ops;
139138
INIT_HLIST_NODE(&vport->dp_hash_node);
140139

140+
if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids))
141+
return ERR_PTR(-EINVAL);
142+
141143
vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
142144
if (!vport->percpu_stats) {
143145
kfree(vport);
@@ -161,6 +163,10 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
161163
*/
162164
void ovs_vport_free(struct vport *vport)
163165
{
166+
/* vport is freed from RCU callback or error path, Therefore
167+
* it is safe to use raw dereference.
168+
*/
169+
kfree(rcu_dereference_raw(vport->upcall_portids));
164170
free_percpu(vport->percpu_stats);
165171
kfree(vport);
166172
}
@@ -326,6 +332,99 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
326332
return 0;
327333
}
328334

335+
/**
336+
* ovs_vport_set_upcall_portids - set upcall portids of @vport.
337+
*
338+
* @vport: vport to modify.
339+
* @ids: new configuration, an array of port ids.
340+
*
341+
* Sets the vport's upcall_portids to @ids.
342+
*
343+
* Returns 0 if successful, -EINVAL if @ids is zero length or cannot be parsed
344+
* as an array of U32.
345+
*
346+
* Must be called with ovs_mutex.
347+
*/
348+
int ovs_vport_set_upcall_portids(struct vport *vport, struct nlattr *ids)
349+
{
350+
struct vport_portids *old, *vport_portids;
351+
352+
if (!nla_len(ids) || nla_len(ids) % sizeof(u32))
353+
return -EINVAL;
354+
355+
old = ovsl_dereference(vport->upcall_portids);
356+
357+
vport_portids = kmalloc(sizeof(*vport_portids) + nla_len(ids),
358+
GFP_KERNEL);
359+
if (!vport_portids)
360+
return -ENOMEM;
361+
362+
vport_portids->n_ids = nla_len(ids) / sizeof(u32);
363+
vport_portids->rn_ids = reciprocal_value(vport_portids->n_ids);
364+
nla_memcpy(vport_portids->ids, ids, nla_len(ids));
365+
366+
rcu_assign_pointer(vport->upcall_portids, vport_portids);
367+
368+
if (old)
369+
kfree_rcu(old, rcu);
370+
return 0;
371+
}
372+
373+
/**
374+
* ovs_vport_get_upcall_portids - get the upcall_portids of @vport.
375+
*
376+
* @vport: vport from which to retrieve the portids.
377+
* @skb: sk_buff where portids should be appended.
378+
*
379+
* Retrieves the configuration of the given vport, appending the
380+
* %OVS_VPORT_ATTR_UPCALL_PID attribute which is the array of upcall
381+
* portids to @skb.
382+
*
383+
* Returns 0 if successful, -EMSGSIZE if @skb has insufficient room.
384+
* If an error occurs, @skb is left unmodified. Must be called with
385+
* ovs_mutex or rcu_read_lock.
386+
*/
387+
int ovs_vport_get_upcall_portids(const struct vport *vport,
388+
struct sk_buff *skb)
389+
{
390+
struct vport_portids *ids;
391+
392+
ids = rcu_dereference_ovsl(vport->upcall_portids);
393+
394+
if (vport->dp->user_features & OVS_DP_F_VPORT_PIDS)
395+
return nla_put(skb, OVS_VPORT_ATTR_UPCALL_PID,
396+
ids->n_ids * sizeof(u32), (void *)ids->ids);
397+
else
398+
return nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, ids->ids[0]);
399+
}
400+
401+
/**
402+
* ovs_vport_find_upcall_portid - find the upcall portid to send upcall.
403+
*
404+
* @vport: vport from which the missed packet is received.
405+
* @skb: skb that the missed packet was received.
406+
*
407+
* Uses the skb_get_hash() to select the upcall portid to send the
408+
* upcall.
409+
*
410+
* Returns the portid of the target socket. Must be called with rcu_read_lock.
411+
*/
412+
u32 ovs_vport_find_upcall_portid(const struct vport *p, struct sk_buff *skb)
413+
{
414+
struct vport_portids *ids;
415+
u32 ids_index;
416+
u32 hash;
417+
418+
ids = rcu_dereference(p->upcall_portids);
419+
420+
if (ids->n_ids == 1 && ids->ids[0] == 0)
421+
return 0;
422+
423+
hash = skb_get_hash(skb);
424+
ids_index = hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids);
425+
return ids->ids[ids_index];
426+
}
427+
329428
/**
330429
* ovs_vport_receive - pass up received packet to the datapath for processing
331430
*

net/openvswitch/vport.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/list.h>
2424
#include <linux/netlink.h>
2525
#include <linux/openvswitch.h>
26+
#include <linux/reciprocal_div.h>
2627
#include <linux/skbuff.h>
2728
#include <linux/spinlock.h>
2829
#include <linux/u64_stats_sync.h>
@@ -52,6 +53,10 @@ void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *);
5253
int ovs_vport_set_options(struct vport *, struct nlattr *options);
5354
int ovs_vport_get_options(const struct vport *, struct sk_buff *);
5455

56+
int ovs_vport_set_upcall_portids(struct vport *, struct nlattr *pids);
57+
int ovs_vport_get_upcall_portids(const struct vport *, struct sk_buff *);
58+
u32 ovs_vport_find_upcall_portid(const struct vport *, struct sk_buff *);
59+
5560
int ovs_vport_send(struct vport *, struct sk_buff *);
5661

5762
/* The following definitions are for implementers of vport devices: */
@@ -62,13 +67,27 @@ struct vport_err_stats {
6267
u64 tx_dropped;
6368
u64 tx_errors;
6469
};
70+
/**
71+
* struct vport_portids - array of netlink portids of a vport.
72+
* must be protected by rcu.
73+
* @rn_ids: The reciprocal value of @n_ids.
74+
* @rcu: RCU callback head for deferred destruction.
75+
* @n_ids: Size of @ids array.
76+
* @ids: Array storing the Netlink socket pids to be used for packets received
77+
* on this port that miss the flow table.
78+
*/
79+
struct vport_portids {
80+
struct reciprocal_value rn_ids;
81+
struct rcu_head rcu;
82+
u32 n_ids;
83+
u32 ids[];
84+
};
6585

6686
/**
6787
* struct vport - one port within a datapath
6888
* @rcu: RCU callback head for deferred destruction.
6989
* @dp: Datapath to which this port belongs.
70-
* @upcall_portid: The Netlink port to use for packets received on this port that
71-
* miss the flow table.
90+
* @upcall_portids: RCU protected 'struct vport_portids'.
7291
* @port_no: Index into @dp's @ports array.
7392
* @hash_node: Element in @dev_table hash table in vport.c.
7493
* @dp_hash_node: Element in @datapath->ports hash table in datapath.c.
@@ -80,7 +99,7 @@ struct vport_err_stats {
8099
struct vport {
81100
struct rcu_head rcu;
82101
struct datapath *dp;
83-
u32 upcall_portid;
102+
struct vport_portids __rcu *upcall_portids;
84103
u16 port_no;
85104

86105
struct hlist_node hash_node;
@@ -111,7 +130,7 @@ struct vport_parms {
111130
/* For ovs_vport_alloc(). */
112131
struct datapath *dp;
113132
u16 port_no;
114-
u32 upcall_portid;
133+
struct nlattr *upcall_portids;
115134
};
116135

117136
/**

0 commit comments

Comments
 (0)