diff options
Diffstat (limited to 'net/llc/llc_conn.c')
-rw-r--r-- | net/llc/llc_conn.c | 147 |
1 files changed, 110 insertions, 37 deletions
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); |