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 | ||
