aboutsummaryrefslogtreecommitdiffstats
path: root/net/llc/llc_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/llc/llc_input.c')
-rw-r--r--net/llc/llc_input.c21
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,
42void llc_add_pack(int type, void (*handler)(struct llc_sap *sap, 42void 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
55void llc_set_station_handler(void (*handler)(struct sk_buff *skb)) 57void 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);
199out: 211out:
@@ -202,9 +214,10 @@ drop:
202 kfree_skb(skb); 214 kfree_skb(skb);
203 goto out; 215 goto out;
204handle_station: 216handle_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