diff options
Diffstat (limited to 'net/llc/llc_input.c')
-rw-r--r-- | net/llc/llc_input.c | 21 |
1 files changed, 17 insertions, 4 deletions
diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c index e32cab44ea95..dd3e83328ad5 100644 --- a/net/llc/llc_input.c +++ b/net/llc/llc_input.c | |||
@@ -42,6 +42,7 @@ static void (*llc_type_handlers[2])(struct llc_sap *sap, | |||
42 | void llc_add_pack(int type, void (*handler)(struct llc_sap *sap, | 42 | void llc_add_pack(int type, void (*handler)(struct llc_sap *sap, |
43 | struct sk_buff *skb)) | 43 | struct sk_buff *skb)) |
44 | { | 44 | { |
45 | smp_wmb(); /* ensure initialisation is complete before it's called */ | ||
45 | if (type == LLC_DEST_SAP || type == LLC_DEST_CONN) | 46 | if (type == LLC_DEST_SAP || type == LLC_DEST_CONN) |
46 | llc_type_handlers[type - 1] = handler; | 47 | llc_type_handlers[type - 1] = handler; |
47 | } | 48 | } |
@@ -50,11 +51,19 @@ void llc_remove_pack(int type) | |||
50 | { | 51 | { |
51 | if (type == LLC_DEST_SAP || type == LLC_DEST_CONN) | 52 | if (type == LLC_DEST_SAP || type == LLC_DEST_CONN) |
52 | llc_type_handlers[type - 1] = NULL; | 53 | llc_type_handlers[type - 1] = NULL; |
54 | synchronize_net(); | ||
53 | } | 55 | } |
54 | 56 | ||
55 | void llc_set_station_handler(void (*handler)(struct sk_buff *skb)) | 57 | void llc_set_station_handler(void (*handler)(struct sk_buff *skb)) |
56 | { | 58 | { |
59 | /* Ensure initialisation is complete before it's called */ | ||
60 | if (handler) | ||
61 | smp_wmb(); | ||
62 | |||
57 | llc_station_handler = handler; | 63 | llc_station_handler = handler; |
64 | |||
65 | if (!handler) | ||
66 | synchronize_net(); | ||
58 | } | 67 | } |
59 | 68 | ||
60 | /** | 69 | /** |
@@ -150,6 +159,8 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev, | |||
150 | int dest; | 159 | int dest; |
151 | int (*rcv)(struct sk_buff *, struct net_device *, | 160 | int (*rcv)(struct sk_buff *, struct net_device *, |
152 | struct packet_type *, struct net_device *); | 161 | struct packet_type *, struct net_device *); |
162 | void (*sta_handler)(struct sk_buff *skb); | ||
163 | void (*sap_handler)(struct llc_sap *sap, struct sk_buff *skb); | ||
153 | 164 | ||
154 | if (!net_eq(dev_net(dev), &init_net)) | 165 | if (!net_eq(dev_net(dev), &init_net)) |
155 | goto drop; | 166 | goto drop; |
@@ -182,7 +193,8 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev, | |||
182 | */ | 193 | */ |
183 | rcv = rcu_dereference(sap->rcv_func); | 194 | rcv = rcu_dereference(sap->rcv_func); |
184 | dest = llc_pdu_type(skb); | 195 | dest = llc_pdu_type(skb); |
185 | if (unlikely(!dest || !llc_type_handlers[dest - 1])) { | 196 | sap_handler = dest ? ACCESS_ONCE(llc_type_handlers[dest - 1]) : NULL; |
197 | if (unlikely(!sap_handler)) { | ||
186 | if (rcv) | 198 | if (rcv) |
187 | rcv(skb, dev, pt, orig_dev); | 199 | rcv(skb, dev, pt, orig_dev); |
188 | else | 200 | else |
@@ -193,7 +205,7 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev, | |||
193 | if (cskb) | 205 | if (cskb) |
194 | rcv(cskb, dev, pt, orig_dev); | 206 | rcv(cskb, dev, pt, orig_dev); |
195 | } | 207 | } |
196 | llc_type_handlers[dest - 1](sap, skb); | 208 | sap_handler(sap, skb); |
197 | } | 209 | } |
198 | llc_sap_put(sap); | 210 | llc_sap_put(sap); |
199 | out: | 211 | out: |
@@ -202,9 +214,10 @@ drop: | |||
202 | kfree_skb(skb); | 214 | kfree_skb(skb); |
203 | goto out; | 215 | goto out; |
204 | handle_station: | 216 | handle_station: |
205 | if (!llc_station_handler) | 217 | sta_handler = ACCESS_ONCE(llc_station_handler); |
218 | if (!sta_handler) | ||
206 | goto drop; | 219 | goto drop; |
207 | llc_station_handler(skb); | 220 | sta_handler(skb); |
208 | goto out; | 221 | goto out; |
209 | } | 222 | } |
210 | 223 | ||