diff options
Diffstat (limited to 'net/llc')
-rw-r--r-- | net/llc/af_llc.c | 64 | ||||
-rw-r--r-- | net/llc/llc_conn.c | 143 | ||||
-rw-r--r-- | net/llc/llc_core.c | 53 | ||||
-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 | 111 |
6 files changed, 315 insertions, 170 deletions
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 3a66546cad06..e35d907fba2c 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c | |||
@@ -47,6 +47,10 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout); | |||
47 | #define dprintk(args...) | 47 | #define dprintk(args...) |
48 | #endif | 48 | #endif |
49 | 49 | ||
50 | /* Maybe we'll add some more in the future. */ | ||
51 | #define LLC_CMSG_PKTINFO 1 | ||
52 | |||
53 | |||
50 | /** | 54 | /** |
51 | * llc_ui_next_link_no - return the next unused link number for a sap | 55 | * llc_ui_next_link_no - return the next unused link number for a sap |
52 | * @sap: Address of sap to get link number from. | 56 | * @sap: Address of sap to get link number from. |
@@ -136,6 +140,7 @@ static struct proto llc_proto = { | |||
136 | .name = "LLC", | 140 | .name = "LLC", |
137 | .owner = THIS_MODULE, | 141 | .owner = THIS_MODULE, |
138 | .obj_size = sizeof(struct llc_sock), | 142 | .obj_size = sizeof(struct llc_sock), |
143 | .slab_flags = SLAB_DESTROY_BY_RCU, | ||
139 | }; | 144 | }; |
140 | 145 | ||
141 | /** | 146 | /** |
@@ -192,10 +197,8 @@ static int llc_ui_release(struct socket *sock) | |||
192 | llc->laddr.lsap, llc->daddr.lsap); | 197 | llc->laddr.lsap, llc->daddr.lsap); |
193 | if (!llc_send_disc(sk)) | 198 | if (!llc_send_disc(sk)) |
194 | llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo); | 199 | llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo); |
195 | if (!sock_flag(sk, SOCK_ZAPPED)) { | 200 | if (!sock_flag(sk, SOCK_ZAPPED)) |
196 | llc_sap_put(llc->sap); | ||
197 | llc_sap_remove_socket(llc->sap, sk); | 201 | llc_sap_remove_socket(llc->sap, sk); |
198 | } | ||
199 | release_sock(sk); | 202 | release_sock(sk); |
200 | if (llc->dev) | 203 | if (llc->dev) |
201 | dev_put(llc->dev); | 204 | dev_put(llc->dev); |
@@ -255,7 +258,14 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr) | |||
255 | if (!sock_flag(sk, SOCK_ZAPPED)) | 258 | if (!sock_flag(sk, SOCK_ZAPPED)) |
256 | goto out; | 259 | goto out; |
257 | rc = -ENODEV; | 260 | rc = -ENODEV; |
258 | llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd); | 261 | if (sk->sk_bound_dev_if) { |
262 | llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if); | ||
263 | if (llc->dev && addr->sllc_arphrd != llc->dev->type) { | ||
264 | dev_put(llc->dev); | ||
265 | llc->dev = NULL; | ||
266 | } | ||
267 | } else | ||
268 | llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd); | ||
259 | if (!llc->dev) | 269 | if (!llc->dev) |
260 | goto out; | 270 | goto out; |
261 | rc = -EUSERS; | 271 | rc = -EUSERS; |
@@ -306,7 +316,25 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) | |||
306 | goto out; | 316 | goto out; |
307 | rc = -ENODEV; | 317 | rc = -ENODEV; |
308 | rtnl_lock(); | 318 | rtnl_lock(); |
309 | llc->dev = dev_getbyhwaddr(&init_net, addr->sllc_arphrd, addr->sllc_mac); | 319 | if (sk->sk_bound_dev_if) { |
320 | llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if); | ||
321 | if (llc->dev) { | ||
322 | if (!addr->sllc_arphrd) | ||
323 | addr->sllc_arphrd = llc->dev->type; | ||
324 | if (llc_mac_null(addr->sllc_mac)) | ||
325 | memcpy(addr->sllc_mac, llc->dev->dev_addr, | ||
326 | IFHWADDRLEN); | ||
327 | if (addr->sllc_arphrd != llc->dev->type || | ||
328 | !llc_mac_match(addr->sllc_mac, | ||
329 | llc->dev->dev_addr)) { | ||
330 | rc = -EINVAL; | ||
331 | dev_put(llc->dev); | ||
332 | llc->dev = NULL; | ||
333 | } | ||
334 | } | ||
335 | } else | ||
336 | llc->dev = dev_getbyhwaddr(&init_net, addr->sllc_arphrd, | ||
337 | addr->sllc_mac); | ||
310 | rtnl_unlock(); | 338 | rtnl_unlock(); |
311 | if (!llc->dev) | 339 | if (!llc->dev) |
312 | goto out; | 340 | goto out; |
@@ -322,7 +350,6 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) | |||
322 | rc = -EBUSY; /* some other network layer is using the sap */ | 350 | rc = -EBUSY; /* some other network layer is using the sap */ |
323 | if (!sap) | 351 | if (!sap) |
324 | goto out; | 352 | goto out; |
325 | llc_sap_hold(sap); | ||
326 | } else { | 353 | } else { |
327 | struct llc_addr laddr, daddr; | 354 | struct llc_addr laddr, daddr; |
328 | struct sock *ask; | 355 | struct sock *ask; |
@@ -591,6 +618,20 @@ static int llc_wait_data(struct sock *sk, long timeo) | |||
591 | return rc; | 618 | return rc; |
592 | } | 619 | } |
593 | 620 | ||
621 | static void llc_cmsg_rcv(struct msghdr *msg, struct sk_buff *skb) | ||
622 | { | ||
623 | struct llc_sock *llc = llc_sk(skb->sk); | ||
624 | |||
625 | if (llc->cmsg_flags & LLC_CMSG_PKTINFO) { | ||
626 | struct llc_pktinfo info; | ||
627 | |||
628 | info.lpi_ifindex = llc_sk(skb->sk)->dev->ifindex; | ||
629 | llc_pdu_decode_dsap(skb, &info.lpi_sap); | ||
630 | llc_pdu_decode_da(skb, info.lpi_mac); | ||
631 | put_cmsg(msg, SOL_LLC, LLC_OPT_PKTINFO, sizeof(info), &info); | ||
632 | } | ||
633 | } | ||
634 | |||
594 | /** | 635 | /** |
595 | * llc_ui_accept - accept a new incoming connection. | 636 | * llc_ui_accept - accept a new incoming connection. |
596 | * @sock: Socket which connections arrive on. | 637 | * @sock: Socket which connections arrive on. |
@@ -812,6 +853,8 @@ copy_uaddr: | |||
812 | memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); | 853 | memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); |
813 | msg->msg_namelen = sizeof(*uaddr); | 854 | msg->msg_namelen = sizeof(*uaddr); |
814 | } | 855 | } |
856 | if (llc_sk(sk)->cmsg_flags) | ||
857 | llc_cmsg_rcv(msg, skb); | ||
815 | goto out; | 858 | goto out; |
816 | } | 859 | } |
817 | 860 | ||
@@ -1030,6 +1073,12 @@ static int llc_ui_setsockopt(struct socket *sock, int level, int optname, | |||
1030 | goto out; | 1073 | goto out; |
1031 | llc->rw = opt; | 1074 | llc->rw = opt; |
1032 | break; | 1075 | break; |
1076 | case LLC_OPT_PKTINFO: | ||
1077 | if (opt) | ||
1078 | llc->cmsg_flags |= LLC_CMSG_PKTINFO; | ||
1079 | else | ||
1080 | llc->cmsg_flags &= ~LLC_CMSG_PKTINFO; | ||
1081 | break; | ||
1033 | default: | 1082 | default: |
1034 | rc = -ENOPROTOOPT; | 1083 | rc = -ENOPROTOOPT; |
1035 | goto out; | 1084 | goto out; |
@@ -1083,6 +1132,9 @@ static int llc_ui_getsockopt(struct socket *sock, int level, int optname, | |||
1083 | val = llc->k; break; | 1132 | val = llc->k; break; |
1084 | case LLC_OPT_RX_WIN: | 1133 | case LLC_OPT_RX_WIN: |
1085 | val = llc->rw; break; | 1134 | val = llc->rw; break; |
1135 | case LLC_OPT_PKTINFO: | ||
1136 | val = (llc->cmsg_flags & LLC_CMSG_PKTINFO) != 0; | ||
1137 | break; | ||
1086 | default: | 1138 | default: |
1087 | rc = -ENOPROTOOPT; | 1139 | rc = -ENOPROTOOPT; |
1088 | goto out; | 1140 | goto out; |
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index c6bab39b018e..a8dde9b010da 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c | |||
@@ -468,6 +468,19 @@ static int llc_exec_conn_trans_actions(struct sock *sk, | |||
468 | return rc; | 468 | return rc; |
469 | } | 469 | } |
470 | 470 | ||
471 | static inline bool llc_estab_match(const struct llc_sap *sap, | ||
472 | const struct llc_addr *daddr, | ||
473 | const struct llc_addr *laddr, | ||
474 | const struct sock *sk) | ||
475 | { | ||
476 | struct llc_sock *llc = llc_sk(sk); | ||
477 | |||
478 | return llc->laddr.lsap == laddr->lsap && | ||
479 | llc->daddr.lsap == daddr->lsap && | ||
480 | llc_mac_match(llc->laddr.mac, laddr->mac) && | ||
481 | llc_mac_match(llc->daddr.mac, daddr->mac); | ||
482 | } | ||
483 | |||
471 | /** | 484 | /** |
472 | * __llc_lookup_established - Finds connection for the remote/local sap/mac | 485 | * __llc_lookup_established - Finds connection for the remote/local sap/mac |
473 | * @sap: SAP | 486 | * @sap: SAP |
@@ -484,23 +497,35 @@ static struct sock *__llc_lookup_established(struct llc_sap *sap, | |||
484 | struct llc_addr *laddr) | 497 | struct llc_addr *laddr) |
485 | { | 498 | { |
486 | struct sock *rc; | 499 | struct sock *rc; |
487 | struct hlist_node *node; | 500 | struct hlist_nulls_node *node; |
488 | 501 | int slot = llc_sk_laddr_hashfn(sap, laddr); | |
489 | read_lock(&sap->sk_list.lock); | 502 | struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot]; |
490 | sk_for_each(rc, node, &sap->sk_list.list) { | 503 | |
491 | struct llc_sock *llc = llc_sk(rc); | 504 | rcu_read_lock(); |
492 | 505 | again: | |
493 | if (llc->laddr.lsap == laddr->lsap && | 506 | sk_nulls_for_each_rcu(rc, node, laddr_hb) { |
494 | llc->daddr.lsap == daddr->lsap && | 507 | if (llc_estab_match(sap, daddr, laddr, rc)) { |
495 | llc_mac_match(llc->laddr.mac, laddr->mac) && | 508 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ |
496 | llc_mac_match(llc->daddr.mac, daddr->mac)) { | 509 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) |
497 | sock_hold(rc); | 510 | goto again; |
511 | if (unlikely(llc_sk(rc)->sap != sap || | ||
512 | !llc_estab_match(sap, daddr, laddr, rc))) { | ||
513 | sock_put(rc); | ||
514 | continue; | ||
515 | } | ||
498 | goto found; | 516 | goto found; |
499 | } | 517 | } |
500 | } | 518 | } |
501 | rc = NULL; | 519 | rc = NULL; |
520 | /* | ||
521 | * if the nulls value we got at the end of this lookup is | ||
522 | * not the expected one, we must restart lookup. | ||
523 | * We probably met an item that was moved to another chain. | ||
524 | */ | ||
525 | if (unlikely(get_nulls_value(node) != slot)) | ||
526 | goto again; | ||
502 | found: | 527 | found: |
503 | read_unlock(&sap->sk_list.lock); | 528 | rcu_read_unlock(); |
504 | return rc; | 529 | return rc; |
505 | } | 530 | } |
506 | 531 | ||
@@ -516,6 +541,53 @@ struct sock *llc_lookup_established(struct llc_sap *sap, | |||
516 | return sk; | 541 | return sk; |
517 | } | 542 | } |
518 | 543 | ||
544 | static inline bool llc_listener_match(const struct llc_sap *sap, | ||
545 | const struct llc_addr *laddr, | ||
546 | const struct sock *sk) | ||
547 | { | ||
548 | struct llc_sock *llc = llc_sk(sk); | ||
549 | |||
550 | return sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN && | ||
551 | llc->laddr.lsap == laddr->lsap && | ||
552 | llc_mac_match(llc->laddr.mac, laddr->mac); | ||
553 | } | ||
554 | |||
555 | static struct sock *__llc_lookup_listener(struct llc_sap *sap, | ||
556 | struct llc_addr *laddr) | ||
557 | { | ||
558 | struct sock *rc; | ||
559 | struct hlist_nulls_node *node; | ||
560 | int slot = llc_sk_laddr_hashfn(sap, laddr); | ||
561 | struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot]; | ||
562 | |||
563 | rcu_read_lock(); | ||
564 | again: | ||
565 | sk_nulls_for_each_rcu(rc, node, laddr_hb) { | ||
566 | if (llc_listener_match(sap, laddr, rc)) { | ||
567 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ | ||
568 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) | ||
569 | goto again; | ||
570 | if (unlikely(llc_sk(rc)->sap != sap || | ||
571 | !llc_listener_match(sap, laddr, rc))) { | ||
572 | sock_put(rc); | ||
573 | continue; | ||
574 | } | ||
575 | goto found; | ||
576 | } | ||
577 | } | ||
578 | rc = NULL; | ||
579 | /* | ||
580 | * if the nulls value we got at the end of this lookup is | ||
581 | * not the expected one, we must restart lookup. | ||
582 | * We probably met an item that was moved to another chain. | ||
583 | */ | ||
584 | if (unlikely(get_nulls_value(node) != slot)) | ||
585 | goto again; | ||
586 | found: | ||
587 | rcu_read_unlock(); | ||
588 | return rc; | ||
589 | } | ||
590 | |||
519 | /** | 591 | /** |
520 | * llc_lookup_listener - Finds listener for local MAC + SAP | 592 | * llc_lookup_listener - Finds listener for local MAC + SAP |
521 | * @sap: SAP | 593 | * @sap: SAP |
@@ -529,24 +601,12 @@ struct sock *llc_lookup_established(struct llc_sap *sap, | |||
529 | static struct sock *llc_lookup_listener(struct llc_sap *sap, | 601 | static struct sock *llc_lookup_listener(struct llc_sap *sap, |
530 | struct llc_addr *laddr) | 602 | struct llc_addr *laddr) |
531 | { | 603 | { |
532 | struct sock *rc; | 604 | static struct llc_addr null_addr; |
533 | struct hlist_node *node; | 605 | struct sock *rc = __llc_lookup_listener(sap, laddr); |
534 | 606 | ||
535 | read_lock(&sap->sk_list.lock); | 607 | if (!rc) |
536 | sk_for_each(rc, node, &sap->sk_list.list) { | 608 | rc = __llc_lookup_listener(sap, &null_addr); |
537 | struct llc_sock *llc = llc_sk(rc); | ||
538 | 609 | ||
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; | 610 | return rc; |
551 | } | 611 | } |
552 | 612 | ||
@@ -647,15 +707,22 @@ static int llc_find_offset(int state, int ev_type) | |||
647 | * @sap: SAP | 707 | * @sap: SAP |
648 | * @sk: socket | 708 | * @sk: socket |
649 | * | 709 | * |
650 | * This function adds a socket to sk_list of a SAP. | 710 | * This function adds a socket to the hash tables of a SAP. |
651 | */ | 711 | */ |
652 | void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) | 712 | void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) |
653 | { | 713 | { |
714 | struct llc_sock *llc = llc_sk(sk); | ||
715 | struct hlist_head *dev_hb = llc_sk_dev_hash(sap, llc->dev->ifindex); | ||
716 | struct hlist_nulls_head *laddr_hb = llc_sk_laddr_hash(sap, &llc->laddr); | ||
717 | |||
654 | llc_sap_hold(sap); | 718 | llc_sap_hold(sap); |
655 | write_lock_bh(&sap->sk_list.lock); | ||
656 | llc_sk(sk)->sap = sap; | 719 | llc_sk(sk)->sap = sap; |
657 | sk_add_node(sk, &sap->sk_list.list); | 720 | |
658 | write_unlock_bh(&sap->sk_list.lock); | 721 | spin_lock_bh(&sap->sk_lock); |
722 | sap->sk_count++; | ||
723 | sk_nulls_add_node_rcu(sk, laddr_hb); | ||
724 | hlist_add_head(&llc->dev_hash_node, dev_hb); | ||
725 | spin_unlock_bh(&sap->sk_lock); | ||
659 | } | 726 | } |
660 | 727 | ||
661 | /** | 728 | /** |
@@ -663,14 +730,18 @@ void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) | |||
663 | * @sap: SAP | 730 | * @sap: SAP |
664 | * @sk: socket | 731 | * @sk: socket |
665 | * | 732 | * |
666 | * This function removes a connection from sk_list.list of a SAP if | 733 | * This function removes a connection from the hash tables of a SAP if |
667 | * the connection was in this list. | 734 | * the connection was in this list. |
668 | */ | 735 | */ |
669 | void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) | 736 | void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) |
670 | { | 737 | { |
671 | write_lock_bh(&sap->sk_list.lock); | 738 | struct llc_sock *llc = llc_sk(sk); |
672 | sk_del_node_init(sk); | 739 | |
673 | write_unlock_bh(&sap->sk_list.lock); | 740 | spin_lock_bh(&sap->sk_lock); |
741 | sk_nulls_del_node_init_rcu(sk); | ||
742 | hlist_del(&llc->dev_hash_node); | ||
743 | sap->sk_count--; | ||
744 | spin_unlock_bh(&sap->sk_lock); | ||
674 | llc_sap_put(sap); | 745 | llc_sap_put(sap); |
675 | } | 746 | } |
676 | 747 | ||
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_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..ad6e6e1cf22f 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c | |||
@@ -297,6 +297,17 @@ static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb, | |||
297 | llc_sap_state_process(sap, skb); | 297 | llc_sap_state_process(sap, skb); |
298 | } | 298 | } |
299 | 299 | ||
300 | static inline bool llc_dgram_match(const struct llc_sap *sap, | ||
301 | const struct llc_addr *laddr, | ||
302 | const struct sock *sk) | ||
303 | { | ||
304 | struct llc_sock *llc = llc_sk(sk); | ||
305 | |||
306 | return sk->sk_type == SOCK_DGRAM && | ||
307 | llc->laddr.lsap == laddr->lsap && | ||
308 | llc_mac_match(llc->laddr.mac, laddr->mac); | ||
309 | } | ||
310 | |||
300 | /** | 311 | /** |
301 | * llc_lookup_dgram - Finds dgram socket for the local sap/mac | 312 | * llc_lookup_dgram - Finds dgram socket for the local sap/mac |
302 | * @sap: SAP | 313 | * @sap: SAP |
@@ -309,25 +320,68 @@ static struct sock *llc_lookup_dgram(struct llc_sap *sap, | |||
309 | const struct llc_addr *laddr) | 320 | const struct llc_addr *laddr) |
310 | { | 321 | { |
311 | struct sock *rc; | 322 | struct sock *rc; |
312 | struct hlist_node *node; | 323 | struct hlist_nulls_node *node; |
313 | 324 | int slot = llc_sk_laddr_hashfn(sap, laddr); | |
314 | read_lock_bh(&sap->sk_list.lock); | 325 | struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot]; |
315 | sk_for_each(rc, node, &sap->sk_list.list) { | 326 | |
316 | struct llc_sock *llc = llc_sk(rc); | 327 | rcu_read_lock_bh(); |
317 | 328 | again: | |
318 | if (rc->sk_type == SOCK_DGRAM && | 329 | sk_nulls_for_each_rcu(rc, node, laddr_hb) { |
319 | llc->laddr.lsap == laddr->lsap && | 330 | if (llc_dgram_match(sap, laddr, rc)) { |
320 | llc_mac_match(llc->laddr.mac, laddr->mac)) { | 331 | /* Extra checks required by SLAB_DESTROY_BY_RCU */ |
321 | sock_hold(rc); | 332 | if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt))) |
333 | goto again; | ||
334 | if (unlikely(llc_sk(rc)->sap != sap || | ||
335 | !llc_dgram_match(sap, laddr, rc))) { | ||
336 | sock_put(rc); | ||
337 | continue; | ||
338 | } | ||
322 | goto found; | 339 | goto found; |
323 | } | 340 | } |
324 | } | 341 | } |
325 | rc = NULL; | 342 | rc = NULL; |
343 | /* | ||
344 | * if the nulls value we got at the end of this lookup is | ||
345 | * not the expected one, we must restart lookup. | ||
346 | * We probably met an item that was moved to another chain. | ||
347 | */ | ||
348 | if (unlikely(get_nulls_value(node) != slot)) | ||
349 | goto again; | ||
326 | found: | 350 | found: |
327 | read_unlock_bh(&sap->sk_list.lock); | 351 | rcu_read_unlock_bh(); |
328 | return rc; | 352 | return rc; |
329 | } | 353 | } |
330 | 354 | ||
355 | static inline bool llc_mcast_match(const struct llc_sap *sap, | ||
356 | const struct llc_addr *laddr, | ||
357 | const struct sk_buff *skb, | ||
358 | const struct sock *sk) | ||
359 | { | ||
360 | struct llc_sock *llc = llc_sk(sk); | ||
361 | |||
362 | return sk->sk_type == SOCK_DGRAM && | ||
363 | llc->laddr.lsap == laddr->lsap && | ||
364 | llc->dev == skb->dev; | ||
365 | } | ||
366 | |||
367 | static void llc_do_mcast(struct llc_sap *sap, struct sk_buff *skb, | ||
368 | struct sock **stack, int count) | ||
369 | { | ||
370 | struct sk_buff *skb1; | ||
371 | int i; | ||
372 | |||
373 | for (i = 0; i < count; i++) { | ||
374 | skb1 = skb_clone(skb, GFP_ATOMIC); | ||
375 | if (!skb1) { | ||
376 | sock_put(stack[i]); | ||
377 | continue; | ||
378 | } | ||
379 | |||
380 | llc_sap_rcv(sap, skb1, stack[i]); | ||
381 | sock_put(stack[i]); | ||
382 | } | ||
383 | } | ||
384 | |||
331 | /** | 385 | /** |
332 | * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets. | 386 | * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets. |
333 | * @sap: SAP | 387 | * @sap: SAP |
@@ -340,32 +394,31 @@ static void llc_sap_mcast(struct llc_sap *sap, | |||
340 | const struct llc_addr *laddr, | 394 | const struct llc_addr *laddr, |
341 | struct sk_buff *skb) | 395 | struct sk_buff *skb) |
342 | { | 396 | { |
343 | struct sock *sk; | 397 | int i = 0, count = 256 / sizeof(struct sock *); |
398 | struct sock *sk, *stack[count]; | ||
344 | struct hlist_node *node; | 399 | struct hlist_node *node; |
400 | struct llc_sock *llc; | ||
401 | struct hlist_head *dev_hb = llc_sk_dev_hash(sap, skb->dev->ifindex); | ||
345 | 402 | ||
346 | read_lock_bh(&sap->sk_list.lock); | 403 | spin_lock_bh(&sap->sk_lock); |
347 | sk_for_each(sk, node, &sap->sk_list.list) { | 404 | 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 | 405 | ||
351 | if (sk->sk_type != SOCK_DGRAM) | 406 | sk = &llc->sk; |
352 | continue; | ||
353 | 407 | ||
354 | if (llc->laddr.lsap != laddr->lsap) | 408 | if (!llc_mcast_match(sap, laddr, skb, sk)) |
355 | continue; | 409 | continue; |
356 | 410 | ||
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); | 411 | sock_hold(sk); |
365 | llc_sap_rcv(sap, skb1, sk); | 412 | if (i < count) |
366 | sock_put(sk); | 413 | stack[i++] = sk; |
414 | else { | ||
415 | llc_do_mcast(sap, skb, stack, i); | ||
416 | i = 0; | ||
417 | } | ||
367 | } | 418 | } |
368 | read_unlock_bh(&sap->sk_list.lock); | 419 | spin_unlock_bh(&sap->sk_lock); |
420 | |||
421 | llc_do_mcast(sap, skb, stack, i); | ||
369 | } | 422 | } |
370 | 423 | ||
371 | 424 | ||