diff options
| author | Andy Zhou <azhou@ovn.org> | 2017-11-10 15:09:42 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2017-11-12 20:37:07 -0500 |
| commit | 96fbc13d7e770b542d2d1fcf700d0baadc6e8063 (patch) | |
| tree | a5af29f888531a46399e77f4cbf63fdbf36adb3e /net/openvswitch | |
| parent | 9602c01e57f7b868d748c2ba2aef0efa64b71ffc (diff) | |
openvswitch: Add meter infrastructure
OVS kernel datapath so far does not support Openflow meter action.
This is the first stab at adding kernel datapath meter support.
This implementation supports only drop band type.
Signed-off-by: Andy Zhou <azhou@ovn.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/openvswitch')
| -rw-r--r-- | net/openvswitch/Makefile | 1 | ||||
| -rw-r--r-- | net/openvswitch/datapath.c | 14 | ||||
| -rw-r--r-- | net/openvswitch/datapath.h | 3 | ||||
| -rw-r--r-- | net/openvswitch/meter.c | 604 | ||||
| -rw-r--r-- | net/openvswitch/meter.h | 54 |
5 files changed, 674 insertions, 2 deletions
diff --git a/net/openvswitch/Makefile b/net/openvswitch/Makefile index 299f4476cf44..41109c326f3a 100644 --- a/net/openvswitch/Makefile +++ b/net/openvswitch/Makefile | |||
| @@ -12,6 +12,7 @@ openvswitch-y := \ | |||
| 12 | flow.o \ | 12 | flow.o \ |
| 13 | flow_netlink.o \ | 13 | flow_netlink.o \ |
| 14 | flow_table.o \ | 14 | flow_table.o \ |
| 15 | meter.o \ | ||
| 15 | vport.o \ | 16 | vport.o \ |
| 16 | vport-internal_dev.o \ | 17 | vport-internal_dev.o \ |
| 17 | vport-netdev.o | 18 | vport-netdev.o |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 6e098035bb8f..0dab33fb9844 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
| @@ -55,6 +55,7 @@ | |||
| 55 | #include "flow.h" | 55 | #include "flow.h" |
| 56 | #include "flow_table.h" | 56 | #include "flow_table.h" |
| 57 | #include "flow_netlink.h" | 57 | #include "flow_netlink.h" |
| 58 | #include "meter.h" | ||
| 58 | #include "vport-internal_dev.h" | 59 | #include "vport-internal_dev.h" |
| 59 | #include "vport-netdev.h" | 60 | #include "vport-netdev.h" |
| 60 | 61 | ||
| @@ -174,6 +175,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu) | |||
| 174 | ovs_flow_tbl_destroy(&dp->table); | 175 | ovs_flow_tbl_destroy(&dp->table); |
| 175 | free_percpu(dp->stats_percpu); | 176 | free_percpu(dp->stats_percpu); |
| 176 | kfree(dp->ports); | 177 | kfree(dp->ports); |
| 178 | ovs_meters_exit(dp); | ||
| 177 | kfree(dp); | 179 | kfree(dp); |
| 178 | } | 180 | } |
| 179 | 181 | ||
| @@ -1572,6 +1574,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
| 1572 | for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) | 1574 | for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) |
| 1573 | INIT_HLIST_HEAD(&dp->ports[i]); | 1575 | INIT_HLIST_HEAD(&dp->ports[i]); |
| 1574 | 1576 | ||
| 1577 | err = ovs_meters_init(dp); | ||
| 1578 | if (err) | ||
| 1579 | goto err_destroy_ports_array; | ||
| 1580 | |||
| 1575 | /* Set up our datapath device. */ | 1581 | /* Set up our datapath device. */ |
| 1576 | parms.name = nla_data(a[OVS_DP_ATTR_NAME]); | 1582 | parms.name = nla_data(a[OVS_DP_ATTR_NAME]); |
| 1577 | parms.type = OVS_VPORT_TYPE_INTERNAL; | 1583 | parms.type = OVS_VPORT_TYPE_INTERNAL; |
| @@ -1600,7 +1606,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
| 1600 | ovs_dp_reset_user_features(skb, info); | 1606 | ovs_dp_reset_user_features(skb, info); |
| 1601 | } | 1607 | } |
| 1602 | 1608 | ||
| 1603 | goto err_destroy_ports_array; | 1609 | goto err_destroy_meters; |
| 1604 | } | 1610 | } |
| 1605 | 1611 | ||
| 1606 | err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, | 1612 | err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, |
| @@ -1615,8 +1621,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
| 1615 | ovs_notify(&dp_datapath_genl_family, reply, info); | 1621 | ovs_notify(&dp_datapath_genl_family, reply, info); |
| 1616 | return 0; | 1622 | return 0; |
| 1617 | 1623 | ||
| 1618 | err_destroy_ports_array: | 1624 | err_destroy_meters: |
| 1619 | ovs_unlock(); | 1625 | ovs_unlock(); |
| 1626 | ovs_meters_exit(dp); | ||
| 1627 | err_destroy_ports_array: | ||
| 1620 | kfree(dp->ports); | 1628 | kfree(dp->ports); |
| 1621 | err_destroy_percpu: | 1629 | err_destroy_percpu: |
| 1622 | free_percpu(dp->stats_percpu); | 1630 | free_percpu(dp->stats_percpu); |
| @@ -2265,6 +2273,7 @@ static struct genl_family * const dp_genl_families[] = { | |||
| 2265 | &dp_vport_genl_family, | 2273 | &dp_vport_genl_family, |
| 2266 | &dp_flow_genl_family, | 2274 | &dp_flow_genl_family, |
| 2267 | &dp_packet_genl_family, | 2275 | &dp_packet_genl_family, |
| 2276 | &dp_meter_genl_family, | ||
| 2268 | }; | 2277 | }; |
| 2269 | 2278 | ||
| 2270 | static void dp_unregister_genl(int n_families) | 2279 | static void dp_unregister_genl(int n_families) |
| @@ -2445,3 +2454,4 @@ MODULE_ALIAS_GENL_FAMILY(OVS_DATAPATH_FAMILY); | |||
| 2445 | MODULE_ALIAS_GENL_FAMILY(OVS_VPORT_FAMILY); | 2454 | MODULE_ALIAS_GENL_FAMILY(OVS_VPORT_FAMILY); |
| 2446 | MODULE_ALIAS_GENL_FAMILY(OVS_FLOW_FAMILY); | 2455 | MODULE_ALIAS_GENL_FAMILY(OVS_FLOW_FAMILY); |
| 2447 | MODULE_ALIAS_GENL_FAMILY(OVS_PACKET_FAMILY); | 2456 | MODULE_ALIAS_GENL_FAMILY(OVS_PACKET_FAMILY); |
| 2457 | MODULE_ALIAS_GENL_FAMILY(OVS_METER_FAMILY); | ||
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 954c4ed465a5..5d2997b42460 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h | |||
| @@ -92,6 +92,9 @@ struct datapath { | |||
| 92 | u32 user_features; | 92 | u32 user_features; |
| 93 | 93 | ||
| 94 | u32 max_headroom; | 94 | u32 max_headroom; |
| 95 | |||
| 96 | /* Switch meters. */ | ||
| 97 | struct hlist_head *meters; | ||
| 95 | }; | 98 | }; |
| 96 | 99 | ||
| 97 | /** | 100 | /** |
diff --git a/net/openvswitch/meter.c b/net/openvswitch/meter.c new file mode 100644 index 000000000000..2a5ba356c472 --- /dev/null +++ b/net/openvswitch/meter.c | |||
| @@ -0,0 +1,604 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2017 Nicira, Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or | ||
| 5 | * modify it under the terms of version 2 of the GNU General Public | ||
| 6 | * License as published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 10 | |||
| 11 | #include <linux/if.h> | ||
| 12 | #include <linux/skbuff.h> | ||
| 13 | #include <linux/ip.h> | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/openvswitch.h> | ||
| 16 | #include <linux/netlink.h> | ||
| 17 | #include <linux/rculist.h> | ||
| 18 | |||
| 19 | #include <net/netlink.h> | ||
| 20 | #include <net/genetlink.h> | ||
| 21 | |||
| 22 | #include "datapath.h" | ||
| 23 | #include "meter.h" | ||
| 24 | |||
| 25 | #define METER_HASH_BUCKETS 1024 | ||
| 26 | |||
| 27 | static const struct nla_policy meter_policy[OVS_METER_ATTR_MAX + 1] = { | ||
| 28 | [OVS_METER_ATTR_ID] = { .type = NLA_U32, }, | ||
| 29 | [OVS_METER_ATTR_KBPS] = { .type = NLA_FLAG }, | ||
| 30 | [OVS_METER_ATTR_STATS] = { .len = sizeof(struct ovs_flow_stats) }, | ||
| 31 | [OVS_METER_ATTR_BANDS] = { .type = NLA_NESTED }, | ||
| 32 | [OVS_METER_ATTR_USED] = { .type = NLA_U64 }, | ||
| 33 | [OVS_METER_ATTR_CLEAR] = { .type = NLA_FLAG }, | ||
| 34 | [OVS_METER_ATTR_MAX_METERS] = { .type = NLA_U32 }, | ||
| 35 | [OVS_METER_ATTR_MAX_BANDS] = { .type = NLA_U32 }, | ||
| 36 | }; | ||
| 37 | |||
| 38 | static const struct nla_policy band_policy[OVS_BAND_ATTR_MAX + 1] = { | ||
| 39 | [OVS_BAND_ATTR_TYPE] = { .type = NLA_U32, }, | ||
| 40 | [OVS_BAND_ATTR_RATE] = { .type = NLA_U32, }, | ||
| 41 | [OVS_BAND_ATTR_BURST] = { .type = NLA_U32, }, | ||
| 42 | [OVS_BAND_ATTR_STATS] = { .len = sizeof(struct ovs_flow_stats) }, | ||
| 43 | }; | ||
| 44 | |||
| 45 | static void rcu_free_ovs_meter_callback(struct rcu_head *rcu) | ||
| 46 | { | ||
| 47 | struct dp_meter *meter = container_of(rcu, struct dp_meter, rcu); | ||
| 48 | |||
| 49 | kfree(meter); | ||
| 50 | } | ||
| 51 | |||
| 52 | static void ovs_meter_free(struct dp_meter *meter) | ||
| 53 | { | ||
| 54 | if (!meter) | ||
| 55 | return; | ||
| 56 | |||
| 57 | call_rcu(&meter->rcu, rcu_free_ovs_meter_callback); | ||
| 58 | } | ||
| 59 | |||
| 60 | static struct hlist_head *meter_hash_bucket(const struct datapath *dp, | ||
| 61 | u32 meter_id) | ||
| 62 | { | ||
| 63 | return &dp->meters[meter_id & (METER_HASH_BUCKETS - 1)]; | ||
| 64 | } | ||
| 65 | |||
| 66 | /* Call with ovs_mutex or RCU read lock. */ | ||
| 67 | static struct dp_meter *lookup_meter(const struct datapath *dp, | ||
| 68 | u32 meter_id) | ||
| 69 | { | ||
| 70 | struct dp_meter *meter; | ||
| 71 | struct hlist_head *head; | ||
| 72 | |||
| 73 | head = meter_hash_bucket(dp, meter_id); | ||
| 74 | hlist_for_each_entry_rcu(meter, head, dp_hash_node) { | ||
| 75 | if (meter->id == meter_id) | ||
| 76 | return meter; | ||
| 77 | } | ||
| 78 | return NULL; | ||
| 79 | } | ||
| 80 | |||
| 81 | static void attach_meter(struct datapath *dp, struct dp_meter *meter) | ||
| 82 | { | ||
| 83 | struct hlist_head *head = meter_hash_bucket(dp, meter->id); | ||
| 84 | |||
| 85 | hlist_add_head_rcu(&meter->dp_hash_node, head); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void detach_meter(struct dp_meter *meter) | ||
| 89 | { | ||
| 90 | ASSERT_OVSL(); | ||
| 91 | if (meter) | ||
| 92 | hlist_del_rcu(&meter->dp_hash_node); | ||
| 93 | } | ||
| 94 | |||
| 95 | static struct sk_buff * | ||
| 96 | ovs_meter_cmd_reply_start(struct genl_info *info, u8 cmd, | ||
| 97 | struct ovs_header **ovs_reply_header) | ||
