Commit f1b4e32a authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by Marc Kleine-Budde

can: bcm: use call_rcu() instead of costly synchronize_rcu()

In commit d5f9023f ("can: bcm: delay release of struct bcm_op
after synchronize_rcu()") Thadeu Lima de Souza Cascardo introduced two
synchronize_rcu() calls in bcm_release() (only once at socket close)
and in bcm_delete_rx_op() (called on removal of each single bcm_op).

Unfortunately this slow removal of the bcm_op's affects user space
applications like cansniffer where the modification of a filter
removes 2048 bcm_op's which blocks the cansniffer application for
40(!) seconds.

In commit 181d4447 ("can: gw: use call_rcu() instead of costly
synchronize_rcu()") Eric Dumazet replaced the synchronize_rcu() calls
with several call_rcu()'s to safely remove the data structures after
the removal of CAN ID subscriptions with can_rx_unregister() calls.

This patch adopts Erics approach for the can-bcm which should be
applicable since the removal of tasklet_kill() in bcm_remove_op() and
the introduction of the HRTIMER_MODE_SOFT timer handling in Linux 5.4.

Fixes: d5f9023f ("can: bcm: delay release of struct bcm_op after synchronize_rcu()") # >= 5.4
Link: https://lore.kernel.org/all/20220520183239.19111-1-socketcan@hartkopp.net
Cc: stable@vger.kernel.org
Cc: Eric Dumazet <edumazet@google.com>
Cc: Norbert Slusarek <nslusarek@gmx.net>
Cc: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
Signed-off-by: default avatarOliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 280e3a85
...@@ -100,6 +100,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset) ...@@ -100,6 +100,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset)
struct bcm_op { struct bcm_op {
struct list_head list; struct list_head list;
struct rcu_head rcu;
int ifindex; int ifindex;
canid_t can_id; canid_t can_id;
u32 flags; u32 flags;
...@@ -718,10 +719,9 @@ static struct bcm_op *bcm_find_op(struct list_head *ops, ...@@ -718,10 +719,9 @@ static struct bcm_op *bcm_find_op(struct list_head *ops,
return NULL; return NULL;
} }
static void bcm_remove_op(struct bcm_op *op) static void bcm_free_op_rcu(struct rcu_head *rcu_head)
{ {
hrtimer_cancel(&op->timer); struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
hrtimer_cancel(&op->thrtimer);
if ((op->frames) && (op->frames != &op->sframe)) if ((op->frames) && (op->frames != &op->sframe))
kfree(op->frames); kfree(op->frames);
...@@ -732,6 +732,14 @@ static void bcm_remove_op(struct bcm_op *op) ...@@ -732,6 +732,14 @@ static void bcm_remove_op(struct bcm_op *op)
kfree(op); kfree(op);
} }
static void bcm_remove_op(struct bcm_op *op)
{
hrtimer_cancel(&op->timer);
hrtimer_cancel(&op->thrtimer);
call_rcu(&op->rcu, bcm_free_op_rcu);
}
static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op) static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
{ {
if (op->rx_reg_dev == dev) { if (op->rx_reg_dev == dev) {
...@@ -757,6 +765,9 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, ...@@ -757,6 +765,9 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) && if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
(op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) { (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) {
/* disable automatic timer on frame reception */
op->flags |= RX_NO_AUTOTIMER;
/* /*
* Don't care if we're bound or not (due to netdev * Don't care if we're bound or not (due to netdev
* problems) can_rx_unregister() is always a save * problems) can_rx_unregister() is always a save
...@@ -785,7 +796,6 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, ...@@ -785,7 +796,6 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
bcm_rx_handler, op); bcm_rx_handler, op);
list_del(&op->list); list_del(&op->list);
synchronize_rcu();
bcm_remove_op(op); bcm_remove_op(op);
return 1; /* done */ return 1; /* done */
} }
......
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