diff options
Diffstat (limited to 'net/openvswitch/vport.c')
| -rw-r--r-- | net/openvswitch/vport.c | 103 |
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 | */ |
| 162 | void ovs_vport_free(struct vport *vport) | 166 | void 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 | */ | ||
| 350 | int 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 | */ | ||
| 389 | int 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 | */ | ||
| 414 | u32 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 |
