Commit eb58eebc authored by Tonghao Zhang's avatar Tonghao Zhang Committed by David S. Miller

net: openvswitch: set max limitation to meters

Don't allow user to create meter unlimitedly, which may cause
to consume a large amount of kernel memory. The max number
supported is decided by physical memory and 20K meters as default.

Cc: Pravin B Shelar <pshelar@ovn.org>
Cc: Andy Zhou <azhou@ovn.org>
Signed-off-by: default avatarTonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: default avatarPravin B Shelar <pshelar@ovn.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c7c4c44c
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/openvswitch.h> #include <linux/openvswitch.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/rculist.h> #include <linux/rculist.h>
#include <linux/swap.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/genetlink.h> #include <net/genetlink.h>
...@@ -137,6 +138,7 @@ static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter) ...@@ -137,6 +138,7 @@ static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter)
{ {
struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti); struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti);
u32 hash = meter_hash(ti, meter->id); u32 hash = meter_hash(ti, meter->id);
int err;
/* In generally, slots selected should be empty, because /* In generally, slots selected should be empty, because
* OvS uses id-pool to fetch a available id. * OvS uses id-pool to fetch a available id.
...@@ -147,16 +149,24 @@ static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter) ...@@ -147,16 +149,24 @@ static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter)
dp_meter_instance_insert(ti, meter); dp_meter_instance_insert(ti, meter);
/* That function is thread-safe. */ /* That function is thread-safe. */
if (++tbl->count >= ti->n_meters) tbl->count++;
if (dp_meter_instance_realloc(tbl, ti->n_meters * 2)) if (tbl->count >= tbl->max_meters_allowed) {
goto expand_err; err = -EFBIG;
goto attach_err;
}
if (tbl->count >= ti->n_meters &&
dp_meter_instance_realloc(tbl, ti->n_meters * 2)) {
err = -ENOMEM;
goto attach_err;
}
return 0; return 0;
expand_err: attach_err:
dp_meter_instance_remove(ti, meter); dp_meter_instance_remove(ti, meter);
tbl->count--; tbl->count--;
return -ENOMEM; return err;
} }
static int detach_meter(struct dp_meter_table *tbl, struct dp_meter *meter) static int detach_meter(struct dp_meter_table *tbl, struct dp_meter *meter)
...@@ -266,18 +276,32 @@ static int ovs_meter_cmd_reply_stats(struct sk_buff *reply, u32 meter_id, ...@@ -266,18 +276,32 @@ static int ovs_meter_cmd_reply_stats(struct sk_buff *reply, u32 meter_id,
static int ovs_meter_cmd_features(struct sk_buff *skb, struct genl_info *info) static int ovs_meter_cmd_features(struct sk_buff *skb, struct genl_info *info)
{ {
struct sk_buff *reply; struct ovs_header *ovs_header = info->userhdr;
struct ovs_header *ovs_reply_header; struct ovs_header *ovs_reply_header;
struct nlattr *nla, *band_nla; struct nlattr *nla, *band_nla;
int err; struct sk_buff *reply;
struct datapath *dp;
int err = -EMSGSIZE;
reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_FEATURES, reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_FEATURES,
&ovs_reply_header); &ovs_reply_header);
if (IS_ERR(reply)) if (IS_ERR(reply))
return PTR_ERR(reply); return PTR_ERR(reply);
if (nla_put_u32(reply, OVS_METER_ATTR_MAX_METERS, U32_MAX) || ovs_lock();
nla_put_u32(reply, OVS_METER_ATTR_MAX_BANDS, DP_MAX_BANDS)) dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
if (!dp) {
err = -ENODEV;
goto exit_unlock;
}
if (nla_put_u32(reply, OVS_METER_ATTR_MAX_METERS,
dp->meter_tbl.max_meters_allowed))
goto exit_unlock;
ovs_unlock();
if (nla_put_u32(reply, OVS_METER_ATTR_MAX_BANDS, DP_MAX_BANDS))
goto nla_put_failure; goto nla_put_failure;
nla = nla_nest_start_noflag(reply, OVS_METER_ATTR_BANDS); nla = nla_nest_start_noflag(reply, OVS_METER_ATTR_BANDS);
...@@ -296,9 +320,10 @@ static int ovs_meter_cmd_features(struct sk_buff *skb, struct genl_info *info) ...@@ -296,9 +320,10 @@ static int ovs_meter_cmd_features(struct sk_buff *skb, struct genl_info *info)
genlmsg_end(reply, ovs_reply_header); genlmsg_end(reply, ovs_reply_header);
return genlmsg_reply(reply, info); return genlmsg_reply(reply, info);
exit_unlock:
ovs_unlock();
nla_put_failure: nla_put_failure:
nlmsg_free(reply); nlmsg_free(reply);
err = -EMSGSIZE;
return err; return err;
} }
...@@ -699,15 +724,27 @@ int ovs_meters_init(struct datapath *dp) ...@@ -699,15 +724,27 @@ int ovs_meters_init(struct datapath *dp)
{ {
struct dp_meter_table *tbl = &dp->meter_tbl; struct dp_meter_table *tbl = &dp->meter_tbl;
struct dp_meter_instance *ti; struct dp_meter_instance *ti;
unsigned long free_mem_bytes;
ti = dp_meter_instance_alloc(DP_METER_ARRAY_SIZE_MIN); ti = dp_meter_instance_alloc(DP_METER_ARRAY_SIZE_MIN);
if (!ti) if (!ti)
return -ENOMEM; return -ENOMEM;
/* Allow meters in a datapath to use ~3.12% of physical memory. */
free_mem_bytes = nr_free_buffer_pages() * (PAGE_SIZE >> 5);
tbl->max_meters_allowed = min(free_mem_bytes / sizeof(struct dp_meter),
DP_METER_NUM_MAX);
if (!tbl->max_meters_allowed)
goto out_err;
rcu_assign_pointer(tbl->ti, ti); rcu_assign_pointer(tbl->ti, ti);
tbl->count = 0; tbl->count = 0;
return 0; return 0;
out_err:
dp_meter_instance_free(ti);
return -ENOMEM;
} }
void ovs_meters_exit(struct datapath *dp) void ovs_meters_exit(struct datapath *dp)
......
...@@ -20,6 +20,7 @@ struct datapath; ...@@ -20,6 +20,7 @@ struct datapath;
#define DP_MAX_BANDS 1 #define DP_MAX_BANDS 1
#define DP_METER_ARRAY_SIZE_MIN BIT_ULL(10) #define DP_METER_ARRAY_SIZE_MIN BIT_ULL(10)
#define DP_METER_NUM_MAX (200000UL)
struct dp_meter_band { struct dp_meter_band {
u32 type; u32 type;
...@@ -50,6 +51,7 @@ struct dp_meter_instance { ...@@ -50,6 +51,7 @@ struct dp_meter_instance {
struct dp_meter_table { struct dp_meter_table {
struct dp_meter_instance __rcu *ti; struct dp_meter_instance __rcu *ti;
u32 count; u32 count;
u32 max_meters_allowed;
}; };
extern struct genl_family dp_meter_genl_family; extern struct genl_family dp_meter_genl_family;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment