diff options
Diffstat (limited to 'net/llc')
-rw-r--r-- | net/llc/af_llc.c | 74 | ||||
-rw-r--r-- | net/llc/llc_c_ac.c | 3 | ||||
-rw-r--r-- | net/llc/llc_conn.c | 147 | ||||
-rw-r--r-- | net/llc/llc_core.c | 53 | ||||
-rw-r--r-- | net/llc/llc_if.c | 1 | ||||
-rw-r--r-- | net/llc/llc_input.c | 1 | ||||
-rw-r--r-- | net/llc/llc_output.c | 45 | ||||
-rw-r--r-- | net/llc/llc_proc.c | 69 | ||||
-rw-r--r-- | net/llc/llc_sap.c | 114 | ||||
-rw-r--r-- | net/llc/llc_station.c | 1 | ||||
-rw-r--r-- | net/llc/sysctl_net_llc.c | 25 |
11 files changed, 338 insertions, 195 deletions
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 7aa4fd170104..2db6a9f75913 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/rtnetlink.h> | 26 | #include <linux/rtnetlink.h> |
27 | #include <linux/init.h> | 27 | #include <linux/init.h> |
28 | #include <linux/slab.h> | ||
28 | #include <net/llc.h> | 29 | #include <net/llc.h> |
29 | #include <net/llc_sap.h> | 30 | #include <net/llc_sap.h> |
30 | #include <net/llc_pdu.h> | 31 | #include <net/llc_pdu.h> |
@@ -47,6 +48,10 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout); | |||
47 | #define dprintk(args...) | 48 | #define dprintk(args...) |
48 | #endif | 49 | #endif |
49 | 50 | ||
51 | /* Maybe we'll add some more in the future. */ | ||
52 | #define LLC_CMSG_PKTINFO 1 | ||
53 | |||
54 | |||
50 | /** | 55 | /** |
51 | * llc_ui_next_link_no - return the next unused link number for a sap | 56 | * llc_ui_next_link_no - return the next unused link number for a sap |
52 | * @sap: Address of sap to get link number from. | 57 | * @sap: Address of sap to get link number from. |
@@ -136,18 +141,22 @@ static struct proto llc_proto = { | |||
136 | .name = "LLC", | 141 | .name = "LLC", |
137 | .owner = THIS_MODULE, | 142 | .owner = THIS_MODULE, |
138 | .obj_size = sizeof(struct llc_sock), | 143 | .obj_size = sizeof(struct llc_sock), |
144 | .slab_flags = SLAB_DESTROY_BY_RCU, | ||
139 | }; | 145 | }; |
140 | 146 | ||
141 | /** | 147 | /** |
142 | * llc_ui_create - alloc and init a new llc_ui socket | 148 | * llc_ui_create - alloc and init a new llc_ui socket |
149 | * @net: network namespace (must be default network) | ||
143 | * @sock: Socket to initialize and attach allocated sk to. | 150 | * @sock: Socket to initialize and attach allocated sk to. |
144 | * @protocol: Unused. | 151 | * @protocol: Unused. |
152 | * @kern: on behalf of kernel or userspace | ||
145 | * | 153 | * |
146 | * Allocate and initialize a new llc_ui socket, validate the user wants a | 154 | * Allocate and initialize a new llc_ui socket, validate the user wants a |
147 | * socket type we have available. | 155 | * socket type we have available. |
148 | * Returns 0 upon success, negative upon failure. | 156 | * Returns 0 upon success, negative upon failure. |
149 | */ | 157 | */ |
150 | static int llc_ui_create(struct net *net, struct socket *sock, int protocol) | 158 | static int llc_ui_create(struct net *net, struct socket *sock, int protocol, |
159 | int kern) | ||
151 | { | 160 | { |
152 | struct sock *sk; | 161 | struct sock *sk; |
153 | int rc = -ESOCKTNOSUPPORT; | 162 | int rc = -ESOCKTNOSUPPORT; |
@@ -155,7 +164,7 @@ static int llc_ui_create(struct net *net, struct socket *sock, int protocol) | |||
155 | if (!capable(CAP_NET_RAW)) | 164 | if (!capable(CAP_NET_RAW)) |
156 | return -EPERM; | 165 | return -EPERM; |
157 | 166 | ||
158 | if (net != &init_net) | 167 | if (!net_eq(net, &init_net)) |
159 | return -EAFNOSUPPORT; | 168 | return -EAFNOSUPPORT; |
160 | 169 | ||
161 | if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) { | 170 | if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) { |
@@ -189,10 +198,8 @@ static int llc_ui_release(struct socket *sock) | |||
189 | llc->laddr.lsap, llc->daddr.lsap); | 198 | llc->laddr.lsap, llc->daddr.lsap); |
190 | if (!llc_send_disc(sk)) | 199 | if (!llc_send_disc(sk)) |
191 | llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo); | 200 | llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo); |
192 | if (!sock_flag(sk, SOCK_ZAPPED)) { | 201 | if (!sock_flag(sk, SOCK_ZAPPED)) |
193 | llc_sap_put(llc->sap); | ||
194 | llc_sap_remove_socket(llc->sap, sk); | 202 | llc_sap_remove_socket(llc->sap, sk); |
195 | } | ||
196 | release_sock(sk); | 203 | release_sock(sk); |
197 | if (llc->dev) | 204 | if (llc->dev) |
198 | dev_put(llc->dev); | 205 | dev_put(llc->dev); |
@@ -252,7 +259,14 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr) | |||
252 | if (!sock_flag(sk, SOCK_ZAPPED)) | 259 | if (!sock_flag(sk, SOCK_ZAPPED)) |
253 | goto out; | 260 | goto out; |
254 | rc = -ENODEV; | 261 | rc = -ENODEV; |
255 | llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd); | 262 | if (sk->sk_bound_dev_if) { |
263 | llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if); | ||
264 | if (llc->dev && addr->sllc_arphrd != llc->dev->type) { | ||
265 | dev_put(llc->dev); | ||
266 | llc->dev = NULL; | ||
267 | } | ||
268 | } else | ||
269 | llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd); | ||
256 | if (!llc->dev) | 270 | if (!llc->dev) |
257 | goto out; | 271 | goto out; |
258 | rc = -EUSERS; | 272 | rc = -EUSERS; |
@@ -303,7 +317,25 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) | |||
303 | goto out; | 317 | goto out; |
304 | rc = -ENODEV; | 318 | rc = -ENODEV; |
305 | rtnl_lock(); | 319 | rtnl_lock(); |
306 | llc->dev = dev_getbyhwaddr(&init_net, addr->sllc_arphrd, addr->sllc_mac); | 320 | if (sk->sk_bound_dev_if) { |
321 | llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if); | ||
322 | if (llc->dev) { | ||
323 | if (!addr->sllc_arphrd) | ||
324 | addr->sllc_arphrd = llc->dev->type; | ||
325 | if (llc_mac_null(addr->sllc_mac)) | ||
326 | memcpy(addr->sllc_mac, llc->dev->dev_addr, | ||
327 | IFHWADDRLEN); | ||
328 | if (addr->sllc_arphrd != llc->dev->type || | ||
329 | !llc_mac_match(addr->sllc_mac, | ||
330 | llc->dev->dev_addr)) { | ||
331 | rc = -EINVAL; | ||
332 | dev_put(llc->dev); | ||
333 | llc->dev = NULL; | ||
334 | } | ||
335 | } | ||
336 | } else | ||
337 | llc->dev = dev_getbyhwaddr(&init_net, addr->sllc_arphrd, | ||
338 | addr->sllc_mac); | ||
307 | rtnl_unlock(); | 339 | rtnl_unlock(); |
308 | if (!llc->dev) | 340 | if (!llc->dev) |
309 | goto out; | 341 | goto out; |
@@ -319,7 +351,6 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) | |||
319 | rc = -EBUSY; /* some other network layer is using the sap */ | 351 | rc = -EBUSY; /* some other network layer is using the sap */ |
320 | if (!sap) | 352 | if (!sap) |
321 | goto out; | 353 | goto out; |
322 | llc_sap_hold(sap); | ||
323 | } else { | 354 | } else { |
324 | struct llc_addr laddr, daddr; | 355 | struct llc_addr laddr, daddr; |
325 | struct sock *ask; | 356 | struct sock *ask; |
@@ -588,6 +619,20 @@ static int llc_wait_data(struct sock *sk, long timeo) | |||
588 | return rc; | 619 | return rc; |
589 | } | 620 | } |
590 | 621 | ||
622 | static void llc_cmsg_rcv(struct msghdr *msg, struct sk_buff *skb) | ||
623 | { | ||
624 | struct llc_sock *llc = llc_sk(skb->sk); | ||
625 | |||
626 | if (llc->cmsg_flags & LLC_CMSG_PKTINFO) { | ||
627 | struct llc_pktinfo info; | ||
628 | |||
629 | info.lpi_ifindex = llc_sk(skb->sk)->dev->ifindex; | ||
630 | llc_pdu_decode_dsap(skb, &info.lpi_sap); | ||
631 | llc_pdu_decode_da(skb, info.lpi_mac); | ||
632 | put_cmsg(msg, SOL_LLC, LLC_OPT_PKTINFO, sizeof(info), &info); | ||
633 | } | ||
634 | } | ||
635 | |||
591 | /** | 636 | /** |
592 | * llc_ui_accept - accept a new incoming connection. | 637 | * llc_ui_accept - accept a new incoming connection. |
593 | * @sock: Socket which connections arrive on. | 638 | * @sock: Socket which connections arrive on. |
@@ -809,6 +854,8 @@ copy_uaddr: | |||
809 | memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); | 854 | memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); |
810 | msg->msg_namelen = sizeof(*uaddr); | 855 | msg->msg_namelen = sizeof(*uaddr); |
811 | } | 856 | } |
857 | if (llc_sk(sk)->cmsg_flags) | ||
858 | llc_cmsg_rcv(msg, skb); | ||
812 | goto out; | 859 | goto out; |
813 | } | 860 | } |
814 | 861 | ||
@@ -1027,6 +1074,12 @@ static int llc_ui_setsockopt(struct socket *sock, int level, int optname, | |||
1027 | goto out; | 1074 | goto out; |
1028 | llc->rw = opt; | 1075 | llc->rw = opt; |
1029 | break; | 1076 | break; |
1077 | case LLC_OPT_PKTINFO: | ||
1078 | if (opt) | ||
1079 | llc->cmsg_flags |= LLC_CMSG_PKTINFO; | ||
1080 | else | ||
1081 | llc->cmsg_flags &= ~LLC_CMSG_PKTINFO; | ||
1082 | break; | ||
1030 | default: | 1083 | default: |
1031 | rc = -ENOPROTOOPT; | 1084 | rc = -ENOPROTOOPT; |
1032 | goto out; | 1085 | goto out; |
@@ -1080,6 +1133,9 @@ static int llc_ui_getsockopt(struct socket *sock, int level, int optname, | |||
1080 | val = llc->k; break; | 1133 | val = llc->k; break; |
1081 | case LLC_OPT_RX_WIN: | 1134 | case LLC_OPT_RX_WIN: |
1082 | val = llc->rw; break; | 1135 | val = llc->rw; break; |
1136 | case LLC_OPT_PKTINFO: | ||
1137 | val = (llc->cmsg_flags & LLC_CMSG_PKTINFO) != 0; | ||
1138 | break; | ||
1083 | default: | 1139 | default: |
1084 | rc = -ENOPROTOOPT; | 1140 | rc = -ENOPROTOOPT; |
1085 | goto out; | 1141 | goto out; |
@@ -1092,7 +1148,7 @@ out: | |||
1092 | return rc; | 1148 | return rc; |
1093 | } | 1149 | } |
1094 | 1150 | ||
1095 | static struct net_proto_family llc_ui_family_ops = { | 1151 | static const struct net_proto_family llc_ui_family_ops = { |
1096 | .family = PF_LLC, | 1152 | .family = PF_LLC, |
1097 | .create = llc_ui_create, | 1153 | .create = llc_ui_create, |
1098 | .owner = THIS_MODULE, | 1154 | .owner = THIS_MODULE, |
diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c index 019c780512e8..ea225bd2672c 100644 --- a/net/llc/llc_c_ac.c +++ b/net/llc/llc_c_ac.c | |||
@@ -18,6 +18,7 @@ | |||
18 | * See the GNU General Public License for more details. | 18 | * See the GNU General Public License for more details. |
19 | */ | 19 | */ |
20 | #include <linux/netdevice.h> | 20 | #include <linux/netdevice.h> |
21 | #include <linux/slab.h> | ||
21 | #include <net/llc_conn.h> | 22 | #include <net/llc_conn.h> |
22 | #include <net/llc_sap.h> | 23 | #include <net/llc_sap.h> |
23 | #include <net/sock.h> | 24 | #include <net/sock.h> |
@@ -1437,7 +1438,7 @@ static void llc_process_tmr_ev(struct sock *sk, struct sk_buff *skb) | |||
1437 | llc_conn_state_process(sk, skb); | 1438 | llc_conn_state_process(sk, skb); |
1438 | else { | 1439 | else { |
1439 | llc_set_backlog_type(skb, LLC_EVENT); | 1440 | llc_set_backlog_type(skb, LLC_EVENT); |
1440 | sk_add_backlog(sk, skb); | 1441 | __sk_add_backlog(sk, skb); |
1441 | } | 1442 | } |
1442 | } | 1443 | } |
1443 | } | 1444 | } |
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index c6bab39b018e..ba137a6a224d 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c | |||
@@ -13,6 +13,7 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/slab.h> | ||
16 | #include <net/llc_sap.h> | 17 | #include <net/llc_sap.h> |
17 | #include <net/llc_conn.h> | 18 | #include <net/llc_conn.h> |
18 | #include <net/sock.h> | 19 | #include <net/sock.h> |
@@ -468,6 +469,19 @@ static int llc_exec_conn_trans_actions(struct sock *sk, | |||
468 | return rc; | 469 | return rc; |
469 | } | 470 | } |
470 | 471 | ||
472 | static inline bool llc_estab_match(const struct llc_sap *sap, | ||
473 | const struct llc_addr *daddr, | ||
474 | const struct llc_addr *laddr, | ||
475 | const struct sock *sk) | ||
476 | { | ||
477 | struct llc_sock *llc = llc_sk(sk); | ||
478 | |||
479 | return llc->laddr.lsap == laddr->lsap && | ||
480 | llc->daddr.lsap == daddr->lsap && | ||
481 | llc_mac_match(llc->laddr.mac, laddr->mac) && | ||
482 | llc_mac_match(llc->daddr.mac, daddr->mac); | ||
483 | } | ||
484 | |||
471 | /** | 485 | /** |
472 | * __llc_lookup_established - Finds connection for the remote/local sap/mac | 486 | * __llc_lookup_established - Finds connection for the remote/local sap/mac |
473 | * @sap: SAP | 487 | * @sap: SAP |
@@ -484,23 +498,35 @@ static struct sock *__llc_lookup_established(struct llc_sap *sap, | |||
484 | struct llc_addr *laddr) | 498 | struct llc_addr *laddr) |
485 | { | 499 | { |
486 | struct sock *rc; | 500 | struct sock *rc; |
487 | struct hlist_node *node; | 501 | struct hlist_nulls_node *node; |
488 | 502 | int slot = llc_sk_laddr_hashfn(sap, laddr); | |
489 | read_lock(&sap->sk_list.lock); | 503 | struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot]; |
490 | sk_for_each(rc, node, &sap->sk_list.list) { | 504 | |
491 | struct llc_sock *llc = llc_sk(rc); | 505 | rcu_read_lock(); |
492 | 506 | again: | |
493 | if (llc->laddr.lsap == laddr->lsap && | 507 | sk_nulls_for_each_rcu(rc, node, laddr_hb) { |
494 | llc->daddr.lsap == daddr->lsap && | 508 | if (llc_estab_match(sap, daddr, laddr, rc)) { |
495 | llc_mac_match(llc->laddr.mac, laddr->mac) && | 509 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ |
496 | llc_mac_match(llc->daddr.mac, daddr->mac)) { | 510 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) |
497 | sock_hold(rc); | 511 | goto again; |
512 | if (unlikely(llc_sk(rc)->sap != sap || | ||
513 | !llc_estab_match(sap, daddr, laddr, rc))) { | ||
514 | sock_put(rc); | ||
515 | continue; | ||
516 | } | ||
498 | goto found; | 517 | goto found; |
499 | } | 518 | } |
500 | } | 519 | } |
501 | rc = NULL; | 520 | rc = NULL; |
521 | /* | ||
522 | * if the nulls value we got at the end of this lookup is | ||
523 | * not the expected one, we must restart lookup. | ||
524 | * We probably met an item that was moved to another chain. | ||
525 | */ | ||
526 | if (unlikely(get_nulls_value(node) != slot)) | ||
527 | goto again; | ||
502 | found: | 528 | found: |
503 | read_unlock(&sap->sk_list.lock); | 529 | rcu_read_unlock(); |
504 | return rc; | 530 | return rc; |
505 | } | 531 | } |
506 | 532 | ||
@@ -516,6 +542,53 @@ struct sock *llc_lookup_established(struct llc_sap *sap, | |||
516 | return sk; | 542 | return sk; |
517 | } | 543 | } |
518 | 544 | ||
545 | static inline bool llc_listener_match(const struct llc_sap *sap, | ||
546 | const struct llc_addr *laddr, | ||
547 | const struct sock *sk) | ||
548 | { | ||
549 | struct llc_sock *llc = llc_sk(sk); | ||
550 | |||
551 | return sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN && | ||
552 | llc->laddr.lsap == laddr->lsap && | ||
553 | llc_mac_match(llc->laddr.mac, laddr->mac); | ||
554 | } | ||
555 | |||
556 | static struct sock *__llc_lookup_listener(struct llc_sap *sap, | ||
557 | struct llc_addr *laddr) | ||
558 | { | ||
559 | struct sock *rc; | ||
560 | struct hlist_nulls_node *node; | ||
561 | int slot = llc_sk_laddr_hashfn(sap, laddr); | ||
562 | struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot]; | ||
563 | |||
564 | rcu_read_lock(); | ||
565 | again: | ||
566 | sk_nulls_for_each_rcu(rc, node, laddr_hb) { | ||
567 | if (llc_listener_match(sap, laddr, rc)) { | ||
568 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ | ||
569 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) | ||
570 | goto again; | ||
571 | if (unlikely(llc_sk(rc)->sap != sap || | ||
572 | !llc_listener_match(sap, laddr, rc))) { | ||
573 | sock_put(rc); | ||
574 | continue; | ||
575 | } | ||
576 | goto found; | ||
577 | } | ||
578 | } | ||
579 | rc = NULL; | ||
580 | /* | ||
581 | * if the nulls value we got at the end of this lookup is | ||
582 | * not the expected one, we must restart lookup. | ||
583 | * We probably met an item that was moved to another chain. | ||
584 | */ | ||
585 | if (unlikely(get_nulls_value(node) != slot)) | ||
586 | goto again; | ||
587 | found: | ||
588 | rcu_read_unlock(); | ||
589 | return rc; | ||
590 | } | ||
591 | |||
519 | /** | 592 | /** |
520 | * llc_lookup_listener - Finds listener for local MAC + SAP | 593 | * llc_lookup_listener - Finds listener for local MAC + SAP |
521 | * @sap: SAP | 594 | * @sap: SAP |
@@ -529,24 +602,12 @@ struct sock *llc_lookup_established(struct llc_sap *sap, | |||
529 | static struct sock *llc_lookup_listener(struct llc_sap *sap, | 602 | static struct sock *llc_lookup_listener(struct llc_sap *sap, |
530 | struct llc_addr *laddr) | 603 | struct llc_addr *laddr) |
531 | { | 604 | { |
532 | struct sock *rc; | 605 | static struct llc_addr null_addr; |
533 | struct hlist_node *node; | 606 | struct sock *rc = __llc_lookup_listener(sap, laddr); |
534 | 607 | ||
535 | read_lock(&sap->sk_list.lock); | 608 | if (!rc) |
536 | sk_for_each(rc, node, &sap->sk_list.list) { | 609 | rc = __llc_lookup_listener(sap, &null_addr); |
537 | struct llc_sock *llc = llc_sk(rc); | ||
538 | 610 | ||
539 | if (rc->sk_type == SOCK_STREAM && rc->sk_state == TCP_LISTEN && | ||
540 | llc->laddr.lsap == laddr->lsap && | ||
541 | (llc_mac_match(llc->laddr.mac, laddr->mac) || | ||
542 | llc_mac_null(llc->laddr.mac))) { | ||
543 | sock_hold(rc); | ||
544 | goto found; | ||
545 | } | ||
546 | } | ||
547 | rc = NULL; | ||
548 | found: | ||
549 | read_unlock(&sap->sk_list.lock); | ||
550 | return rc; | 611 | return rc; |
551 | } | 612 | } |
552 | 613 | ||
@@ -647,15 +708,22 @@ static int llc_find_offset(int state, int ev_type) | |||
647 | * @sap: SAP | 708 | * @sap: SAP |
648 | * @sk: socket | 709 | * @sk: socket |
649 | * | 710 | * |
650 | * This function adds a socket to sk_list of a SAP. | 711 | * This function adds a socket to the hash tables of a SAP. |
651 | */ | 712 | */ |
652 | void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) | 713 | void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) |
653 | { | 714 | { |
715 | struct llc_sock *llc = llc_sk(sk); | ||
716 | struct hlist_head *dev_hb = llc_sk_dev_hash(sap, llc->dev->ifindex); | ||
717 | struct hlist_nulls_head *laddr_hb = llc_sk_laddr_hash(sap, &llc->laddr); | ||
718 | |||
654 | llc_sap_hold(sap); | 719 | llc_sap_hold(sap); |
655 | write_lock_bh(&sap->sk_list.lock); | ||
656 | llc_sk(sk)->sap = sap; | 720 | llc_sk(sk)->sap = sap; |
657 | sk_add_node(sk, &sap->sk_list.list); | 721 | |
658 | write_unlock_bh(&sap->sk_list.lock); | 722 | spin_lock_bh(&sap->sk_lock); |
723 | sap->sk_count++; | ||
724 | sk_nulls_add_node_rcu(sk, laddr_hb); | ||
725 | hlist_add_head(&llc->dev_hash_node, dev_hb); | ||
726 | spin_unlock_bh(&sap->sk_lock); | ||
659 | } | 727 | } |
660 | 728 | ||
661 | /** | 729 | /** |
@@ -663,14 +731,18 @@ void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) | |||
663 | * @sap: SAP | 731 | * @sap: SAP |
664 | * @sk: socket | 732 | * @sk: socket |
665 | * | 733 | * |
666 | * This function removes a connection from sk_list.list of a SAP if | 734 | * This function removes a connection from the hash tables of a SAP if |
667 | * the connection was in this list. | 735 | * the connection was in this list. |
668 | */ | 736 | */ |
669 | void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) | 737 | void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) |
670 | { | 738 | { |
671 | write_lock_bh(&sap->sk_list.lock); | 739 | struct llc_sock *llc = llc_sk(sk); |
672 | sk_del_node_init(sk); | 740 | |
673 | write_unlock_bh(&sap->sk_list.lock); | 741 | spin_lock_bh(&sap->sk_lock); |
742 | sk_nulls_del_node_init_rcu(sk); | ||
743 | hlist_del(&llc->dev_hash_node); | ||
744 | sap->sk_count--; | ||
745 | spin_unlock_bh(&sap->sk_lock); | ||
674 | llc_sap_put(sap); | 746 | llc_sap_put(sap); |
675 | } | 747 | } |
676 | 748 | ||
@@ -756,7 +828,8 @@ void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb) | |||
756 | else { | 828 | else { |
757 | dprintk("%s: adding to backlog...\n", __func__); | 829 | dprintk("%s: adding to backlog...\n", __func__); |
758 | llc_set_backlog_type(skb, LLC_PACKET); | 830 | llc_set_backlog_type(skb, LLC_PACKET); |
759 | sk_add_backlog(sk, skb); | 831 | if (sk_add_backlog(sk, skb)) |
832 | goto drop_unlock; | ||
760 | } | 833 | } |
761 | out: | 834 | out: |
762 | bh_unlock_sock(sk); | 835 | bh_unlock_sock(sk); |
diff --git a/net/llc/llc_core.c b/net/llc/llc_core.c index ff4c0ab96a69..78167e81dfeb 100644 --- a/net/llc/llc_core.c +++ b/net/llc/llc_core.c | |||
@@ -23,7 +23,7 @@ | |||
23 | #include <net/llc.h> | 23 | #include <net/llc.h> |
24 | 24 | ||
25 | LIST_HEAD(llc_sap_list); | 25 | LIST_HEAD(llc_sap_list); |
26 | DEFINE_RWLOCK(llc_sap_list_lock); | 26 | DEFINE_SPINLOCK(llc_sap_list_lock); |
27 | 27 | ||
28 | /** | 28 | /** |
29 | * llc_sap_alloc - allocates and initializes sap. | 29 | * llc_sap_alloc - allocates and initializes sap. |
@@ -33,40 +33,19 @@ DEFINE_RWLOCK(llc_sap_list_lock); | |||
33 | static struct llc_sap *llc_sap_alloc(void) | 33 | static struct llc_sap *llc_sap_alloc(void) |
34 | { | 34 | { |
35 | struct llc_sap *sap = kzalloc(sizeof(*sap), GFP_ATOMIC); | 35 | struct llc_sap *sap = kzalloc(sizeof(*sap), GFP_ATOMIC); |
36 | int i; | ||
36 | 37 | ||
37 | if (sap) { | 38 | if (sap) { |
38 | /* sap->laddr.mac - leave as a null, it's filled by bind */ | 39 | /* sap->laddr.mac - leave as a null, it's filled by bind */ |
39 | sap->state = LLC_SAP_STATE_ACTIVE; | 40 | sap->state = LLC_SAP_STATE_ACTIVE; |
40 | rwlock_init(&sap->sk_list.lock); | 41 | spin_lock_init(&sap->sk_lock); |
42 | for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++) | ||
43 | INIT_HLIST_NULLS_HEAD(&sap->sk_laddr_hash[i], i); | ||
41 | atomic_set(&sap->refcnt, 1); | 44 | atomic_set(&sap->refcnt, 1); |
42 | } | 45 | } |
43 | return sap; | 46 | return sap; |
44 | } | 47 | } |
45 | 48 | ||
46 | /** | ||
47 | * llc_add_sap - add sap to station list | ||
48 | * @sap: Address of the sap | ||
49 | * | ||
50 | * Adds a sap to the LLC's station sap list. | ||
51 | */ | ||
52 | static void llc_add_sap(struct llc_sap *sap) | ||
53 | { | ||
54 | list_add_tail(&sap->node, &llc_sap_list); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * llc_del_sap - del sap from station list | ||
59 | * @sap: Address of the sap | ||
60 | * | ||
61 | * Removes a sap to the LLC's station sap list. | ||
62 | */ | ||
63 | static void llc_del_sap(struct llc_sap *sap) | ||
64 | { | ||
65 | write_lock_bh(&llc_sap_list_lock); | ||
66 | list_del(&sap->node); | ||
67 | write_unlock_bh(&llc_sap_list_lock); | ||
68 | } | ||
69 | |||
70 | static struct llc_sap *__llc_sap_find(unsigned char sap_value) | 49 | static struct llc_sap *__llc_sap_find(unsigned char sap_value) |
71 | { | 50 | { |
72 | struct llc_sap* sap; | 51 | struct llc_sap* sap; |
@@ -90,13 +69,13 @@ out: | |||
90 | */ | 69 | */ |
91 | struct llc_sap *llc_sap_find(unsigned char sap_value) | 70 | struct llc_sap *llc_sap_find(unsigned char sap_value) |
92 | { | 71 | { |
93 | struct llc_sap* sap; | 72 | struct llc_sap *sap; |
94 | 73 | ||
95 | read_lock_bh(&llc_sap_list_lock); | 74 | rcu_read_lock_bh(); |
96 | sap = __llc_sap_find(sap_value); | 75 | sap = __llc_sap_find(sap_value); |
97 | if (sap) | 76 | if (sap) |
98 | llc_sap_hold(sap); | 77 | llc_sap_hold(sap); |
99 | read_unlock_bh(&llc_sap_list_lock); | 78 | rcu_read_unlock_bh(); |
100 | return sap; | 79 | return sap; |
101 | } | 80 | } |
102 | 81 | ||
@@ -117,7 +96,7 @@ struct llc_sap *llc_sap_open(unsigned char lsap, | |||
117 | { | 96 | { |
118 | struct llc_sap *sap = NULL; | 97 | struct llc_sap *sap = NULL; |
119 | 98 | ||
120 | write_lock_bh(&llc_sap_list_lock); | 99 | spin_lock_bh(&llc_sap_list_lock); |
121 | if (__llc_sap_find(lsap)) /* SAP already exists */ | 100 | if (__llc_sap_find(lsap)) /* SAP already exists */ |
122 | goto out; | 101 | goto out; |
123 | sap = llc_sap_alloc(); | 102 | sap = llc_sap_alloc(); |
@@ -125,9 +104,9 @@ struct llc_sap *llc_sap_open(unsigned char lsap, | |||
125 | goto out; | 104 | goto out; |
126 | sap->laddr.lsap = lsap; | 105 | sap->laddr.lsap = lsap; |
127 | sap->rcv_func = func; | 106 | sap->rcv_func = func; |
128 | llc_add_sap(sap); | 107 | list_add_tail_rcu(&sap->node, &llc_sap_list); |
129 | out: | 108 | out: |
130 | write_unlock_bh(&llc_sap_list_lock); | 109 | spin_unlock_bh(&llc_sap_list_lock); |
131 | return sap; | 110 | return sap; |
132 | } | 111 | } |
133 | 112 | ||
@@ -142,8 +121,14 @@ out: | |||
142 | */ | 121 | */ |
143 | void llc_sap_close(struct llc_sap *sap) | 122 | void llc_sap_close(struct llc_sap *sap) |
144 | { | 123 | { |
145 | WARN_ON(!hlist_empty(&sap->sk_list.list)); | 124 | WARN_ON(sap->sk_count); |
146 | llc_del_sap(sap); | 125 | |
126 | spin_lock_bh(&llc_sap_list_lock); | ||
127 | list_del_rcu(&sap->node); | ||
128 | spin_unlock_bh(&llc_sap_list_lock); | ||
129 | |||
130 | synchronize_rcu(); | ||
131 | |||
147 | kfree(sap); | 132 | kfree(sap); |
148 | } | 133 | } |
149 | 134 | ||
diff --git a/net/llc/llc_if.c b/net/llc/llc_if.c index a89917130a7b..25c31c0a3fdb 100644 --- a/net/llc/llc_if.c +++ b/net/llc/llc_if.c | |||
@@ -11,6 +11,7 @@ | |||
11 | * | 11 | * |
12 | * See the GNU General Public License for more details. | 12 | * See the GNU General Public License for more details. |
13 | */ | 13 | */ |
14 | #include <linux/gfp.h> | ||
14 | #include <linux/module.h> | 15 | #include <linux/module.h> |
15 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
16 | #include <linux/netdevice.h> | 17 | #include <linux/netdevice.h> |
diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c index 57ad974e4d94..f99687439139 100644 --- a/net/llc/llc_input.c +++ b/net/llc/llc_input.c | |||
@@ -12,6 +12,7 @@ | |||
12 | * See the GNU General Public License for more details. | 12 | * See the GNU General Public License for more details. |
13 | */ | 13 | */ |
14 | #include <linux/netdevice.h> | 14 | #include <linux/netdevice.h> |
15 | #include <linux/slab.h> | ||
15 | #include <net/net_namespace.h> | 16 | #include <net/net_namespace.h> |
16 | #include <net/llc.h> | 17 | #include <net/llc.h> |
17 | #include <net/llc_pdu.h> | 18 | #include <net/llc_pdu.h> |
diff --git a/net/llc/llc_output.c b/net/llc/llc_output.c index 754f4fedc852..b38a1079a98e 100644 --- a/net/llc/llc_output.c +++ b/net/llc/llc_output.c | |||
@@ -33,48 +33,19 @@ | |||
33 | int llc_mac_hdr_init(struct sk_buff *skb, | 33 | int llc_mac_hdr_init(struct sk_buff *skb, |
34 | const unsigned char *sa, const unsigned char *da) | 34 | const unsigned char *sa, const unsigned char *da) |
35 | { | 35 | { |
36 | int rc = 0; | 36 | int rc = -EINVAL; |
37 | 37 | ||
38 | switch (skb->dev->type) { | 38 | switch (skb->dev->type) { |
39 | #ifdef CONFIG_TR | 39 | case ARPHRD_IEEE802_TR: |
40 | case ARPHRD_IEEE802_TR: { | ||
41 | struct net_device *dev = skb->dev; | ||
42 | struct trh_hdr *trh; | ||
43 | |||
44 | skb_push(skb, sizeof(*trh)); | ||
45 | skb_reset_mac_header(skb); | ||
46 | trh = tr_hdr(skb); | ||
47 | trh->ac = AC; | ||
48 | trh->fc = LLC_FRAME; | ||
49 | if (sa) | ||
50 | memcpy(trh->saddr, sa, dev->addr_len); | ||
51 | else | ||
52 | memset(trh->saddr, 0, dev->addr_len); | ||
53 | if (da) { | ||
54 | memcpy(trh->daddr, da, dev->addr_len); | ||
55 | tr_source_route(skb, trh, dev); | ||
56 | skb_reset_mac_header(skb); | ||
57 | } | ||
58 | break; | ||
59 | } | ||
60 | #endif | ||
61 | case ARPHRD_ETHER: | 40 | case ARPHRD_ETHER: |
62 | case ARPHRD_LOOPBACK: { | 41 | case ARPHRD_LOOPBACK: |
63 | unsigned short len = skb->len; | 42 | rc = dev_hard_header(skb, skb->dev, ETH_P_802_2, da, sa, |
64 | struct ethhdr *eth; | 43 | skb->len); |
65 | 44 | if (rc > 0) | |
66 | skb_push(skb, sizeof(*eth)); | 45 | rc = 0; |
67 | skb_reset_mac_header(skb); | ||
68 | eth = eth_hdr(skb); | ||
69 | eth->h_proto = htons(len); | ||
70 | memcpy(eth->h_dest, da, ETH_ALEN); | ||
71 | memcpy(eth->h_source, sa, ETH_ALEN); | ||
72 | break; | 46 | break; |
73 | } | ||
74 | default: | 47 | default: |
75 | printk(KERN_WARNING "device type not supported: %d\n", | 48 | WARN(1, "device type not supported: %d\n", skb->dev->type); |
76 | skb->dev->type); | ||
77 | rc = -EINVAL; | ||
78 | } | 49 | } |
79 | return rc; | 50 | return rc; |
80 | } | 51 | } |
diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index be47ac427f6b..7af1ff2d1f19 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c | |||
@@ -32,21 +32,23 @@ static void llc_ui_format_mac(struct seq_file *seq, u8 *addr) | |||
32 | 32 | ||
33 | static struct sock *llc_get_sk_idx(loff_t pos) | 33 | static struct sock *llc_get_sk_idx(loff_t pos) |
34 | { | 34 | { |
35 | struct list_head *sap_entry; | ||
36 | struct llc_sap *sap; | 35 | struct llc_sap *sap; |
37 | struct hlist_node *node; | ||
38 | struct sock *sk = NULL; | 36 | struct sock *sk = NULL; |
39 | 37 | int i; | |
40 | list_for_each(sap_entry, &llc_sap_list) { | 38 | |
41 | sap = list_entry(sap_entry, struct llc_sap, node); | 39 | list_for_each_entry_rcu(sap, &llc_sap_list, node) { |
42 | 40 | spin_lock_bh(&sap->sk_lock); | |
43 | read_lock_bh(&sap->sk_list.lock); | 41 | for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++) { |
44 | sk_for_each(sk, node, &sap->sk_list.list) { | 42 | struct hlist_nulls_head *head = &sap->sk_laddr_hash[i]; |
45 | if (!pos) | 43 | struct hlist_nulls_node *node; |
46 | goto found; | 44 | |
47 | --pos; | 45 | sk_nulls_for_each(sk, node, head) { |
46 | if (!pos) | ||
47 | goto found; /* keep the lock */ | ||
48 | --pos; | ||
49 | } | ||
48 | } | 50 | } |
49 | read_unlock_bh(&sap->sk_list.lock); | 51 | spin_unlock_bh(&sap->sk_lock); |
50 | } | 52 | } |
51 | sk = NULL; | 53 | sk = NULL; |
52 | found: | 54 | found: |
@@ -57,10 +59,23 @@ static void *llc_seq_start(struct seq_file *seq, loff_t *pos) | |||
57 | { | 59 | { |
58 | loff_t l = *pos; | 60 | loff_t l = *pos; |
59 | 61 | ||
60 | read_lock_bh(&llc_sap_list_lock); | 62 | rcu_read_lock_bh(); |
61 | return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN; | 63 | return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN; |
62 | } | 64 | } |
63 | 65 | ||
66 | static struct sock *laddr_hash_next(struct llc_sap *sap, int bucket) | ||
67 | { | ||
68 | struct hlist_nulls_node *node; | ||
69 | struct sock *sk = NULL; | ||
70 | |||
71 | while (++bucket < LLC_SK_LADDR_HASH_ENTRIES) | ||
72 | sk_nulls_for_each(sk, node, &sap->sk_laddr_hash[bucket]) | ||
73 | goto out; | ||
74 | |||
75 | out: | ||
76 | return sk; | ||
77 | } | ||
78 | |||
64 | static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 79 | static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
65 | { | 80 | { |
66 | struct sock* sk, *next; | 81 | struct sock* sk, *next; |
@@ -73,25 +88,23 @@ static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |||
73 | goto out; | 88 | goto out; |
74 | } | 89 | } |
75 | sk = v; | 90 | sk = v; |
76 | next = sk_next(sk); | 91 | next = sk_nulls_next(sk); |
77 | if (next) { | 92 | if (next) { |
78 | sk = next; | 93 | sk = next; |
79 | goto out; | 94 | goto out; |
80 | } | 95 | } |
81 | llc = llc_sk(sk); | 96 | llc = llc_sk(sk); |
82 | sap = llc->sap; | 97 | sap = llc->sap; |
83 | read_unlock_bh(&sap->sk_list.lock); | 98 | sk = laddr_hash_next(sap, llc_sk_laddr_hashfn(sap, &llc->laddr)); |
84 | sk = NULL; | 99 | if (sk) |
85 | for (;;) { | 100 | goto out; |
86 | if (sap->node.next == &llc_sap_list) | 101 | spin_unlock_bh(&sap->sk_lock); |
87 | break; | 102 | list_for_each_entry_continue_rcu(sap, &llc_sap_list, node) { |
88 | sap = list_entry(sap->node.next, struct llc_sap, node); | 103 | spin_lock_bh(&sap->sk_lock); |
89 | read_lock_bh(&sap->sk_list.lock); | 104 | sk = laddr_hash_next(sap, -1); |
90 | if (!hlist_empty(&sap->sk_list.list)) { | 105 | if (sk) |
91 | sk = sk_head(&sap->sk_list.list); | 106 | break; /* keep the lock */ |
92 | break; | 107 | spin_unlock_bh(&sap->sk_lock); |
93 | } | ||
94 | read_unlock_bh(&sap->sk_list.lock); | ||
95 | } | 108 | } |
96 | out: | 109 | out: |
97 | return sk; | 110 | return sk; |
@@ -104,9 +117,9 @@ static void llc_seq_stop(struct seq_file *seq, void *v) | |||
104 | struct llc_sock *llc = llc_sk(sk); | 117 | struct llc_sock *llc = llc_sk(sk); |
105 | struct llc_sap *sap = llc->sap; | 118 | struct llc_sap *sap = llc->sap; |
106 | 119 | ||
107 | read_unlock_bh(&sap->sk_list.lock); | 120 | spin_unlock_bh(&sap->sk_lock); |
108 | } | 121 | } |
109 | read_unlock_bh(&llc_sap_list_lock); | 122 | rcu_read_unlock_bh(); |
110 | } | 123 | } |
111 | 124 | ||
112 | static int llc_seq_socket_show(struct seq_file *seq, void *v) | 125 | static int llc_seq_socket_show(struct seq_file *seq, void *v) |
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c index 008de1fc42ca..94e7fca75b85 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <net/sock.h> | 23 | #include <net/sock.h> |
24 | #include <net/tcp_states.h> | 24 | #include <net/tcp_states.h> |
25 | #include <linux/llc.h> | 25 | #include <linux/llc.h> |
26 | #include <linux/slab.h> | ||
26 | 27 | ||
27 | static int llc_mac_header_len(unsigned short devtype) | 28 | static int llc_mac_header_len(unsigned short devtype) |
28 | { | 29 | { |
@@ -30,7 +31,7 @@ static int llc_mac_header_len(unsigned short devtype) | |||
30 | case ARPHRD_ETHER: | 31 | case ARPHRD_ETHER: |
31 | case ARPHRD_LOOPBACK: | 32 | case ARPHRD_LOOPBACK: |
32 | return sizeof(struct ethhdr); | 33 | return sizeof(struct ethhdr); |
33 | #ifdef CONFIG_TR | 34 | #if defined(CONFIG_TR) || defined(CONFIG_TR_MODULE) |
34 | case ARPHRD_IEEE802_TR: | 35 | case ARPHRD_IEEE802_TR: |
35 | return sizeof(struct trh_hdr); | 36 | return sizeof(struct trh_hdr); |
36 | #endif | 37 | #endif |
@@ -297,6 +298,17 @@ static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb, | |||
297 | llc_sap_state_process(sap, skb); | 298 | llc_sap_state_process(sap, skb); |
298 | } | 299 | } |
299 | 300 | ||
301 | static inline bool llc_dgram_match(const struct llc_sap *sap, | ||
302 | const struct llc_addr *laddr, | ||
303 | const struct sock *sk) | ||
304 | { | ||
305 | struct llc_sock *llc = llc_sk(sk); | ||
306 | |||
307 | return sk->sk_type == SOCK_DGRAM && | ||
308 | llc->laddr.lsap == laddr->lsap && | ||
309 | llc_mac_match(llc->laddr.mac, laddr->mac); | ||
310 | } | ||
311 | |||
300 | /** | 312 | /** |
301 | * llc_lookup_dgram - Finds dgram socket for the local sap/mac | 313 | * llc_lookup_dgram - Finds dgram socket for the local sap/mac |
302 | * @sap: SAP | 314 | * @sap: SAP |
@@ -309,25 +321,68 @@ static struct sock *llc_lookup_dgram(struct llc_sap *sap, | |||
309 | const struct llc_addr *laddr) | 321 | const struct llc_addr *laddr) |
310 | { | 322 | { |
311 | struct sock *rc; | 323 | struct sock *rc; |
312 | struct hlist_node *node; | 324 | struct hlist_nulls_node *node; |
313 | 325 | int slot = llc_sk_laddr_hashfn(sap, laddr); | |
314 | read_lock_bh(&sap->sk_list.lock); | 326 | struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot]; |
315 | sk_for_each(rc, node, &sap->sk_list.list) { | 327 | |
316 | struct llc_sock *llc = llc_sk(rc); | 328 | rcu_read_lock_bh(); |
317 | 329 | again: | |
318 | if (rc->sk_type == SOCK_DGRAM && | 330 | sk_nulls_for_each_rcu(rc, node, laddr_hb) { |
319 | llc->laddr.lsap == laddr->lsap && | 331 | if (llc_dgram_match(sap, laddr, rc)) { |
320 | llc_mac_match(llc->laddr.mac, laddr->mac)) { | 332 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ |
321 | sock_hold(rc); | 333 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) |
334 | goto again; | ||
335 | if (unlikely(llc_sk(rc)->sap != sap || | ||
336 | !llc_dgram_match(sap, laddr, rc))) { | ||
337 | sock_put(rc); | ||
338 | continue; | ||
339 | } | ||
322 | goto found; | 340 | goto found; |
323 | } | 341 | } |
324 | } | 342 | } |
325 | rc = NULL; | 343 | rc = NULL; |
344 | /* | ||
345 | * if the nulls value we got at the end of this lookup is | ||
346 | * not the expected one, we must restart lookup. | ||
347 | * We probably met an item that was moved to another chain. | ||
348 | */ | ||
349 | if (unlikely(get_nulls_value(node) != slot)) | ||
350 | goto again; | ||
326 | found: | 351 | found: |
327 | read_unlock_bh(&sap->sk_list.lock); | 352 | rcu_read_unlock_bh(); |
328 | return rc; | 353 | return rc; |
329 | } | 354 | } |
330 | 355 | ||
356 | static inline bool llc_mcast_match(const struct llc_sap *sap, | ||
357 | const struct llc_addr *laddr, | ||
358 | const struct sk_buff *skb, | ||
359 | const struct sock *sk) | ||
360 | { | ||
361 | struct llc_sock *llc = llc_sk(sk); | ||
362 | |||
363 | return sk->sk_type == SOCK_DGRAM && | ||
364 | llc->laddr.lsap == laddr->lsap && | ||
365 | llc->dev == skb->dev; | ||
366 | } | ||
367 | |||
368 | static void llc_do_mcast(struct llc_sap *sap, struct sk_buff *skb, | ||
369 | struct sock **stack, int count) | ||
370 | { | ||
371 | struct sk_buff *skb1; | ||
372 | int i; | ||
373 | |||
374 | for (i = 0; i < count; i++) { | ||
375 | skb1 = skb_clone(skb, GFP_ATOMIC); | ||
376 | if (!skb1) { | ||
377 | sock_put(stack[i]); | ||
378 | continue; | ||
379 | } | ||
380 | |||
381 | llc_sap_rcv(sap, skb1, stack[i]); | ||
382 | sock_put(stack[i]); | ||
383 | } | ||
384 | } | ||
385 | |||
331 | /** | 386 | /** |
332 | * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets. | 387 | * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets. |
333 | * @sap: SAP | 388 | * @sap: SAP |
@@ -340,32 +395,31 @@ static void llc_sap_mcast(struct llc_sap *sap, | |||
340 | const struct llc_addr *laddr, | 395 | const struct llc_addr *laddr, |
341 | struct sk_buff *skb) | 396 | struct sk_buff *skb) |
342 | { | 397 | { |
343 | struct sock *sk; | 398 | int i = 0, count = 256 / sizeof(struct sock *); |
399 | struct sock *sk, *stack[count]; | ||
344 | struct hlist_node *node; | 400 | struct hlist_node *node; |
401 | struct llc_sock *llc; | ||
402 | struct hlist_head *dev_hb = llc_sk_dev_hash(sap, skb->dev->ifindex); | ||
345 | 403 | ||
346 | read_lock_bh(&sap->sk_list.lock); | 404 | spin_lock_bh(&sap->sk_lock); |
347 | sk_for_each(sk, node, &sap->sk_list.list) { | 405 | hlist_for_each_entry(llc, node, dev_hb, dev_hash_node) { |
348 | struct llc_sock *llc = llc_sk(sk); | ||
349 | struct sk_buff *skb1; | ||
350 | 406 | ||
351 | if (sk->sk_type != SOCK_DGRAM) | 407 | sk = &llc->sk; |
352 | continue; | ||
353 | 408 | ||
354 | if (llc->laddr.lsap != laddr->lsap) | 409 | if (!llc_mcast_match(sap, laddr, skb, sk)) |
355 | continue; | 410 | continue; |
356 | 411 | ||
357 | if (llc->dev != skb->dev) | ||
358 | continue; | ||
359 | |||
360 | skb1 = skb_clone(skb, GFP_ATOMIC); | ||
361 | if (!skb1) | ||
362 | break; | ||
363 | |||
364 | sock_hold(sk); | 412 | sock_hold(sk); |
365 | llc_sap_rcv(sap, skb1, sk); | 413 | if (i < count) |
366 | sock_put(sk); | 414 | stack[i++] = sk; |
415 | else { | ||
416 | llc_do_mcast(sap, skb, stack, i); | ||
417 | i = 0; | ||
418 | } | ||
367 | } | 419 | } |
368 | read_unlock_bh(&sap->sk_list.lock); | 420 | spin_unlock_bh(&sap->sk_lock); |
421 | |||
422 | llc_do_mcast(sap, skb, stack, i); | ||
369 | } | 423 | } |
370 | 424 | ||
371 | 425 | ||
diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c index 83da13339490..e4dae0244d76 100644 --- a/net/llc/llc_station.c +++ b/net/llc/llc_station.c | |||
@@ -13,6 +13,7 @@ | |||
13 | */ | 13 | */ |
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/slab.h> | ||
16 | #include <net/llc.h> | 17 | #include <net/llc.h> |
17 | #include <net/llc_sap.h> | 18 | #include <net/llc_sap.h> |
18 | #include <net/llc_conn.h> | 19 | #include <net/llc_conn.h> |
diff --git a/net/llc/sysctl_net_llc.c b/net/llc/sysctl_net_llc.c index 57b9304d444c..e2ebe3586263 100644 --- a/net/llc/sysctl_net_llc.c +++ b/net/llc/sysctl_net_llc.c | |||
@@ -15,86 +15,73 @@ | |||
15 | 15 | ||
16 | static struct ctl_table llc2_timeout_table[] = { | 16 | static struct ctl_table llc2_timeout_table[] = { |
17 | { | 17 | { |
18 | .ctl_name = NET_LLC2_ACK_TIMEOUT, | ||
19 | .procname = "ack", | 18 | .procname = "ack", |
20 | .data = &sysctl_llc2_ack_timeout, | 19 | .data = &sysctl_llc2_ack_timeout, |
21 | .maxlen = sizeof(long), | 20 | .maxlen = sizeof(long), |
22 | .mode = 0644, | 21 | .mode = 0644, |
23 | .proc_handler = proc_dointvec_jiffies, | 22 | .proc_handler = proc_dointvec_jiffies, |
24 | .strategy = sysctl_jiffies, | ||
25 | }, | 23 | }, |
26 | { | 24 | { |
27 | .ctl_name = NET_LLC2_BUSY_TIMEOUT, | ||
28 | .procname = "busy", | 25 | .procname = "busy", |
29 | .data = &sysctl_llc2_busy_timeout, | 26 | .data = &sysctl_llc2_busy_timeout, |
30 | .maxlen = sizeof(long), | 27 | .maxlen = sizeof(long), |
31 | .mode = 0644, | 28 | .mode = 0644, |
32 | .proc_handler = proc_dointvec_jiffies, | 29 | .proc_handler = proc_dointvec_jiffies, |
33 | .strategy = sysctl_jiffies, | ||
34 | }, | 30 | }, |
35 | { | 31 | { |
36 | .ctl_name = NET_LLC2_P_TIMEOUT, | ||
37 | .procname = "p", | 32 | .procname = "p", |
38 | .data = &sysctl_llc2_p_timeout, | 33 | .data = &sysctl_llc2_p_timeout, |
39 | .maxlen = sizeof(long), | 34 | .maxlen = sizeof(long), |
40 | .mode = 0644, | 35 | .mode = 0644, |
41 | .proc_handler = proc_dointvec_jiffies, | 36 | .proc_handler = proc_dointvec_jiffies, |
42 | .strategy = sysctl_jiffies, | ||
43 | }, | 37 | }, |
44 | { | 38 | { |
45 | .ctl_name = NET_LLC2_REJ_TIMEOUT, | ||
46 | .procname = "rej", | 39 | .procname = "rej", |
47 | .data = &sysctl_llc2_rej_timeout, | 40 | .data = &sysctl_llc2_rej_timeout, |
48 | .maxlen = sizeof(long), | 41 | .maxlen = sizeof(long), |
49 | .mode = 0644, | 42 | .mode = 0644, |
50 | .proc_handler = proc_dointvec_jiffies, | 43 | .proc_handler = proc_dointvec_jiffies, |
51 | .strategy = sysctl_jiffies, | ||
52 | }, | 44 | }, |
53 | { 0 }, | 45 | { }, |
54 | }; | 46 | }; |
55 | 47 | ||
56 | static struct ctl_table llc_station_table[] = { | 48 | static struct ctl_table llc_station_table[] = { |
57 | { | 49 | { |
58 | .ctl_name = NET_LLC_STATION_ACK_TIMEOUT, | ||
59 | .procname = "ack_timeout", | 50 | .procname = "ack_timeout", |
60 | .data = &sysctl_llc_station_ack_timeout, | 51 | .data = &sysctl_llc_station_ack_timeout, |
61 | .maxlen = sizeof(long), | 52 | .maxlen = sizeof(long), |
62 | .mode = 0644, | 53 | .mode = 0644, |
63 | .proc_handler = proc_dointvec_jiffies, | 54 | .proc_handler = proc_dointvec_jiffies, |
64 | .strategy = sysctl_jiffies, | ||
65 | }, | 55 | }, |
66 | { 0 }, | 56 | { }, |
67 | }; | 57 | }; |
68 | 58 | ||
69 | static struct ctl_table llc2_dir_timeout_table[] = { | 59 | static struct ctl_table llc2_dir_timeout_table[] = { |
70 | { | 60 | { |
71 | .ctl_name = NET_LLC2, | ||
72 | .procname = "timeout", | 61 | .procname = "timeout", |
73 | .mode = 0555, | 62 | .mode = 0555, |
74 | .child = llc2_timeout_table, | 63 | .child = llc2_timeout_table, |
75 | }, | 64 | }, |
76 | { 0 }, | 65 | { }, |
77 | }; | 66 | }; |
78 | 67 | ||
79 | static struct ctl_table llc_table[] = { | 68 | static struct ctl_table llc_table[] = { |
80 | { | 69 | { |
81 | .ctl_name = NET_LLC2, | ||
82 | .procname = "llc2", | 70 | .procname = "llc2", |
83 | .mode = 0555, | 71 | .mode = 0555, |
84 | .child = llc2_dir_timeout_table, | 72 | .child = llc2_dir_timeout_table, |
85 | }, | 73 | }, |
86 | { | 74 | { |
87 | .ctl_name = NET_LLC_STATION, | ||
88 | .procname = "station", | 75 | .procname = "station", |
89 | .mode = 0555, | 76 | .mode = 0555, |
90 | .child = llc_station_table, | 77 | .child = llc_station_table, |
91 | }, | 78 | }, |
92 | { 0 }, | 79 | { }, |
93 | }; | 80 | }; |
94 | 81 | ||
95 | static struct ctl_path llc_path[] = { | 82 | static struct ctl_path llc_path[] = { |
96 | { .procname = "net", .ctl_name = CTL_NET, }, | 83 | { .procname = "net", }, |
97 | { .procname = "llc", .ctl_name = NET_LLC, }, | 84 | { .procname = "llc", }, |
98 | { } | 85 | { } |
99 | }; | 86 | }; |
100 | 87 | ||