@@ -130,7 +130,7 @@ struct cgw_job {
130
130
u32 handled_frames ;
131
131
u32 dropped_frames ;
132
132
u32 deleted_frames ;
133
- struct cf_mod mod ;
133
+ struct cf_mod __rcu * cf_mod ;
134
134
union {
135
135
/* CAN frame data source */
136
136
struct net_device * dev ;
@@ -459,6 +459,7 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
459
459
struct cgw_job * gwj = (struct cgw_job * )data ;
460
460
struct canfd_frame * cf ;
461
461
struct sk_buff * nskb ;
462
+ struct cf_mod * mod ;
462
463
int modidx = 0 ;
463
464
464
465
/* process strictly Classic CAN or CAN FD frames */
@@ -506,7 +507,8 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
506
507
* When there is at least one modification function activated,
507
508
* we need to copy the skb as we want to modify skb->data.
508
509
*/
509
- if (gwj -> mod .modfunc [0 ])
510
+ mod = rcu_dereference (gwj -> cf_mod );
511
+ if (mod -> modfunc [0 ])
510
512
nskb = skb_copy (skb , GFP_ATOMIC );
511
513
else
512
514
nskb = skb_clone (skb , GFP_ATOMIC );
@@ -529,8 +531,8 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
529
531
cf = (struct canfd_frame * )nskb -> data ;
530
532
531
533
/* perform preprocessed modification functions if there are any */
532
- while (modidx < MAX_MODFUNCTIONS && gwj -> mod . modfunc [modidx ])
533
- (* gwj -> mod . modfunc [modidx ++ ])(cf , & gwj -> mod );
534
+ while (modidx < MAX_MODFUNCTIONS && mod -> modfunc [modidx ])
535
+ (* mod -> modfunc [modidx ++ ])(cf , mod );
534
536
535
537
/* Has the CAN frame been modified? */
536
538
if (modidx ) {
@@ -546,11 +548,11 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
546
548
}
547
549
548
550
/* check for checksum updates */
549
- if (gwj -> mod . csumfunc .crc8 )
550
- (* gwj -> mod . csumfunc .crc8 )(cf , & gwj -> mod . csum .crc8 );
551
+ if (mod -> csumfunc .crc8 )
552
+ (* mod -> csumfunc .crc8 )(cf , & mod -> csum .crc8 );
551
553
552
- if (gwj -> mod . csumfunc .xor )
553
- (* gwj -> mod . csumfunc .xor )(cf , & gwj -> mod . csum .xor );
554
+ if (mod -> csumfunc .xor )
555
+ (* mod -> csumfunc .xor )(cf , & mod -> csum .xor );
554
556
}
555
557
556
558
/* clear the skb timestamp if not configured the other way */
@@ -581,9 +583,20 @@ static void cgw_job_free_rcu(struct rcu_head *rcu_head)
581
583
{
582
584
struct cgw_job * gwj = container_of (rcu_head , struct cgw_job , rcu );
583
585
586
+ /* cgw_job::cf_mod is always accessed from the same cgw_job object within
587
+ * the same RCU read section. Once cgw_job is scheduled for removal,
588
+ * cf_mod can also be removed without mandating an additional grace period.
589
+ */
590
+ kfree (rcu_access_pointer (gwj -> cf_mod ));
584
591
kmem_cache_free (cgw_cache , gwj );
585
592
}
586
593
594
+ /* Return cgw_job::cf_mod with RTNL protected section */
595
+ static struct cf_mod * cgw_job_cf_mod (struct cgw_job * gwj )
596
+ {
597
+ return rcu_dereference_protected (gwj -> cf_mod , rtnl_is_locked ());
598
+ }
599
+
587
600
static int cgw_notifier (struct notifier_block * nb ,
588
601
unsigned long msg , void * ptr )
589
602
{
@@ -616,6 +629,7 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
616
629
{
617
630
struct rtcanmsg * rtcan ;
618
631
struct nlmsghdr * nlh ;
632
+ struct cf_mod * mod ;
619
633
620
634
nlh = nlmsg_put (skb , pid , seq , type , sizeof (* rtcan ), flags );
621
635
if (!nlh )
@@ -650,82 +664,83 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
650
664
goto cancel ;
651
665
}
652
666
667
+ mod = cgw_job_cf_mod (gwj );
653
668
if (gwj -> flags & CGW_FLAGS_CAN_FD ) {
654
669
struct cgw_fdframe_mod mb ;
655
670
656
- if (gwj -> mod . modtype .and ) {
657
- memcpy (& mb .cf , & gwj -> mod . modframe .and , sizeof (mb .cf ));
658
- mb .modtype = gwj -> mod . modtype .and ;
671
+ if (mod -> modtype .and ) {
672
+ memcpy (& mb .cf , & mod -> modframe .and , sizeof (mb .cf ));
673
+ mb .modtype = mod -> modtype .and ;
659
674
if (nla_put (skb , CGW_FDMOD_AND , sizeof (mb ), & mb ) < 0 )
660
675
goto cancel ;
661
676
}
662
677
663
- if (gwj -> mod . modtype .or ) {
664
- memcpy (& mb .cf , & gwj -> mod . modframe .or , sizeof (mb .cf ));
665
- mb .modtype = gwj -> mod . modtype .or ;
678
+ if (mod -> modtype .or ) {
679
+ memcpy (& mb .cf , & mod -> modframe .or , sizeof (mb .cf ));
680
+ mb .modtype = mod -> modtype .or ;
666
681
if (nla_put (skb , CGW_FDMOD_OR , sizeof (mb ), & mb ) < 0 )
667
682
goto cancel ;
668
683
}
669
684
670
- if (gwj -> mod . modtype .xor ) {
671
- memcpy (& mb .cf , & gwj -> mod . modframe .xor , sizeof (mb .cf ));
672
- mb .modtype = gwj -> mod . modtype .xor ;
685
+ if (mod -> modtype .xor ) {
686
+ memcpy (& mb .cf , & mod -> modframe .xor , sizeof (mb .cf ));
687
+ mb .modtype = mod -> modtype .xor ;
673
688
if (nla_put (skb , CGW_FDMOD_XOR , sizeof (mb ), & mb ) < 0 )
674
689
goto cancel ;
675
690
}
676
691
677
- if (gwj -> mod . modtype .set ) {
678
- memcpy (& mb .cf , & gwj -> mod . modframe .set , sizeof (mb .cf ));
679
- mb .modtype = gwj -> mod . modtype .set ;
692
+ if (mod -> modtype .set ) {
693
+ memcpy (& mb .cf , & mod -> modframe .set , sizeof (mb .cf ));
694
+ mb .modtype = mod -> modtype .set ;
680
695
if (nla_put (skb , CGW_FDMOD_SET , sizeof (mb ), & mb ) < 0 )
681
696
goto cancel ;
682
697
}
683
698
} else {
684
699
struct cgw_frame_mod mb ;
685
700
686
- if (gwj -> mod . modtype .and ) {
687
- memcpy (& mb .cf , & gwj -> mod . modframe .and , sizeof (mb .cf ));
688
- mb .modtype = gwj -> mod . modtype .and ;
701
+ if (mod -> modtype .and ) {
702
+ memcpy (& mb .cf , & mod -> modframe .and , sizeof (mb .cf ));
703
+ mb .modtype = mod -> modtype .and ;
689
704
if (nla_put (skb , CGW_MOD_AND , sizeof (mb ), & mb ) < 0 )
690
705
goto cancel ;
691
706
}
692
707
693
- if (gwj -> mod . modtype .or ) {
694
- memcpy (& mb .cf , & gwj -> mod . modframe .or , sizeof (mb .cf ));
695
- mb .modtype = gwj -> mod . modtype .or ;
708
+ if (mod -> modtype .or ) {
709
+ memcpy (& mb .cf , & mod -> modframe .or , sizeof (mb .cf ));
710
+ mb .modtype = mod -> modtype .or ;
696
711
if (nla_put (skb , CGW_MOD_OR , sizeof (mb ), & mb ) < 0 )
697
712
goto cancel ;
698
713
}
699
714
700
- if (gwj -> mod . modtype .xor ) {
701
- memcpy (& mb .cf , & gwj -> mod . modframe .xor , sizeof (mb .cf ));
702
- mb .modtype = gwj -> mod . modtype .xor ;
715
+ if (mod -> modtype .xor ) {
716
+ memcpy (& mb .cf , & mod -> modframe .xor , sizeof (mb .cf ));
717
+ mb .modtype = mod -> modtype .xor ;
703
718
if (nla_put (skb , CGW_MOD_XOR , sizeof (mb ), & mb ) < 0 )
704
719
goto cancel ;
705
720
}
706
721
707
- if (gwj -> mod . modtype .set ) {
708
- memcpy (& mb .cf , & gwj -> mod . modframe .set , sizeof (mb .cf ));
709
- mb .modtype = gwj -> mod . modtype .set ;
722
+ if (mod -> modtype .set ) {
723
+ memcpy (& mb .cf , & mod -> modframe .set , sizeof (mb .cf ));
724
+ mb .modtype = mod -> modtype .set ;
710
725
if (nla_put (skb , CGW_MOD_SET , sizeof (mb ), & mb ) < 0 )
711
726
goto cancel ;
712
727
}
713
728
}
714
729
715
- if (gwj -> mod . uid ) {
716
- if (nla_put_u32 (skb , CGW_MOD_UID , gwj -> mod . uid ) < 0 )
730
+ if (mod -> uid ) {
731
+ if (nla_put_u32 (skb , CGW_MOD_UID , mod -> uid ) < 0 )
717
732
goto cancel ;
718
733
}
719
734
720
- if (gwj -> mod . csumfunc .crc8 ) {
735
+ if (mod -> csumfunc .crc8 ) {
721
736
if (nla_put (skb , CGW_CS_CRC8 , CGW_CS_CRC8_LEN ,
722
- & gwj -> mod . csum .crc8 ) < 0 )
737
+ & mod -> csum .crc8 ) < 0 )
723
738
goto cancel ;
724
739
}
725
740
726
- if (gwj -> mod . csumfunc .xor ) {
741
+ if (mod -> csumfunc .xor ) {
727
742
if (nla_put (skb , CGW_CS_XOR , CGW_CS_XOR_LEN ,
728
- & gwj -> mod . csum .xor ) < 0 )
743
+ & mod -> csum .xor ) < 0 )
729
744
goto cancel ;
730
745
}
731
746
@@ -1059,7 +1074,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
1059
1074
struct net * net = sock_net (skb -> sk );
1060
1075
struct rtcanmsg * r ;
1061
1076
struct cgw_job * gwj ;
1062
- struct cf_mod mod ;
1077
+ struct cf_mod * mod ;
1063
1078
struct can_can_gw ccgw ;
1064
1079
u8 limhops = 0 ;
1065
1080
int err = 0 ;
@@ -1078,37 +1093,48 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
1078
1093
if (r -> gwtype != CGW_TYPE_CAN_CAN )
1079
1094
return - EINVAL ;
1080
1095
1081
- err = cgw_parse_attr (nlh , & mod , CGW_TYPE_CAN_CAN , & ccgw , & limhops );
1096
+ mod = kmalloc (sizeof (* mod ), GFP_KERNEL );
1097
+ if (!mod )
1098
+ return - ENOMEM ;
1099
+
1100
+ err = cgw_parse_attr (nlh , mod , CGW_TYPE_CAN_CAN , & ccgw , & limhops );
1082
1101
if (err < 0 )
1083
- return err ;
1102
+ goto out_free_cf ;
1084
1103
1085
- if (mod . uid ) {
1104
+ if (mod -> uid ) {
1086
1105
ASSERT_RTNL ();
1087
1106
1088
1107
/* check for updating an existing job with identical uid */
1089
1108
hlist_for_each_entry (gwj , & net -> can .cgw_list , list ) {
1090
- if (gwj -> mod .uid != mod .uid )
1109
+ struct cf_mod * old_cf ;
1110
+
1111
+ old_cf = cgw_job_cf_mod (gwj );
1112
+ if (old_cf -> uid != mod -> uid )
1091
1113
continue ;
1092
1114
1093
1115
/* interfaces & filters must be identical */
1094
- if (memcmp (& gwj -> ccgw , & ccgw , sizeof (ccgw )))
1095
- return - EINVAL ;
1116
+ if (memcmp (& gwj -> ccgw , & ccgw , sizeof (ccgw ))) {
1117
+ err = - EINVAL ;
1118
+ goto out_free_cf ;
1119
+ }
1096
1120
1097
- /* update modifications with disabled softirq & quit */
1098
- local_bh_disable ();
1099
- memcpy (& gwj -> mod , & mod , sizeof (mod ));
1100
- local_bh_enable ();
1121
+ rcu_assign_pointer (gwj -> cf_mod , mod );
1122
+ kfree_rcu_mightsleep (old_cf );
1101
1123
return 0 ;
1102
1124
}
1103
1125
}
1104
1126
1105
1127
/* ifindex == 0 is not allowed for job creation */
1106
- if (!ccgw .src_idx || !ccgw .dst_idx )
1107
- return - ENODEV ;
1128
+ if (!ccgw .src_idx || !ccgw .dst_idx ) {
1129
+ err = - ENODEV ;
1130
+ goto out_free_cf ;
1131
+ }
1108
1132
1109
1133
gwj = kmem_cache_alloc (cgw_cache , GFP_KERNEL );
1110
- if (!gwj )
1111
- return - ENOMEM ;
1134
+ if (!gwj ) {
1135
+ err = - ENOMEM ;
1136
+ goto out_free_cf ;
1137
+ }
1112
1138
1113
1139
gwj -> handled_frames = 0 ;
1114
1140
gwj -> dropped_frames = 0 ;
@@ -1118,7 +1144,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
1118
1144
gwj -> limit_hops = limhops ;
1119
1145
1120
1146
/* insert already parsed information */
1121
- memcpy ( & gwj -> mod , & mod , sizeof ( mod ) );
1147
+ RCU_INIT_POINTER ( gwj -> cf_mod , mod );
1122
1148
memcpy (& gwj -> ccgw , & ccgw , sizeof (ccgw ));
1123
1149
1124
1150
err = - ENODEV ;
@@ -1152,9 +1178,11 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
1152
1178
if (!err )
1153
1179
hlist_add_head_rcu (& gwj -> list , & net -> can .cgw_list );
1154
1180
out :
1155
- if (err )
1181
+ if (err ) {
1156
1182
kmem_cache_free (cgw_cache , gwj );
1157
-
1183
+ out_free_cf :
1184
+ kfree (mod );
1185
+ }
1158
1186
return err ;
1159
1187
}
1160
1188
@@ -1214,19 +1242,22 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
1214
1242
1215
1243
/* remove only the first matching entry */
1216
1244
hlist_for_each_entry_safe (gwj , nx , & net -> can .cgw_list , list ) {
1245
+ struct cf_mod * cf_mod ;
1246
+
1217
1247
if (gwj -> flags != r -> flags )
1218
1248
continue ;
1219
1249
1220
1250
if (gwj -> limit_hops != limhops )
1221
1251
continue ;
1222
1252
1253
+ cf_mod = cgw_job_cf_mod (gwj );
1223
1254
/* we have a match when uid is enabled and identical */
1224
- if (gwj -> mod . uid || mod .uid ) {
1225
- if (gwj -> mod . uid != mod .uid )
1255
+ if (cf_mod -> uid || mod .uid ) {
1256
+ if (cf_mod -> uid != mod .uid )
1226
1257
continue ;
1227
1258
} else {
1228
1259
/* no uid => check for identical modifications */
1229
- if (memcmp (& gwj -> mod , & mod , sizeof (mod )))
1260
+ if (memcmp (cf_mod , & mod , sizeof (mod )))
1230
1261
continue ;
1231
1262
}
1232
1263
0 commit comments