mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-09-22 22:11:38 +08:00
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: Tonghao Zhang <xiangxia.m.yue@gmail.com> Acked-by: Pravin B Shelar <pshelar@ovn.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
c7c4c44c9a
commit
eb58eebc7f
@ -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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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 @@ error:
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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;
|
|||||||
|
|
||||||
#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 {
|
|||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user