diff options
| -rw-r--r-- | net/llc/llc_input.c | 21 | ||||
| -rw-r--r-- | net/llc/llc_station.c | 2 |
2 files changed, 18 insertions, 5 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 | ||
diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c index bba5184fafd7..b2f2bac2c2a2 100644 --- a/net/llc/llc_station.c +++ b/net/llc/llc_station.c | |||
| @@ -696,9 +696,9 @@ void __init llc_station_init(void) | |||
| 696 | (unsigned long)&llc_main_station); | 696 | (unsigned long)&llc_main_station); |
| 697 | llc_main_station.ack_timer.expires = jiffies + | 697 | llc_main_station.ack_timer.expires = jiffies + |
| 698 | sysctl_llc_station_ack_timeout; | 698 | sysctl_llc_station_ack_timeout; |
| 699 | llc_set_station_handler(llc_station_rcv); | ||
| 700 | llc_main_station.maximum_retry = 1; | 699 | llc_main_station.maximum_retry = 1; |
| 701 | llc_main_station.state = LLC_STATION_STATE_UP; | 700 | llc_main_station.state = LLC_STATION_STATE_UP; |
| 701 | llc_set_station_handler(llc_station_rcv); | ||
| 702 | } | 702 | } |
| 703 | 703 | ||
| 704 | void llc_station_exit(void) | 704 | void llc_station_exit(void) |
