mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 17:41:44 +00:00
net: openvswitch: fix possible memleak on destroy flow-table
When we destroy the flow tables which may contain the flow_mask, so release the flow mask struct. Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com> Tested-by: Greg Rose <gvrose8192@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
0a3e01371d
commit
50b0e61b32
@ -210,6 +210,74 @@ static int tbl_mask_array_realloc(struct flow_table *tbl, int size)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tbl_mask_array_add_mask(struct flow_table *tbl,
|
||||||
|
struct sw_flow_mask *new)
|
||||||
|
{
|
||||||
|
struct mask_array *ma = ovsl_dereference(tbl->mask_array);
|
||||||
|
int err, ma_count = READ_ONCE(ma->count);
|
||||||
|
|
||||||
|
if (ma_count >= ma->max) {
|
||||||
|
err = tbl_mask_array_realloc(tbl, ma->max +
|
||||||
|
MASK_ARRAY_SIZE_MIN);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ma = ovsl_dereference(tbl->mask_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
BUG_ON(ovsl_dereference(ma->masks[ma_count]));
|
||||||
|
|
||||||
|
rcu_assign_pointer(ma->masks[ma_count], new);
|
||||||
|
WRITE_ONCE(ma->count, ma_count +1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tbl_mask_array_del_mask(struct flow_table *tbl,
|
||||||
|
struct sw_flow_mask *mask)
|
||||||
|
{
|
||||||
|
struct mask_array *ma = ovsl_dereference(tbl->mask_array);
|
||||||
|
int i, ma_count = READ_ONCE(ma->count);
|
||||||
|
|
||||||
|
/* Remove the deleted mask pointers from the array */
|
||||||
|
for (i = 0; i < ma_count; i++) {
|
||||||
|
if (mask == ovsl_dereference(ma->masks[i]))
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
BUG();
|
||||||
|
return;
|
||||||
|
|
||||||
|
found:
|
||||||
|
WRITE_ONCE(ma->count, ma_count -1);
|
||||||
|
|
||||||
|
rcu_assign_pointer(ma->masks[i], ma->masks[ma_count -1]);
|
||||||
|
RCU_INIT_POINTER(ma->masks[ma_count -1], NULL);
|
||||||
|
|
||||||
|
kfree_rcu(mask, rcu);
|
||||||
|
|
||||||
|
/* Shrink the mask array if necessary. */
|
||||||
|
if (ma->max >= (MASK_ARRAY_SIZE_MIN * 2) &&
|
||||||
|
ma_count <= (ma->max / 3))
|
||||||
|
tbl_mask_array_realloc(tbl, ma->max / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove 'mask' from the mask list, if it is not needed any more. */
|
||||||
|
static void flow_mask_remove(struct flow_table *tbl, struct sw_flow_mask *mask)
|
||||||
|
{
|
||||||
|
if (mask) {
|
||||||
|
/* ovs-lock is required to protect mask-refcount and
|
||||||
|
* mask list.
|
||||||
|
*/
|
||||||
|
ASSERT_OVSL();
|
||||||
|
BUG_ON(!mask->ref_count);
|
||||||
|
mask->ref_count--;
|
||||||
|
|
||||||
|
if (!mask->ref_count)
|
||||||
|
tbl_mask_array_del_mask(tbl, mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ovs_flow_tbl_init(struct flow_table *table)
|
int ovs_flow_tbl_init(struct flow_table *table)
|
||||||
{
|
{
|
||||||
struct table_instance *ti, *ufid_ti;
|
struct table_instance *ti, *ufid_ti;
|
||||||
@ -257,7 +325,28 @@ static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu)
|
|||||||
__table_instance_destroy(ti);
|
__table_instance_destroy(ti);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void table_instance_destroy(struct table_instance *ti,
|
static void table_instance_flow_free(struct flow_table *table,
|
||||||
|
struct table_instance *ti,
|
||||||
|
struct table_instance *ufid_ti,
|
||||||
|
struct sw_flow *flow,
|
||||||
|
bool count)
|
||||||
|
{
|
||||||
|
hlist_del_rcu(&flow->flow_table.node[ti->node_ver]);
|
||||||
|
if (count)
|
||||||
|
table->count--;
|
||||||
|
|
||||||
|
if (ovs_identifier_is_ufid(&flow->id)) {
|
||||||
|
hlist_del_rcu(&flow->ufid_table.node[ufid_ti->node_ver]);
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
table->ufid_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
flow_mask_remove(table, flow->mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void table_instance_destroy(struct flow_table *table,
|
||||||
|
struct table_instance *ti,
|
||||||
struct table_instance *ufid_ti,
|
struct table_instance *ufid_ti,
|
||||||
bool deferred)
|
bool deferred)
|
||||||
{
|
{
|
||||||
@ -274,13 +363,12 @@ static void table_instance_destroy(struct table_instance *ti,
|
|||||||
struct sw_flow *flow;
|
struct sw_flow *flow;
|
||||||
struct hlist_head *head = &ti->buckets[i];
|
struct hlist_head *head = &ti->buckets[i];
|
||||||
struct hlist_node *n;
|
struct hlist_node *n;
|
||||||
int ver = ti->node_ver;
|
|
||||||
int ufid_ver = ufid_ti->node_ver;
|
|
||||||
|
|
||||||
hlist_for_each_entry_safe(flow, n, head, flow_table.node[ver]) {
|
hlist_for_each_entry_safe(flow, n, head,
|
||||||
hlist_del_rcu(&flow->flow_table.node[ver]);
|
flow_table.node[ti->node_ver]) {
|
||||||
if (ovs_identifier_is_ufid(&flow->id))
|
|
||||||
hlist_del_rcu(&flow->ufid_table.node[ufid_ver]);
|
table_instance_flow_free(table, ti, ufid_ti,
|
||||||
|
flow, false);
|
||||||
ovs_flow_free(flow, deferred);
|
ovs_flow_free(flow, deferred);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,7 +393,7 @@ void ovs_flow_tbl_destroy(struct flow_table *table)
|
|||||||
|
|
||||||
free_percpu(table->mask_cache);
|
free_percpu(table->mask_cache);
|
||||||
kfree_rcu(rcu_dereference_raw(table->mask_array), rcu);
|
kfree_rcu(rcu_dereference_raw(table->mask_array), rcu);
|
||||||
table_instance_destroy(ti, ufid_ti, false);
|
table_instance_destroy(table, ti, ufid_ti, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti,
|
struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti,
|
||||||
@ -421,7 +509,7 @@ int ovs_flow_tbl_flush(struct flow_table *flow_table)
|
|||||||
flow_table->count = 0;
|
flow_table->count = 0;
|
||||||
flow_table->ufid_count = 0;
|
flow_table->ufid_count = 0;
|
||||||
|
|
||||||
table_instance_destroy(old_ti, old_ufid_ti, true);
|
table_instance_destroy(flow_table, old_ti, old_ufid_ti, true);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_ti:
|
err_free_ti:
|
||||||
@ -701,51 +789,6 @@ static struct table_instance *table_instance_expand(struct table_instance *ti,
|
|||||||
return table_instance_rehash(ti, ti->n_buckets * 2, ufid);
|
return table_instance_rehash(ti, ti->n_buckets * 2, ufid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tbl_mask_array_del_mask(struct flow_table *tbl,
|
|
||||||
struct sw_flow_mask *mask)
|
|
||||||
{
|
|
||||||
struct mask_array *ma = ovsl_dereference(tbl->mask_array);
|
|
||||||
int i, ma_count = READ_ONCE(ma->count);
|
|
||||||
|
|
||||||
/* Remove the deleted mask pointers from the array */
|
|
||||||
for (i = 0; i < ma_count; i++) {
|
|
||||||
if (mask == ovsl_dereference(ma->masks[i]))
|
|
||||||
goto found;
|
|
||||||
}
|
|
||||||
|
|
||||||
BUG();
|
|
||||||
return;
|
|
||||||
|
|
||||||
found:
|
|
||||||
WRITE_ONCE(ma->count, ma_count -1);
|
|
||||||
|
|
||||||
rcu_assign_pointer(ma->masks[i], ma->masks[ma_count -1]);
|
|
||||||
RCU_INIT_POINTER(ma->masks[ma_count -1], NULL);
|
|
||||||
|
|
||||||
kfree_rcu(mask, rcu);
|
|
||||||
|
|
||||||
/* Shrink the mask array if necessary. */
|
|
||||||
if (ma->max >= (MASK_ARRAY_SIZE_MIN * 2) &&
|
|
||||||
ma_count <= (ma->max / 3))
|
|
||||||
tbl_mask_array_realloc(tbl, ma->max / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove 'mask' from the mask list, if it is not needed any more. */
|
|
||||||
static void flow_mask_remove(struct flow_table *tbl, struct sw_flow_mask *mask)
|
|
||||||
{
|
|
||||||
if (mask) {
|
|
||||||
/* ovs-lock is required to protect mask-refcount and
|
|
||||||
* mask list.
|
|
||||||
*/
|
|
||||||
ASSERT_OVSL();
|
|
||||||
BUG_ON(!mask->ref_count);
|
|
||||||
mask->ref_count--;
|
|
||||||
|
|
||||||
if (!mask->ref_count)
|
|
||||||
tbl_mask_array_del_mask(tbl, mask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Must be called with OVS mutex held. */
|
/* Must be called with OVS mutex held. */
|
||||||
void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
|
void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
|
||||||
{
|
{
|
||||||
@ -753,17 +796,7 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
|
|||||||
struct table_instance *ufid_ti = ovsl_dereference(table->ufid_ti);
|
struct table_instance *ufid_ti = ovsl_dereference(table->ufid_ti);
|
||||||
|
|
||||||
BUG_ON(table->count == 0);
|
BUG_ON(table->count == 0);
|
||||||
hlist_del_rcu(&flow->flow_table.node[ti->node_ver]);
|
table_instance_flow_free(table, ti, ufid_ti, flow, true);
|
||||||
table->count--;
|
|
||||||
if (ovs_identifier_is_ufid(&flow->id)) {
|
|
||||||
hlist_del_rcu(&flow->ufid_table.node[ufid_ti->node_ver]);
|
|
||||||
table->ufid_count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RCU delete the mask. 'flow->mask' is not NULLed, as it should be
|
|
||||||
* accessible as long as the RCU read lock is held.
|
|
||||||
*/
|
|
||||||
flow_mask_remove(table, flow->mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sw_flow_mask *mask_alloc(void)
|
static struct sw_flow_mask *mask_alloc(void)
|
||||||
@ -806,29 +839,6 @@ static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tbl_mask_array_add_mask(struct flow_table *tbl,
|
|
||||||
struct sw_flow_mask *new)
|
|
||||||
{
|
|
||||||
struct mask_array *ma = ovsl_dereference(tbl->mask_array);
|
|
||||||
int err, ma_count = READ_ONCE(ma->count);
|
|
||||||
|
|
||||||
if (ma_count >= ma->max) {
|
|
||||||
err = tbl_mask_array_realloc(tbl, ma->max +
|
|
||||||
MASK_ARRAY_SIZE_MIN);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
ma = ovsl_dereference(tbl->mask_array);
|
|
||||||
}
|
|
||||||
|
|
||||||
BUG_ON(ovsl_dereference(ma->masks[ma_count]));
|
|
||||||
|
|
||||||
rcu_assign_pointer(ma->masks[ma_count], new);
|
|
||||||
WRITE_ONCE(ma->count, ma_count +1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add 'mask' into the mask list, if it is not already there. */
|
/* Add 'mask' into the mask list, if it is not already there. */
|
||||||
static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
|
static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
|
||||||
const struct sw_flow_mask *new)
|
const struct sw_flow_mask *new)
|
||||||
|
Loading…
Reference in New Issue
Block a user