goto error_free_actions;
old_acts = rcu_dereference(flow->sf_acts);
rcu_assign_pointer(flow->sf_acts, new_acts);
- synchronize_rcu(); /* XXX expensive! */
- kfree(old_acts);
+ flow_deferred_free_acts(old_acts);
return 0;
/* Replace 'old_flow' by 'flow'. */
struct sw_flow *old_flow = *rcu_dereference(bucket);
rcu_assign_pointer(*bucket, flow);
- synchronize_rcu(); /* XXX expensive! */
+
+ /* XXX These statistics might lose a few packets, since other
+ * CPUs can be using this flow. We used to synchronize_rcu()
+ * to make sure that we get completely accurate stats, but that
+ * blows our performance, badly. */
error = put_stats(old_flow, ufp) ? -EFAULT : 0;
- flow_free(old_flow);
+ flow_deferred_free(old_flow);
}
return error;
error = dp_table_delete(table, flow);
if (error)
goto error;
+
+ /* XXX These statistics might lose a few packets, since other
+ * CPUs can be using this flow. We used to synchronize_rcu()
+ * to make sure that we get completely accurate stats, but that
+ * blows our performance, badly. */
dp->n_flows--;
- synchronize_rcu(); /* XXX expensive! */
error = answer_query(flow, ufp);
- flow_free(flow);
+ flow_deferred_free(flow);
} else {
error = answer_query(flow, ufp);
}