aboutsummaryrefslogtreecommitdiffstats
path: root/net/openvswitch
diff options
context:
space:
mode:
authorAndy Zhou <azhou@ovn.org>2017-11-10 15:09:42 -0500
committerDavid S. Miller <davem@davemloft.net>2017-11-12 20:37:07 -0500
commit96fbc13d7e770b542d2d1fcf700d0baadc6e8063 (patch)
treea5af29f888531a46399e77f4cbf63fdbf36adb3e /net/openvswitch
parent9602c01e57f7b868d748c2ba2aef0efa64b71ffc (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/Makefile1
-rw-r--r--net/openvswitch/datapath.c14
-rw-r--r--net/openvswitch/datapath.h3
-rw-r--r--net/openvswitch/meter.c604
-rw-r--r--net/openvswitch/meter.h54
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
1618err_destroy_ports_array: 1624err_destroy_meters:
1619 ovs_unlock(); 1625 ovs_unlock();
1626 ovs_meters_exit(dp);
1627err_destroy_ports_array:
1620 kfree(dp->ports); 1628 kfree(dp->ports);
1621err_destroy_percpu: 1629err_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
2270static void dp_unregister_genl(int n_families) 2279static void dp_unregister_genl(int n_families)
@@ -2445,3 +2454,4 @@ MODULE_ALIAS_GENL_FAMILY(OVS_DATAPATH_FAMILY);
2445MODULE_ALIAS_GENL_FAMILY(OVS_VPORT_FAMILY); 2454MODULE_ALIAS_GENL_FAMILY(OVS_VPORT_FAMILY);
2446MODULE_ALIAS_GENL_FAMILY(OVS_FLOW_FAMILY); 2455MODULE_ALIAS_GENL_FAMILY(OVS_FLOW_FAMILY);
2447MODULE_ALIAS_GENL_FAMILY(OVS_PACKET_FAMILY); 2456MODULE_ALIAS_GENL_FAMILY(OVS_PACKET_FAMILY);
2457MODULE_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
27static 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
38static 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
45static 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
52static 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
60static 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. */
67static 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
81static 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
88static void detach_meter(struct dp_meter *meter)
89{
90 ASSERT_OVSL();
91 if (meter)
92 hlist_del_rcu(&meter->dp_hash_node);
93}
94
95static struct sk_buff *
96ovs_meter_cmd_reply_start(struct genl_info *info, u8 cmd,
97 struct ovs_header **ovs_reply_header)