forked from Minki/linux
can: bcm: use call_rcu() instead of costly synchronize_rcu()
In commitd5f9023fa6
("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 commit181d444790
("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:d5f9023fa6
("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: Oliver Hartkopp <socketcan@hartkopp.net> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
parent
280e3a857d
commit
f1b4e32aca
@ -100,6 +100,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset)
|
||||
|
||||
struct bcm_op {
|
||||
struct list_head list;
|
||||
struct rcu_head rcu;
|
||||
int ifindex;
|
||||
canid_t can_id;
|
||||
u32 flags;
|
||||
@ -718,10 +719,9 @@ static struct bcm_op *bcm_find_op(struct list_head *ops,
|
||||
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);
|
||||
hrtimer_cancel(&op->thrtimer);
|
||||
struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
|
||||
|
||||
if ((op->frames) && (op->frames != &op->sframe))
|
||||
kfree(op->frames);
|
||||
@ -732,6 +732,14 @@ static void bcm_remove_op(struct bcm_op *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)
|
||||
{
|
||||
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,
|
||||
if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
|
||||
(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
|
||||
* 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,
|
||||
bcm_rx_handler, op);
|
||||
|
||||
list_del(&op->list);
|
||||
synchronize_rcu();
|
||||
bcm_remove_op(op);
|
||||
return 1; /* done */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user