aboutsummaryrefslogtreecommitdiffstats
path: root/net/openvswitch/vport.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/openvswitch/vport.c')
-rw-r--r--net/openvswitch/vport.c103
1 files changed, 102 insertions, 1 deletions
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 42c0f4a0b78c..6d8f2ec481d9 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -134,10 +134,14 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
134 134
135 vport->dp = parms->dp; 135 vport->dp = parms->dp;
136 vport->port_no = parms->port_no; 136 vport->port_no = parms->port_no;
137 vport->upcall_portid = parms->upcall_portid;
138 vport->ops = ops; 137 vport->ops = ops;
139 INIT_HLIST_NODE(&vport->dp_hash_node); 138 INIT_HLIST_NODE(&vport->dp_hash_node);
140 139
140 if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids)) {
141 kfree(vport);
142 return ERR_PTR(-EINVAL);
143 }
144
141 vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 145 vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
142 if (!vport->percpu_stats) { 146 if (!vport->percpu_stats) {
143 kfree(vport); 147 kfree(vport);
@@ -161,6 +165,10 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
161 */ 165 */
162void ovs_vport_free(struct vport *vport) 166void ovs_vport_free(struct vport *vport)
163{ 167{
168 /* vport is freed from RCU callback or error path, Therefore
169 * it is safe to use raw dereference.
170 */
171 kfree(rcu_dereference_raw(vport->upcall_portids));
164 free_percpu(vport->percpu_stats); 172 free_percpu(vport->percpu_stats);
165 kfree(vport); 173 kfree(vport);
166} 174}
@@ -327,6 +335,99 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
327} 335}
328 336
329/** 337/**
338 * ovs_vport_set_upcall_portids - set upcall portids of @vport.
339 *
340 * @vport: vport to modify.
341 * @ids: new configuration, an array of port ids.
342 *
343 * Sets the vport's upcall_portids to @ids.
344 *
345 * Returns 0 if successful, -EINVAL if @ids is zero length or cannot be parsed
346 * as an array of U32.
347 *
348 * Must be called with ovs_mutex.
349 */
350int ovs_vport_set_upcall_portids(struct vport *vport, struct nlattr *ids)
351{
352 struct vport_portids *old, *vport_portids;
353
354 if (!nla_len(ids) || nla_len(ids) % sizeof(u32))
355 return -EINVAL;
356
357 old = ovsl_dereference(vport->upcall_portids);
358
359 vport_portids = kmalloc(sizeof(*vport_portids) + nla_len(ids),
360 GFP_KERNEL);
361 if (!vport_portids)
362 return -ENOMEM;
363
364 vport_portids->n_ids = nla_len(ids) / sizeof(u32);
365 vport_portids->rn_ids = reciprocal_value(vport_portids->n_ids);
366 nla_memcpy(vport_portids->ids, ids, nla_len(ids));
367
368 rcu_assign_pointer(vport->upcall_portids, vport_portids);
369
370 if (old)
371 kfree_rcu(old, rcu);
372 return 0;
373}
374
375/**
376 * ovs_vport_get_upcall_portids - get the upcall_portids of @vport.
377 *
378 * @vport: vport from which to retrieve the portids.
379 * @skb: sk_buff where portids should be appended.
380 *
381 * Retrieves the configuration of the given vport, appending the
382 * %OVS_VPORT_ATTR_UPCALL_PID attribute which is the array of upcall
383 * portids to @skb.
384 *
385 * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room.
386 * If an error occurs, @skb is left unmodified. Must be called with
387 * ovs_mutex or rcu_read_lock.
388 */
389int ovs_vport_get_upcall_portids(const struct vport *vport,
390 struct sk_buff *skb)
391{
392 struct vport_portids *ids;
393
394 ids = rcu_dereference_ovsl(vport->upcall_portids);
395
396 if (vport->dp->user_features & OVS_DP_F_VPORT_PIDS)
397 return nla_put(skb, OVS_VPORT_ATTR_UPCALL_PID,
398 ids->n_ids * sizeof(u32), (void *)ids->ids);
399 else
400 return nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, ids->ids[0]);
401}
402
403/**
404 * ovs_vport_find_upcall_portid - find the upcall portid to send upcall.
405 *
406 * @vport: vport from which the missed packet is received.
407 * @skb: skb that the missed packet was received.
408 *
409 * Uses the skb_get_hash() to select the upcall portid to send the
410 * upcall.
411 *
412 * Returns the portid of the target socket. Must be called with rcu_read_lock.
413 */
414u32 ovs_vport_find_upcall_portid(const struct vport *p, struct sk_buff *skb)
415{
416 struct vport_portids *ids;
417 u32 ids_index;
418 u32 hash;
419
420 ids = rcu_dereference(p->upcall_portids);
421
422 if (ids->n_ids == 1 && ids->ids[0] == 0)
423 return 0;
424
425 hash = skb_get_hash(skb);
426 ids_index = hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids);
427 return ids->ids[ids_index];
428}
429
430/**
330 * ovs_vport_receive - pass up received packet to the datapath for processing 431 * ovs_vport_receive - pass up received packet to the datapath for processing
331 * 432 *
332 * @vport: vport that received the packet 433 * @vport: vport that received the packet