can: bcm: use call_rcu() instead of costly synchronize_rcu()
Subsystems (2)
Oliver Hartkopp
maintainerRFC PATCH 1/1 can: bcm: use call_rcu() instead of costly synchronize_rcu()
In commit d5f9023fa61e ("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 181d4447905d ("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.
--- net/can/bcm.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
net/can/bcm.c +14 -4
@@ -98,10 +98,11 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset)return *(u64 *)(cp->data + offset); }struct bcm_op {struct list_head list; +struct rcu_head rcu;int ifindex;canid_t can_id;u32 flags;unsigned long frames_abs, frames_filtered;struct bcm_timeval ival1, ival2;
@@ -716,24 +717,31 @@ 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);if ((op->last_frames) && (op->last_frames != &op->last_sframe))kfree (op->last_frames);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) {can_rx_unregister (dev_net (dev), dev, op->can_id,REGMASK (op->can_id), bcm_rx_handler, op);
@@ -755,10 +763,13 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,list_for_each_entry_safe (op, n, ops, list) {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 * thing to do here. */
@@ -783,11 +794,10 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, op->can_id,REGMASK (op->can_id), bcm_rx_handler, op);list_del (&op->list); -synchronize_rcu ();bcm_remove_op (op);return 1 ;/* done */ } }
Signature · 1 line
2.30.2
Oliver Hartkopp
maintainerHello Marc,
unfortunately I did not get any feedback/review on this patch from the original authors.
But I think this fix should go into Linux 5.4+
Best regards,
Oliver
Quoted 104 lines · Oliver Hartkopp
In commit
d5f9023fa61e("can: bcm: delay release of structbcm_opaftersynchronize_rcu()") Thadeu Lima de Souza Cascardo introduced twosynchronize_rcu()calls inbcm_release()(only once at socket close) and inbcm_delete_rx_op()(called on removal of each singlebcm_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
181d4447905d("can: gw: usecall_rcu()instead of costlysynchronize_rcu()") Eric Dumazet replaced thesynchronize_rcu()calls with several call_rcu()'s to safely remove the data structures after the removal of CAN ID subscriptions withcan_rx_unregister()calls.This patch adopts Erics approach for the can-bcm which should be applicable since the removal of
tasklet_kill()inbcm_remove_op()and the introduction of theHRTIMER_MODE_SOFTtimer handling in Linux 5.4.Fixes d5f9023fa61e ("can: bcm: delay release of struct bcm_op after synchronize_rcu()") # >= 5.4Cc Eric Dumazet <[email protected]>Cc Norbert Slusarek <[email protected]>Cc Thadeu Lima de Souza Cascardo <[email protected]>Signed-off-by Oliver Hartkopp <[email protected]>--- net/can/bcm.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)net/can/bcm.c +14
-4@@ -98,10 +98,11 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset)return *(u64 *)(cp->data + offset); }struct bcm_op {struct list_head list; +struct rcu_head rcu;int ifindex;canid_t can_id;u32 flags;unsigned long frames_abs, frames_filtered;struct bcm_timeval ival1, ival2;@@ -716,24 +717,31 @@ 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);if ((op->last_frames) && (op->last_frames != &op->last_sframe))kfree (op->last_frames);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) {can_rx_unregister (dev_net (dev), dev, op->can_id,REGMASK (op->can_id), bcm_rx_handler, op);@@ -755,10 +763,13 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,list_for_each_entry_safe (op, n, ops, list) {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 * thing to do here. */ @@ -783,11 +794,10 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, op->can_id,REGMASK (op->can_id), bcm_rx_handler, op);list_del (&op->list); -synchronize_rcu ();bcm_remove_op (op);return 1 ;/* done */ } }