18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2017 Nicira, Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/if.h> 98c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 108c2ecf20Sopenharmony_ci#include <linux/ip.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/openvswitch.h> 138c2ecf20Sopenharmony_ci#include <linux/netlink.h> 148c2ecf20Sopenharmony_ci#include <linux/rculist.h> 158c2ecf20Sopenharmony_ci#include <linux/swap.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <net/netlink.h> 188c2ecf20Sopenharmony_ci#include <net/genetlink.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "datapath.h" 218c2ecf20Sopenharmony_ci#include "meter.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic const struct nla_policy meter_policy[OVS_METER_ATTR_MAX + 1] = { 248c2ecf20Sopenharmony_ci [OVS_METER_ATTR_ID] = { .type = NLA_U32, }, 258c2ecf20Sopenharmony_ci [OVS_METER_ATTR_KBPS] = { .type = NLA_FLAG }, 268c2ecf20Sopenharmony_ci [OVS_METER_ATTR_STATS] = { .len = sizeof(struct ovs_flow_stats) }, 278c2ecf20Sopenharmony_ci [OVS_METER_ATTR_BANDS] = { .type = NLA_NESTED }, 288c2ecf20Sopenharmony_ci [OVS_METER_ATTR_USED] = { .type = NLA_U64 }, 298c2ecf20Sopenharmony_ci [OVS_METER_ATTR_CLEAR] = { .type = NLA_FLAG }, 308c2ecf20Sopenharmony_ci [OVS_METER_ATTR_MAX_METERS] = { .type = NLA_U32 }, 318c2ecf20Sopenharmony_ci [OVS_METER_ATTR_MAX_BANDS] = { .type = NLA_U32 }, 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic const struct nla_policy band_policy[OVS_BAND_ATTR_MAX + 1] = { 358c2ecf20Sopenharmony_ci [OVS_BAND_ATTR_TYPE] = { .type = NLA_U32, }, 368c2ecf20Sopenharmony_ci [OVS_BAND_ATTR_RATE] = { .type = NLA_U32, }, 378c2ecf20Sopenharmony_ci [OVS_BAND_ATTR_BURST] = { .type = NLA_U32, }, 388c2ecf20Sopenharmony_ci [OVS_BAND_ATTR_STATS] = { .len = sizeof(struct ovs_flow_stats) }, 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic u32 meter_hash(struct dp_meter_instance *ti, u32 id) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci return id % ti->n_meters; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void ovs_meter_free(struct dp_meter *meter) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci if (!meter) 498c2ecf20Sopenharmony_ci return; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci kfree_rcu(meter, rcu); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Call with ovs_mutex or RCU read lock. */ 558c2ecf20Sopenharmony_cistatic struct dp_meter *lookup_meter(const struct dp_meter_table *tbl, 568c2ecf20Sopenharmony_ci u32 meter_id) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti); 598c2ecf20Sopenharmony_ci u32 hash = meter_hash(ti, meter_id); 608c2ecf20Sopenharmony_ci struct dp_meter *meter; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci meter = rcu_dereference_ovsl(ti->dp_meters[hash]); 638c2ecf20Sopenharmony_ci if (meter && likely(meter->id == meter_id)) 648c2ecf20Sopenharmony_ci return meter; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return NULL; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic struct dp_meter_instance *dp_meter_instance_alloc(const u32 size) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct dp_meter_instance *ti; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci ti = kvzalloc(sizeof(*ti) + 748c2ecf20Sopenharmony_ci sizeof(struct dp_meter *) * size, 758c2ecf20Sopenharmony_ci GFP_KERNEL); 768c2ecf20Sopenharmony_ci if (!ti) 778c2ecf20Sopenharmony_ci return NULL; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ti->n_meters = size; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return ti; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void dp_meter_instance_free(struct dp_meter_instance *ti) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci kvfree(ti); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void dp_meter_instance_free_rcu(struct rcu_head *rcu) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct dp_meter_instance *ti; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ti = container_of(rcu, struct dp_meter_instance, rcu); 948c2ecf20Sopenharmony_ci kvfree(ti); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int 988c2ecf20Sopenharmony_cidp_meter_instance_realloc(struct dp_meter_table *tbl, u32 size) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti); 1018c2ecf20Sopenharmony_ci int n_meters = min(size, ti->n_meters); 1028c2ecf20Sopenharmony_ci struct dp_meter_instance *new_ti; 1038c2ecf20Sopenharmony_ci int i; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci new_ti = dp_meter_instance_alloc(size); 1068c2ecf20Sopenharmony_ci if (!new_ti) 1078c2ecf20Sopenharmony_ci return -ENOMEM; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (i = 0; i < n_meters; i++) 1108c2ecf20Sopenharmony_ci if (rcu_dereference_ovsl(ti->dp_meters[i])) 1118c2ecf20Sopenharmony_ci new_ti->dp_meters[i] = ti->dp_meters[i]; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci rcu_assign_pointer(tbl->ti, new_ti); 1148c2ecf20Sopenharmony_ci call_rcu(&ti->rcu, dp_meter_instance_free_rcu); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void dp_meter_instance_insert(struct dp_meter_instance *ti, 1208c2ecf20Sopenharmony_ci struct dp_meter *meter) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci u32 hash; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci hash = meter_hash(ti, meter->id); 1258c2ecf20Sopenharmony_ci rcu_assign_pointer(ti->dp_meters[hash], meter); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void dp_meter_instance_remove(struct dp_meter_instance *ti, 1298c2ecf20Sopenharmony_ci struct dp_meter *meter) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci u32 hash; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci hash = meter_hash(ti, meter->id); 1348c2ecf20Sopenharmony_ci RCU_INIT_POINTER(ti->dp_meters[hash], NULL); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti); 1408c2ecf20Sopenharmony_ci u32 hash = meter_hash(ti, meter->id); 1418c2ecf20Sopenharmony_ci int err; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* In generally, slots selected should be empty, because 1448c2ecf20Sopenharmony_ci * OvS uses id-pool to fetch a available id. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci if (unlikely(rcu_dereference_ovsl(ti->dp_meters[hash]))) 1478c2ecf20Sopenharmony_ci return -EBUSY; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci dp_meter_instance_insert(ti, meter); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* That function is thread-safe. */ 1528c2ecf20Sopenharmony_ci tbl->count++; 1538c2ecf20Sopenharmony_ci if (tbl->count >= tbl->max_meters_allowed) { 1548c2ecf20Sopenharmony_ci err = -EFBIG; 1558c2ecf20Sopenharmony_ci goto attach_err; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (tbl->count >= ti->n_meters && 1598c2ecf20Sopenharmony_ci dp_meter_instance_realloc(tbl, ti->n_meters * 2)) { 1608c2ecf20Sopenharmony_ci err = -ENOMEM; 1618c2ecf20Sopenharmony_ci goto attach_err; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ciattach_err: 1678c2ecf20Sopenharmony_ci dp_meter_instance_remove(ti, meter); 1688c2ecf20Sopenharmony_ci tbl->count--; 1698c2ecf20Sopenharmony_ci return err; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int detach_meter(struct dp_meter_table *tbl, struct dp_meter *meter) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct dp_meter_instance *ti; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ASSERT_OVSL(); 1778c2ecf20Sopenharmony_ci if (!meter) 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ti = rcu_dereference_ovsl(tbl->ti); 1818c2ecf20Sopenharmony_ci dp_meter_instance_remove(ti, meter); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci tbl->count--; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Shrink the meter array if necessary. */ 1868c2ecf20Sopenharmony_ci if (ti->n_meters > DP_METER_ARRAY_SIZE_MIN && 1878c2ecf20Sopenharmony_ci tbl->count <= (ti->n_meters / 4)) { 1888c2ecf20Sopenharmony_ci int half_size = ti->n_meters / 2; 1898c2ecf20Sopenharmony_ci int i; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Avoid hash collision, don't move slots to other place. 1928c2ecf20Sopenharmony_ci * Make sure there are no references of meters in array 1938c2ecf20Sopenharmony_ci * which will be released. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci for (i = half_size; i < ti->n_meters; i++) 1968c2ecf20Sopenharmony_ci if (rcu_dereference_ovsl(ti->dp_meters[i])) 1978c2ecf20Sopenharmony_ci goto out; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (dp_meter_instance_realloc(tbl, half_size)) 2008c2ecf20Sopenharmony_ci goto shrink_err; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ciout: 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cishrink_err: 2078c2ecf20Sopenharmony_ci dp_meter_instance_insert(ti, meter); 2088c2ecf20Sopenharmony_ci tbl->count++; 2098c2ecf20Sopenharmony_ci return -ENOMEM; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic struct sk_buff * 2138c2ecf20Sopenharmony_ciovs_meter_cmd_reply_start(struct genl_info *info, u8 cmd, 2148c2ecf20Sopenharmony_ci struct ovs_header **ovs_reply_header) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct sk_buff *skb; 2178c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 2208c2ecf20Sopenharmony_ci if (!skb) 2218c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci *ovs_reply_header = genlmsg_put(skb, info->snd_portid, 2248c2ecf20Sopenharmony_ci info->snd_seq, 2258c2ecf20Sopenharmony_ci &dp_meter_genl_family, 0, cmd); 2268c2ecf20Sopenharmony_ci if (!*ovs_reply_header) { 2278c2ecf20Sopenharmony_ci nlmsg_free(skb); 2288c2ecf20Sopenharmony_ci return ERR_PTR(-EMSGSIZE); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci (*ovs_reply_header)->dp_ifindex = ovs_header->dp_ifindex; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return skb; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int ovs_meter_cmd_reply_stats(struct sk_buff *reply, u32 meter_id, 2368c2ecf20Sopenharmony_ci struct dp_meter *meter) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct nlattr *nla; 2398c2ecf20Sopenharmony_ci struct dp_meter_band *band; 2408c2ecf20Sopenharmony_ci u16 i; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (nla_put_u32(reply, OVS_METER_ATTR_ID, meter_id)) 2438c2ecf20Sopenharmony_ci goto error; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (nla_put(reply, OVS_METER_ATTR_STATS, 2468c2ecf20Sopenharmony_ci sizeof(struct ovs_flow_stats), &meter->stats)) 2478c2ecf20Sopenharmony_ci goto error; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (nla_put_u64_64bit(reply, OVS_METER_ATTR_USED, meter->used, 2508c2ecf20Sopenharmony_ci OVS_METER_ATTR_PAD)) 2518c2ecf20Sopenharmony_ci goto error; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci nla = nla_nest_start_noflag(reply, OVS_METER_ATTR_BANDS); 2548c2ecf20Sopenharmony_ci if (!nla) 2558c2ecf20Sopenharmony_ci goto error; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci band = meter->bands; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci for (i = 0; i < meter->n_bands; ++i, ++band) { 2608c2ecf20Sopenharmony_ci struct nlattr *band_nla; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci band_nla = nla_nest_start_noflag(reply, OVS_BAND_ATTR_UNSPEC); 2638c2ecf20Sopenharmony_ci if (!band_nla || nla_put(reply, OVS_BAND_ATTR_STATS, 2648c2ecf20Sopenharmony_ci sizeof(struct ovs_flow_stats), 2658c2ecf20Sopenharmony_ci &band->stats)) 2668c2ecf20Sopenharmony_ci goto error; 2678c2ecf20Sopenharmony_ci nla_nest_end(reply, band_nla); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci nla_nest_end(reply, nla); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_cierror: 2738c2ecf20Sopenharmony_ci return -EMSGSIZE; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int ovs_meter_cmd_features(struct sk_buff *skb, struct genl_info *info) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 2798c2ecf20Sopenharmony_ci struct ovs_header *ovs_reply_header; 2808c2ecf20Sopenharmony_ci struct nlattr *nla, *band_nla; 2818c2ecf20Sopenharmony_ci struct sk_buff *reply; 2828c2ecf20Sopenharmony_ci struct datapath *dp; 2838c2ecf20Sopenharmony_ci int err = -EMSGSIZE; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_FEATURES, 2868c2ecf20Sopenharmony_ci &ovs_reply_header); 2878c2ecf20Sopenharmony_ci if (IS_ERR(reply)) 2888c2ecf20Sopenharmony_ci return PTR_ERR(reply); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci ovs_lock(); 2918c2ecf20Sopenharmony_ci dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); 2928c2ecf20Sopenharmony_ci if (!dp) { 2938c2ecf20Sopenharmony_ci err = -ENODEV; 2948c2ecf20Sopenharmony_ci goto exit_unlock; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (nla_put_u32(reply, OVS_METER_ATTR_MAX_METERS, 2988c2ecf20Sopenharmony_ci dp->meter_tbl.max_meters_allowed)) 2998c2ecf20Sopenharmony_ci goto exit_unlock; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ovs_unlock(); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (nla_put_u32(reply, OVS_METER_ATTR_MAX_BANDS, DP_MAX_BANDS)) 3048c2ecf20Sopenharmony_ci goto nla_put_failure; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci nla = nla_nest_start_noflag(reply, OVS_METER_ATTR_BANDS); 3078c2ecf20Sopenharmony_ci if (!nla) 3088c2ecf20Sopenharmony_ci goto nla_put_failure; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci band_nla = nla_nest_start_noflag(reply, OVS_BAND_ATTR_UNSPEC); 3118c2ecf20Sopenharmony_ci if (!band_nla) 3128c2ecf20Sopenharmony_ci goto nla_put_failure; 3138c2ecf20Sopenharmony_ci /* Currently only DROP band type is supported. */ 3148c2ecf20Sopenharmony_ci if (nla_put_u32(reply, OVS_BAND_ATTR_TYPE, OVS_METER_BAND_TYPE_DROP)) 3158c2ecf20Sopenharmony_ci goto nla_put_failure; 3168c2ecf20Sopenharmony_ci nla_nest_end(reply, band_nla); 3178c2ecf20Sopenharmony_ci nla_nest_end(reply, nla); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci genlmsg_end(reply, ovs_reply_header); 3208c2ecf20Sopenharmony_ci return genlmsg_reply(reply, info); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ciexit_unlock: 3238c2ecf20Sopenharmony_ci ovs_unlock(); 3248c2ecf20Sopenharmony_cinla_put_failure: 3258c2ecf20Sopenharmony_ci nlmsg_free(reply); 3268c2ecf20Sopenharmony_ci return err; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic struct dp_meter *dp_meter_create(struct nlattr **a) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct nlattr *nla; 3328c2ecf20Sopenharmony_ci int rem; 3338c2ecf20Sopenharmony_ci u16 n_bands = 0; 3348c2ecf20Sopenharmony_ci struct dp_meter *meter; 3358c2ecf20Sopenharmony_ci struct dp_meter_band *band; 3368c2ecf20Sopenharmony_ci int err; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Validate attributes, count the bands. */ 3398c2ecf20Sopenharmony_ci if (!a[OVS_METER_ATTR_BANDS]) 3408c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci nla_for_each_nested(nla, a[OVS_METER_ATTR_BANDS], rem) 3438c2ecf20Sopenharmony_ci if (++n_bands > DP_MAX_BANDS) 3448c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Allocate and set up the meter before locking anything. */ 3478c2ecf20Sopenharmony_ci meter = kzalloc(struct_size(meter, bands, n_bands), GFP_KERNEL); 3488c2ecf20Sopenharmony_ci if (!meter) 3498c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci meter->id = nla_get_u32(a[OVS_METER_ATTR_ID]); 3528c2ecf20Sopenharmony_ci meter->used = div_u64(ktime_get_ns(), 1000 * 1000); 3538c2ecf20Sopenharmony_ci meter->kbps = a[OVS_METER_ATTR_KBPS] ? 1 : 0; 3548c2ecf20Sopenharmony_ci meter->keep_stats = !a[OVS_METER_ATTR_CLEAR]; 3558c2ecf20Sopenharmony_ci spin_lock_init(&meter->lock); 3568c2ecf20Sopenharmony_ci if (meter->keep_stats && a[OVS_METER_ATTR_STATS]) { 3578c2ecf20Sopenharmony_ci meter->stats = *(struct ovs_flow_stats *) 3588c2ecf20Sopenharmony_ci nla_data(a[OVS_METER_ATTR_STATS]); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci meter->n_bands = n_bands; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Set up meter bands. */ 3638c2ecf20Sopenharmony_ci band = meter->bands; 3648c2ecf20Sopenharmony_ci nla_for_each_nested(nla, a[OVS_METER_ATTR_BANDS], rem) { 3658c2ecf20Sopenharmony_ci struct nlattr *attr[OVS_BAND_ATTR_MAX + 1]; 3668c2ecf20Sopenharmony_ci u32 band_max_delta_t; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci err = nla_parse_deprecated((struct nlattr **)&attr, 3698c2ecf20Sopenharmony_ci OVS_BAND_ATTR_MAX, nla_data(nla), 3708c2ecf20Sopenharmony_ci nla_len(nla), band_policy, NULL); 3718c2ecf20Sopenharmony_ci if (err) 3728c2ecf20Sopenharmony_ci goto exit_free_meter; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (!attr[OVS_BAND_ATTR_TYPE] || 3758c2ecf20Sopenharmony_ci !attr[OVS_BAND_ATTR_RATE] || 3768c2ecf20Sopenharmony_ci !attr[OVS_BAND_ATTR_BURST]) { 3778c2ecf20Sopenharmony_ci err = -EINVAL; 3788c2ecf20Sopenharmony_ci goto exit_free_meter; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci band->type = nla_get_u32(attr[OVS_BAND_ATTR_TYPE]); 3828c2ecf20Sopenharmony_ci band->rate = nla_get_u32(attr[OVS_BAND_ATTR_RATE]); 3838c2ecf20Sopenharmony_ci if (band->rate == 0) { 3848c2ecf20Sopenharmony_ci err = -EINVAL; 3858c2ecf20Sopenharmony_ci goto exit_free_meter; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci band->burst_size = nla_get_u32(attr[OVS_BAND_ATTR_BURST]); 3898c2ecf20Sopenharmony_ci /* Figure out max delta_t that is enough to fill any bucket. 3908c2ecf20Sopenharmony_ci * Keep max_delta_t size to the bucket units: 3918c2ecf20Sopenharmony_ci * pkts => 1/1000 packets, kilobits => bits. 3928c2ecf20Sopenharmony_ci * 3938c2ecf20Sopenharmony_ci * Start with a full bucket. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci band->bucket = (band->burst_size + band->rate) * 1000ULL; 3968c2ecf20Sopenharmony_ci band_max_delta_t = div_u64(band->bucket, band->rate); 3978c2ecf20Sopenharmony_ci if (band_max_delta_t > meter->max_delta_t) 3988c2ecf20Sopenharmony_ci meter->max_delta_t = band_max_delta_t; 3998c2ecf20Sopenharmony_ci band++; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return meter; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ciexit_free_meter: 4058c2ecf20Sopenharmony_ci kfree(meter); 4068c2ecf20Sopenharmony_ci return ERR_PTR(err); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int ovs_meter_cmd_set(struct sk_buff *skb, struct genl_info *info) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 4128c2ecf20Sopenharmony_ci struct dp_meter *meter, *old_meter; 4138c2ecf20Sopenharmony_ci struct sk_buff *reply; 4148c2ecf20Sopenharmony_ci struct ovs_header *ovs_reply_header; 4158c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 4168c2ecf20Sopenharmony_ci struct dp_meter_table *meter_tbl; 4178c2ecf20Sopenharmony_ci struct datapath *dp; 4188c2ecf20Sopenharmony_ci int err; 4198c2ecf20Sopenharmony_ci u32 meter_id; 4208c2ecf20Sopenharmony_ci bool failed; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (!a[OVS_METER_ATTR_ID]) 4238c2ecf20Sopenharmony_ci return -EINVAL; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci meter = dp_meter_create(a); 4268c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(meter)) 4278c2ecf20Sopenharmony_ci return PTR_ERR(meter); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_SET, 4308c2ecf20Sopenharmony_ci &ovs_reply_header); 4318c2ecf20Sopenharmony_ci if (IS_ERR(reply)) { 4328c2ecf20Sopenharmony_ci err = PTR_ERR(reply); 4338c2ecf20Sopenharmony_ci goto exit_free_meter; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci ovs_lock(); 4378c2ecf20Sopenharmony_ci dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); 4388c2ecf20Sopenharmony_ci if (!dp) { 4398c2ecf20Sopenharmony_ci err = -ENODEV; 4408c2ecf20Sopenharmony_ci goto exit_unlock; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci meter_tbl = &dp->meter_tbl; 4448c2ecf20Sopenharmony_ci meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci old_meter = lookup_meter(meter_tbl, meter_id); 4478c2ecf20Sopenharmony_ci err = detach_meter(meter_tbl, old_meter); 4488c2ecf20Sopenharmony_ci if (err) 4498c2ecf20Sopenharmony_ci goto exit_unlock; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci err = attach_meter(meter_tbl, meter); 4528c2ecf20Sopenharmony_ci if (err) 4538c2ecf20Sopenharmony_ci goto exit_free_old_meter; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci ovs_unlock(); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Build response with the meter_id and stats from 4588c2ecf20Sopenharmony_ci * the old meter, if any. 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_ci failed = nla_put_u32(reply, OVS_METER_ATTR_ID, meter_id); 4618c2ecf20Sopenharmony_ci WARN_ON(failed); 4628c2ecf20Sopenharmony_ci if (old_meter) { 4638c2ecf20Sopenharmony_ci spin_lock_bh(&old_meter->lock); 4648c2ecf20Sopenharmony_ci if (old_meter->keep_stats) { 4658c2ecf20Sopenharmony_ci err = ovs_meter_cmd_reply_stats(reply, meter_id, 4668c2ecf20Sopenharmony_ci old_meter); 4678c2ecf20Sopenharmony_ci WARN_ON(err); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci spin_unlock_bh(&old_meter->lock); 4708c2ecf20Sopenharmony_ci ovs_meter_free(old_meter); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci genlmsg_end(reply, ovs_reply_header); 4748c2ecf20Sopenharmony_ci return genlmsg_reply(reply, info); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ciexit_free_old_meter: 4778c2ecf20Sopenharmony_ci ovs_meter_free(old_meter); 4788c2ecf20Sopenharmony_ciexit_unlock: 4798c2ecf20Sopenharmony_ci ovs_unlock(); 4808c2ecf20Sopenharmony_ci nlmsg_free(reply); 4818c2ecf20Sopenharmony_ciexit_free_meter: 4828c2ecf20Sopenharmony_ci kfree(meter); 4838c2ecf20Sopenharmony_ci return err; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int ovs_meter_cmd_get(struct sk_buff *skb, struct genl_info *info) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 4898c2ecf20Sopenharmony_ci struct ovs_header *ovs_reply_header; 4908c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 4918c2ecf20Sopenharmony_ci struct dp_meter *meter; 4928c2ecf20Sopenharmony_ci struct sk_buff *reply; 4938c2ecf20Sopenharmony_ci struct datapath *dp; 4948c2ecf20Sopenharmony_ci u32 meter_id; 4958c2ecf20Sopenharmony_ci int err; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (!a[OVS_METER_ATTR_ID]) 4988c2ecf20Sopenharmony_ci return -EINVAL; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_GET, 5038c2ecf20Sopenharmony_ci &ovs_reply_header); 5048c2ecf20Sopenharmony_ci if (IS_ERR(reply)) 5058c2ecf20Sopenharmony_ci return PTR_ERR(reply); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci ovs_lock(); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); 5108c2ecf20Sopenharmony_ci if (!dp) { 5118c2ecf20Sopenharmony_ci err = -ENODEV; 5128c2ecf20Sopenharmony_ci goto exit_unlock; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Locate meter, copy stats. */ 5168c2ecf20Sopenharmony_ci meter = lookup_meter(&dp->meter_tbl, meter_id); 5178c2ecf20Sopenharmony_ci if (!meter) { 5188c2ecf20Sopenharmony_ci err = -ENOENT; 5198c2ecf20Sopenharmony_ci goto exit_unlock; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci spin_lock_bh(&meter->lock); 5238c2ecf20Sopenharmony_ci err = ovs_meter_cmd_reply_stats(reply, meter_id, meter); 5248c2ecf20Sopenharmony_ci spin_unlock_bh(&meter->lock); 5258c2ecf20Sopenharmony_ci if (err) 5268c2ecf20Sopenharmony_ci goto exit_unlock; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci ovs_unlock(); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci genlmsg_end(reply, ovs_reply_header); 5318c2ecf20Sopenharmony_ci return genlmsg_reply(reply, info); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ciexit_unlock: 5348c2ecf20Sopenharmony_ci ovs_unlock(); 5358c2ecf20Sopenharmony_ci nlmsg_free(reply); 5368c2ecf20Sopenharmony_ci return err; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic int ovs_meter_cmd_del(struct sk_buff *skb, struct genl_info *info) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct ovs_header *ovs_header = info->userhdr; 5428c2ecf20Sopenharmony_ci struct ovs_header *ovs_reply_header; 5438c2ecf20Sopenharmony_ci struct nlattr **a = info->attrs; 5448c2ecf20Sopenharmony_ci struct dp_meter *old_meter; 5458c2ecf20Sopenharmony_ci struct sk_buff *reply; 5468c2ecf20Sopenharmony_ci struct datapath *dp; 5478c2ecf20Sopenharmony_ci u32 meter_id; 5488c2ecf20Sopenharmony_ci int err; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (!a[OVS_METER_ATTR_ID]) 5518c2ecf20Sopenharmony_ci return -EINVAL; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_DEL, 5548c2ecf20Sopenharmony_ci &ovs_reply_header); 5558c2ecf20Sopenharmony_ci if (IS_ERR(reply)) 5568c2ecf20Sopenharmony_ci return PTR_ERR(reply); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci ovs_lock(); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); 5618c2ecf20Sopenharmony_ci if (!dp) { 5628c2ecf20Sopenharmony_ci err = -ENODEV; 5638c2ecf20Sopenharmony_ci goto exit_unlock; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]); 5678c2ecf20Sopenharmony_ci old_meter = lookup_meter(&dp->meter_tbl, meter_id); 5688c2ecf20Sopenharmony_ci if (old_meter) { 5698c2ecf20Sopenharmony_ci spin_lock_bh(&old_meter->lock); 5708c2ecf20Sopenharmony_ci err = ovs_meter_cmd_reply_stats(reply, meter_id, old_meter); 5718c2ecf20Sopenharmony_ci WARN_ON(err); 5728c2ecf20Sopenharmony_ci spin_unlock_bh(&old_meter->lock); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci err = detach_meter(&dp->meter_tbl, old_meter); 5758c2ecf20Sopenharmony_ci if (err) 5768c2ecf20Sopenharmony_ci goto exit_unlock; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ovs_unlock(); 5808c2ecf20Sopenharmony_ci ovs_meter_free(old_meter); 5818c2ecf20Sopenharmony_ci genlmsg_end(reply, ovs_reply_header); 5828c2ecf20Sopenharmony_ci return genlmsg_reply(reply, info); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ciexit_unlock: 5858c2ecf20Sopenharmony_ci ovs_unlock(); 5868c2ecf20Sopenharmony_ci nlmsg_free(reply); 5878c2ecf20Sopenharmony_ci return err; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* Meter action execution. 5918c2ecf20Sopenharmony_ci * 5928c2ecf20Sopenharmony_ci * Return true 'meter_id' drop band is triggered. The 'skb' should be 5938c2ecf20Sopenharmony_ci * dropped by the caller'. 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_cibool ovs_meter_execute(struct datapath *dp, struct sk_buff *skb, 5968c2ecf20Sopenharmony_ci struct sw_flow_key *key, u32 meter_id) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci long long int now_ms = div_u64(ktime_get_ns(), 1000 * 1000); 5998c2ecf20Sopenharmony_ci long long int long_delta_ms; 6008c2ecf20Sopenharmony_ci struct dp_meter_band *band; 6018c2ecf20Sopenharmony_ci struct dp_meter *meter; 6028c2ecf20Sopenharmony_ci int i, band_exceeded_max = -1; 6038c2ecf20Sopenharmony_ci u32 band_exceeded_rate = 0; 6048c2ecf20Sopenharmony_ci u32 delta_ms; 6058c2ecf20Sopenharmony_ci u32 cost; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci meter = lookup_meter(&dp->meter_tbl, meter_id); 6088c2ecf20Sopenharmony_ci /* Do not drop the packet when there is no meter. */ 6098c2ecf20Sopenharmony_ci if (!meter) 6108c2ecf20Sopenharmony_ci return false; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* Lock the meter while using it. */ 6138c2ecf20Sopenharmony_ci spin_lock(&meter->lock); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci long_delta_ms = (now_ms - meter->used); /* ms */ 6168c2ecf20Sopenharmony_ci if (long_delta_ms < 0) { 6178c2ecf20Sopenharmony_ci /* This condition means that we have several threads fighting 6188c2ecf20Sopenharmony_ci * for a meter lock, and the one who received the packets a 6198c2ecf20Sopenharmony_ci * bit later wins. Assuming that all racing threads received 6208c2ecf20Sopenharmony_ci * packets at the same time to avoid overflow. 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ci long_delta_ms = 0; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* Make sure delta_ms will not be too large, so that bucket will not 6268c2ecf20Sopenharmony_ci * wrap around below. 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_ci delta_ms = (long_delta_ms > (long long int)meter->max_delta_t) 6298c2ecf20Sopenharmony_ci ? meter->max_delta_t : (u32)long_delta_ms; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* Update meter statistics. 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_ci meter->used = now_ms; 6348c2ecf20Sopenharmony_ci meter->stats.n_packets += 1; 6358c2ecf20Sopenharmony_ci meter->stats.n_bytes += skb->len; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* Bucket rate is either in kilobits per second, or in packets per 6388c2ecf20Sopenharmony_ci * second. We maintain the bucket in the units of either bits or 6398c2ecf20Sopenharmony_ci * 1/1000th of a packet, correspondingly. 6408c2ecf20Sopenharmony_ci * Then, when rate is multiplied with milliseconds, we get the 6418c2ecf20Sopenharmony_ci * bucket units: 6428c2ecf20Sopenharmony_ci * msec * kbps = bits, and 6438c2ecf20Sopenharmony_ci * msec * packets/sec = 1/1000 packets. 6448c2ecf20Sopenharmony_ci * 6458c2ecf20Sopenharmony_ci * 'cost' is the number of bucket units in this packet. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_ci cost = (meter->kbps) ? skb->len * 8 : 1000; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* Update all bands and find the one hit with the highest rate. */ 6508c2ecf20Sopenharmony_ci for (i = 0; i < meter->n_bands; ++i) { 6518c2ecf20Sopenharmony_ci long long int max_bucket_size; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci band = &meter->bands[i]; 6548c2ecf20Sopenharmony_ci max_bucket_size = (band->burst_size + band->rate) * 1000LL; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci band->bucket += delta_ms * band->rate; 6578c2ecf20Sopenharmony_ci if (band->bucket > max_bucket_size) 6588c2ecf20Sopenharmony_ci band->bucket = max_bucket_size; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (band->bucket >= cost) { 6618c2ecf20Sopenharmony_ci band->bucket -= cost; 6628c2ecf20Sopenharmony_ci } else if (band->rate > band_exceeded_rate) { 6638c2ecf20Sopenharmony_ci band_exceeded_rate = band->rate; 6648c2ecf20Sopenharmony_ci band_exceeded_max = i; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (band_exceeded_max >= 0) { 6698c2ecf20Sopenharmony_ci /* Update band statistics. */ 6708c2ecf20Sopenharmony_ci band = &meter->bands[band_exceeded_max]; 6718c2ecf20Sopenharmony_ci band->stats.n_packets += 1; 6728c2ecf20Sopenharmony_ci band->stats.n_bytes += skb->len; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* Drop band triggered, let the caller drop the 'skb'. */ 6758c2ecf20Sopenharmony_ci if (band->type == OVS_METER_BAND_TYPE_DROP) { 6768c2ecf20Sopenharmony_ci spin_unlock(&meter->lock); 6778c2ecf20Sopenharmony_ci return true; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci spin_unlock(&meter->lock); 6828c2ecf20Sopenharmony_ci return false; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic const struct genl_small_ops dp_meter_genl_ops[] = { 6868c2ecf20Sopenharmony_ci { .cmd = OVS_METER_CMD_FEATURES, 6878c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 6888c2ecf20Sopenharmony_ci .flags = 0, /* OK for unprivileged users. */ 6898c2ecf20Sopenharmony_ci .doit = ovs_meter_cmd_features 6908c2ecf20Sopenharmony_ci }, 6918c2ecf20Sopenharmony_ci { .cmd = OVS_METER_CMD_SET, 6928c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 6938c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN 6948c2ecf20Sopenharmony_ci * privilege. 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_ci .doit = ovs_meter_cmd_set, 6978c2ecf20Sopenharmony_ci }, 6988c2ecf20Sopenharmony_ci { .cmd = OVS_METER_CMD_GET, 6998c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 7008c2ecf20Sopenharmony_ci .flags = 0, /* OK for unprivileged users. */ 7018c2ecf20Sopenharmony_ci .doit = ovs_meter_cmd_get, 7028c2ecf20Sopenharmony_ci }, 7038c2ecf20Sopenharmony_ci { .cmd = OVS_METER_CMD_DEL, 7048c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 7058c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN 7068c2ecf20Sopenharmony_ci * privilege. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_ci .doit = ovs_meter_cmd_del 7098c2ecf20Sopenharmony_ci }, 7108c2ecf20Sopenharmony_ci}; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic const struct genl_multicast_group ovs_meter_multicast_group = { 7138c2ecf20Sopenharmony_ci .name = OVS_METER_MCGROUP, 7148c2ecf20Sopenharmony_ci}; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistruct genl_family dp_meter_genl_family __ro_after_init = { 7178c2ecf20Sopenharmony_ci .hdrsize = sizeof(struct ovs_header), 7188c2ecf20Sopenharmony_ci .name = OVS_METER_FAMILY, 7198c2ecf20Sopenharmony_ci .version = OVS_METER_VERSION, 7208c2ecf20Sopenharmony_ci .maxattr = OVS_METER_ATTR_MAX, 7218c2ecf20Sopenharmony_ci .policy = meter_policy, 7228c2ecf20Sopenharmony_ci .netnsok = true, 7238c2ecf20Sopenharmony_ci .parallel_ops = true, 7248c2ecf20Sopenharmony_ci .small_ops = dp_meter_genl_ops, 7258c2ecf20Sopenharmony_ci .n_small_ops = ARRAY_SIZE(dp_meter_genl_ops), 7268c2ecf20Sopenharmony_ci .mcgrps = &ovs_meter_multicast_group, 7278c2ecf20Sopenharmony_ci .n_mcgrps = 1, 7288c2ecf20Sopenharmony_ci .module = THIS_MODULE, 7298c2ecf20Sopenharmony_ci}; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ciint ovs_meters_init(struct datapath *dp) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct dp_meter_table *tbl = &dp->meter_tbl; 7348c2ecf20Sopenharmony_ci struct dp_meter_instance *ti; 7358c2ecf20Sopenharmony_ci unsigned long free_mem_bytes; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci ti = dp_meter_instance_alloc(DP_METER_ARRAY_SIZE_MIN); 7388c2ecf20Sopenharmony_ci if (!ti) 7398c2ecf20Sopenharmony_ci return -ENOMEM; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* Allow meters in a datapath to use ~3.12% of physical memory. */ 7428c2ecf20Sopenharmony_ci free_mem_bytes = nr_free_buffer_pages() * (PAGE_SIZE >> 5); 7438c2ecf20Sopenharmony_ci tbl->max_meters_allowed = min(free_mem_bytes / sizeof(struct dp_meter), 7448c2ecf20Sopenharmony_ci DP_METER_NUM_MAX); 7458c2ecf20Sopenharmony_ci if (!tbl->max_meters_allowed) 7468c2ecf20Sopenharmony_ci goto out_err; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci rcu_assign_pointer(tbl->ti, ti); 7498c2ecf20Sopenharmony_ci tbl->count = 0; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return 0; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ciout_err: 7548c2ecf20Sopenharmony_ci dp_meter_instance_free(ti); 7558c2ecf20Sopenharmony_ci return -ENOMEM; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_civoid ovs_meters_exit(struct datapath *dp) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct dp_meter_table *tbl = &dp->meter_tbl; 7618c2ecf20Sopenharmony_ci struct dp_meter_instance *ti = rcu_dereference_raw(tbl->ti); 7628c2ecf20Sopenharmony_ci int i; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci for (i = 0; i < ti->n_meters; i++) 7658c2ecf20Sopenharmony_ci ovs_meter_free(rcu_dereference_raw(ti->dp_meters[i])); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci dp_meter_instance_free(ti); 7688c2ecf20Sopenharmony_ci} 769