dp_table_destroy(dp->table, 1);
for (i = 0; i < DP_N_QUEUES; i++)
skb_queue_purge(&dp->queues[i]);
- for (i = 0; i < DP_MAX_GROUPS; i++) {
- struct odp_port_group *pg = dp->groups[i];
- if (pg) {
- kfree(dp->groups[i]->ports);
- kfree(dp->groups[i]);
- }
- }
+ for (i = 0; i < DP_MAX_GROUPS; i++)
+ kfree(dp->groups[i]);
free_percpu(dp->stats_percpu);
kfree(dp);
module_put(THIS_MODULE);
return put_user(idx, &pvp->n_ports);
}
+/* RCU callback for freeing a dp_port_group */
+static void free_port_group(struct rcu_head *rcu)
+{
+ struct dp_port_group *g = container_of(rcu, struct dp_port_group, rcu);
+ kfree(g);
+}
+
static int
set_port_group(struct datapath *dp, const struct odp_port_group __user *upg)
{
struct odp_port_group pg;
- struct odp_port_group *pgp, *old_pg;
+ struct dp_port_group *new_group, *old_group;
int error;
error = -EFAULT;
goto error;
error = -ENOMEM;
- pgp = kmalloc(sizeof *pgp, GFP_KERNEL);
- if (!pgp)
+ new_group = kmalloc(sizeof *new_group + sizeof(u16) * pg.n_ports,
+ GFP_KERNEL);
+ if (!new_group)
goto error;
- pgp->ports = kmalloc(sizeof(u16) * pg.n_ports, GFP_KERNEL);
- if (!pgp->ports)
- goto error_free_pgp;
- pgp->n_ports = pg.n_ports;
+ new_group->n_ports = pg.n_ports;
error = -EFAULT;
- if (copy_from_user(pgp->ports, pg.ports, sizeof(u16) * pg.n_ports))
- goto error_free_pgp_ports;
-
- old_pg = rcu_dereference(dp->groups[pg.group]);
- rcu_assign_pointer(dp->groups[pg.group], pgp);
- if (old_pg) {
- synchronize_rcu(); /* XXX expensive! */
- kfree(old_pg->ports);
- kfree(old_pg);
- }
+ if (copy_from_user(new_group->ports, pg.ports,
+ sizeof(u16) * pg.n_ports))
+ goto error_free;
+
+ old_group = rcu_dereference(dp->groups[pg.group]);
+ rcu_assign_pointer(dp->groups[pg.group], new_group);
+ if (old_group)
+ call_rcu(&old_group->rcu, free_port_group);
return 0;
-error_free_pgp_ports:
- kfree(pgp->ports);
-error_free_pgp:
- kfree(pgp);
+error_free:
+ kfree(new_group);
error:
return error;
}
static int
get_port_group(struct datapath *dp, struct odp_port_group *upg)
{
- struct odp_port_group pg, *g;
+ struct odp_port_group pg;
+ struct dp_port_group *g;
u16 n_copy;
if (copy_from_user(&pg, upg, sizeof pg))
return -EINVAL;
g = dp->groups[pg.group];
- n_copy = g ? min(g->n_ports, pg.n_ports) : 0;
+ n_copy = g ? min_t(int, g->n_ports, pg.n_ports) : 0;
if (n_copy && copy_to_user(pg.ports, g->ports, n_copy * sizeof(u16)))
return -EFAULT;