5858#include <linux/can/skb.h>
5959#include <linux/can/bcm.h>
6060#include <linux/slab.h>
61+ #include <linux/spinlock.h>
6162#include <net/sock.h>
6263#include <net/net_namespace.h>
6364
@@ -122,6 +123,7 @@ struct bcm_op {
122123 struct canfd_frame last_sframe ;
123124 struct sock * sk ;
124125 struct net_device * rx_reg_dev ;
126+ spinlock_t bcm_tx_lock ; /* protect currframe/count in runtime updates */
125127};
126128
127129struct bcm_sock {
@@ -217,7 +219,9 @@ static int bcm_proc_show(struct seq_file *m, void *v)
217219 seq_printf (m , " / bound %s" , bcm_proc_getifname (net , ifname , bo -> ifindex ));
218220 seq_printf (m , " <<<\n" );
219221
220- list_for_each_entry (op , & bo -> rx_ops , list ) {
222+ rcu_read_lock ();
223+
224+ list_for_each_entry_rcu (op , & bo -> rx_ops , list ) {
221225
222226 unsigned long reduction ;
223227
@@ -273,6 +277,9 @@ static int bcm_proc_show(struct seq_file *m, void *v)
273277 seq_printf (m , "# sent %ld\n" , op -> frames_abs );
274278 }
275279 seq_putc (m , '\n' );
280+
281+ rcu_read_unlock ();
282+
276283 return 0 ;
277284}
278285#endif /* CONFIG_PROC_FS */
@@ -285,13 +292,18 @@ static void bcm_can_tx(struct bcm_op *op)
285292{
286293 struct sk_buff * skb ;
287294 struct net_device * dev ;
288- struct canfd_frame * cf = op -> frames + op -> cfsiz * op -> currframe ;
295+ struct canfd_frame * cf ;
289296 int err ;
290297
291298 /* no target device? => exit */
292299 if (!op -> ifindex )
293300 return ;
294301
302+ /* read currframe under lock protection */
303+ spin_lock_bh (& op -> bcm_tx_lock );
304+ cf = op -> frames + op -> cfsiz * op -> currframe ;
305+ spin_unlock_bh (& op -> bcm_tx_lock );
306+
295307 dev = dev_get_by_index (sock_net (op -> sk ), op -> ifindex );
296308 if (!dev ) {
297309 /* RFC: should this bcm_op remove itself here? */
@@ -312,6 +324,10 @@ static void bcm_can_tx(struct bcm_op *op)
312324 skb -> dev = dev ;
313325 can_skb_set_owner (skb , op -> sk );
314326 err = can_send (skb , 1 );
327+
328+ /* update currframe and count under lock protection */
329+ spin_lock_bh (& op -> bcm_tx_lock );
330+
315331 if (!err )
316332 op -> frames_abs ++ ;
317333
@@ -320,6 +336,11 @@ static void bcm_can_tx(struct bcm_op *op)
320336 /* reached last frame? */
321337 if (op -> currframe >= op -> nframes )
322338 op -> currframe = 0 ;
339+
340+ if (op -> count > 0 )
341+ op -> count -- ;
342+
343+ spin_unlock_bh (& op -> bcm_tx_lock );
323344out :
324345 dev_put (dev );
325346}
@@ -430,7 +451,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
430451 struct bcm_msg_head msg_head ;
431452
432453 if (op -> kt_ival1 && (op -> count > 0 )) {
433- op -> count -- ;
454+ bcm_can_tx ( op ) ;
434455 if (!op -> count && (op -> flags & TX_COUNTEVT )) {
435456
436457 /* create notification to user */
@@ -445,7 +466,6 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
445466
446467 bcm_send_to_user (op , & msg_head , NULL , 0 );
447468 }
448- bcm_can_tx (op );
449469
450470 } else if (op -> kt_ival2 ) {
451471 bcm_can_tx (op );
@@ -843,7 +863,7 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
843863 REGMASK (op -> can_id ),
844864 bcm_rx_handler , op );
845865
846- list_del (& op -> list );
866+ list_del_rcu (& op -> list );
847867 bcm_remove_op (op );
848868 return 1 ; /* done */
849869 }
@@ -863,7 +883,7 @@ static int bcm_delete_tx_op(struct list_head *ops, struct bcm_msg_head *mh,
863883 list_for_each_entry_safe (op , n , ops , list ) {
864884 if ((op -> can_id == mh -> can_id ) && (op -> ifindex == ifindex ) &&
865885 (op -> flags & CAN_FD_FRAME ) == (mh -> flags & CAN_FD_FRAME )) {
866- list_del (& op -> list );
886+ list_del_rcu (& op -> list );
867887 bcm_remove_op (op );
868888 return 1 ; /* done */
869889 }
@@ -956,16 +976,42 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
956976 }
957977 op -> flags = msg_head -> flags ;
958978
979+ /* only lock for unlikely count/nframes/currframe changes */
980+ if (op -> nframes != msg_head -> nframes ||
981+ op -> flags & TX_RESET_MULTI_IDX ||
982+ op -> flags & SETTIMER ) {
983+
984+ spin_lock_bh (& op -> bcm_tx_lock );
985+
986+ if (op -> nframes != msg_head -> nframes ||
987+ op -> flags & TX_RESET_MULTI_IDX ) {
988+ /* potentially update changed nframes */
989+ op -> nframes = msg_head -> nframes ;
990+ /* restart multiple frame transmission */
991+ op -> currframe = 0 ;
992+ }
993+
994+ if (op -> flags & SETTIMER )
995+ op -> count = msg_head -> count ;
996+
997+ spin_unlock_bh (& op -> bcm_tx_lock );
998+ }
999+
9591000 } else {
9601001 /* insert new BCM operation for the given can_id */
9611002
9621003 op = kzalloc (OPSIZ , GFP_KERNEL );
9631004 if (!op )
9641005 return - ENOMEM ;
9651006
1007+ spin_lock_init (& op -> bcm_tx_lock );
9661008 op -> can_id = msg_head -> can_id ;
9671009 op -> cfsiz = CFSIZ (msg_head -> flags );
9681010 op -> flags = msg_head -> flags ;
1011+ op -> nframes = msg_head -> nframes ;
1012+
1013+ if (op -> flags & SETTIMER )
1014+ op -> count = msg_head -> count ;
9691015
9701016 /* create array for CAN frames and copy the data */
9711017 if (msg_head -> nframes > 1 ) {
@@ -1023,22 +1069,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
10231069
10241070 } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
10251071
1026- if (op -> nframes != msg_head -> nframes ) {
1027- op -> nframes = msg_head -> nframes ;
1028- /* start multiple frame transmission with index 0 */
1029- op -> currframe = 0 ;
1030- }
1031-
1032- /* check flags */
1033-
1034- if (op -> flags & TX_RESET_MULTI_IDX ) {
1035- /* start multiple frame transmission with index 0 */
1036- op -> currframe = 0 ;
1037- }
1038-
10391072 if (op -> flags & SETTIMER ) {
10401073 /* set timer values */
1041- op -> count = msg_head -> count ;
10421074 op -> ival1 = msg_head -> ival1 ;
10431075 op -> ival2 = msg_head -> ival2 ;
10441076 op -> kt_ival1 = bcm_timeval_to_ktime (msg_head -> ival1 );
@@ -1055,11 +1087,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
10551087 op -> flags |= TX_ANNOUNCE ;
10561088 }
10571089
1058- if (op -> flags & TX_ANNOUNCE ) {
1090+ if (op -> flags & TX_ANNOUNCE )
10591091 bcm_can_tx (op );
1060- if (op -> count )
1061- op -> count -- ;
1062- }
10631092
10641093 if (op -> flags & STARTTIMER )
10651094 bcm_tx_start_timer (op );
@@ -1272,7 +1301,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
12721301 bcm_rx_handler , op , "bcm" , sk );
12731302 if (err ) {
12741303 /* this bcm rx op is broken -> remove it */
1275- list_del (& op -> list );
1304+ list_del_rcu (& op -> list );
12761305 bcm_remove_op (op );
12771306 return err ;
12781307 }
0 commit comments