|
@@ -498,10 +498,12 @@ static struct sock *__llc_lookup_established(struct llc_sap *sap,
|
|
|
{
|
|
|
struct sock *rc;
|
|
|
struct hlist_nulls_node *node;
|
|
|
+ int slot = llc_sk_laddr_hashfn(sap, laddr);
|
|
|
+ struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
|
|
|
|
|
|
rcu_read_lock();
|
|
|
again:
|
|
|
- sk_nulls_for_each_rcu(rc, node, &sap->sk_list) {
|
|
|
+ sk_nulls_for_each_rcu(rc, node, laddr_hb) {
|
|
|
if (llc_estab_match(sap, daddr, laddr, rc)) {
|
|
|
/* Extra checks required by SLAB_DESTROY_BY_RCU */
|
|
|
if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
|
|
@@ -515,6 +517,13 @@ again:
|
|
|
}
|
|
|
}
|
|
|
rc = NULL;
|
|
|
+ /*
|
|
|
+ * if the nulls value we got at the end of this lookup is
|
|
|
+ * not the expected one, we must restart lookup.
|
|
|
+ * We probably met an item that was moved to another chain.
|
|
|
+ */
|
|
|
+ if (unlikely(get_nulls_value(node) != slot))
|
|
|
+ goto again;
|
|
|
found:
|
|
|
rcu_read_unlock();
|
|
|
return rc;
|
|
@@ -540,29 +549,20 @@ static inline bool llc_listener_match(const struct llc_sap *sap,
|
|
|
|
|
|
return sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN &&
|
|
|
llc->laddr.lsap == laddr->lsap &&
|
|
|
- (llc_mac_match(llc->laddr.mac, laddr->mac) ||
|
|
|
- llc_mac_null(llc->laddr.mac));
|
|
|
+ llc_mac_match(llc->laddr.mac, laddr->mac);
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * llc_lookup_listener - Finds listener for local MAC + SAP
|
|
|
- * @sap: SAP
|
|
|
- * @laddr: address of local LLC (MAC + SAP)
|
|
|
- *
|
|
|
- * Search connection list of the SAP and finds connection listening on
|
|
|
- * local mac, and local sap. Returns pointer for parent socket found,
|
|
|
- * %NULL otherwise.
|
|
|
- * Caller has to make sure local_bh is disabled.
|
|
|
- */
|
|
|
-static struct sock *llc_lookup_listener(struct llc_sap *sap,
|
|
|
- struct llc_addr *laddr)
|
|
|
+static struct sock *__llc_lookup_listener(struct llc_sap *sap,
|
|
|
+ struct llc_addr *laddr)
|
|
|
{
|
|
|
struct sock *rc;
|
|
|
struct hlist_nulls_node *node;
|
|
|
+ int slot = llc_sk_laddr_hashfn(sap, laddr);
|
|
|
+ struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
|
|
|
|
|
|
rcu_read_lock();
|
|
|
again:
|
|
|
- sk_nulls_for_each_rcu(rc, node, &sap->sk_list) {
|
|
|
+ sk_nulls_for_each_rcu(rc, node, laddr_hb) {
|
|
|
if (llc_listener_match(sap, laddr, rc)) {
|
|
|
/* Extra checks required by SLAB_DESTROY_BY_RCU */
|
|
|
if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
|
|
@@ -576,11 +576,40 @@ again:
|
|
|
}
|
|
|
}
|
|
|
rc = NULL;
|
|
|
+ /*
|
|
|
+ * if the nulls value we got at the end of this lookup is
|
|
|
+ * not the expected one, we must restart lookup.
|
|
|
+ * We probably met an item that was moved to another chain.
|
|
|
+ */
|
|
|
+ if (unlikely(get_nulls_value(node) != slot))
|
|
|
+ goto again;
|
|
|
found:
|
|
|
rcu_read_unlock();
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * llc_lookup_listener - Finds listener for local MAC + SAP
|
|
|
+ * @sap: SAP
|
|
|
+ * @laddr: address of local LLC (MAC + SAP)
|
|
|
+ *
|
|
|
+ * Search connection list of the SAP and finds connection listening on
|
|
|
+ * local mac, and local sap. Returns pointer for parent socket found,
|
|
|
+ * %NULL otherwise.
|
|
|
+ * Caller has to make sure local_bh is disabled.
|
|
|
+ */
|
|
|
+static struct sock *llc_lookup_listener(struct llc_sap *sap,
|
|
|
+ struct llc_addr *laddr)
|
|
|
+{
|
|
|
+ static struct llc_addr null_addr;
|
|
|
+ struct sock *rc = __llc_lookup_listener(sap, laddr);
|
|
|
+
|
|
|
+ if (!rc)
|
|
|
+ rc = __llc_lookup_listener(sap, &null_addr);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
static struct sock *__llc_lookup(struct llc_sap *sap,
|
|
|
struct llc_addr *daddr,
|
|
|
struct llc_addr *laddr)
|
|
@@ -678,18 +707,20 @@ static int llc_find_offset(int state, int ev_type)
|
|
|
* @sap: SAP
|
|
|
* @sk: socket
|
|
|
*
|
|
|
- * This function adds a socket to sk_list of a SAP.
|
|
|
+ * This function adds a socket to the hash tables of a SAP.
|
|
|
*/
|
|
|
void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
|
|
|
{
|
|
|
struct llc_sock *llc = llc_sk(sk);
|
|
|
struct hlist_head *dev_hb = llc_sk_dev_hash(sap, llc->dev->ifindex);
|
|
|
+ struct hlist_nulls_head *laddr_hb = llc_sk_laddr_hash(sap, &llc->laddr);
|
|
|
|
|
|
llc_sap_hold(sap);
|
|
|
llc_sk(sk)->sap = sap;
|
|
|
|
|
|
spin_lock_bh(&sap->sk_lock);
|
|
|
- sk_nulls_add_node_rcu(sk, &sap->sk_list);
|
|
|
+ sap->sk_count++;
|
|
|
+ sk_nulls_add_node_rcu(sk, laddr_hb);
|
|
|
hlist_add_head(&llc->dev_hash_node, dev_hb);
|
|
|
spin_unlock_bh(&sap->sk_lock);
|
|
|
}
|
|
@@ -699,7 +730,7 @@ void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
|
|
|
* @sap: SAP
|
|
|
* @sk: socket
|
|
|
*
|
|
|
- * This function removes a connection from sk_list of a SAP if
|
|
|
+ * This function removes a connection from the hash tables of a SAP if
|
|
|
* the connection was in this list.
|
|
|
*/
|
|
|
void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk)
|
|
@@ -709,6 +740,7 @@ void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk)
|
|
|
spin_lock_bh(&sap->sk_lock);
|
|
|
sk_nulls_del_node_init_rcu(sk);
|
|
|
hlist_del(&llc->dev_hash_node);
|
|
|
+ sap->sk_count--;
|
|
|
spin_unlock_bh(&sap->sk_lock);
|
|
|
llc_sap_put(sap);
|
|
|
}
|