diff options
-rw-r--r-- | net/rxrpc/Makefile | 3 | ||||
-rw-r--r-- | net/rxrpc/af_rxrpc.c | 3 | ||||
-rw-r--r-- | net/rxrpc/ar-internal.h | 46 | ||||
-rw-r--r-- | net/rxrpc/call_accept.c | 2 | ||||
-rw-r--r-- | net/rxrpc/input.c | 13 | ||||
-rw-r--r-- | net/rxrpc/peer_event.c | 59 | ||||
-rw-r--r-- | net/rxrpc/peer_object.c | 369 | ||||
-rw-r--r-- | net/rxrpc/transport.c | 2 | ||||
-rw-r--r-- | net/rxrpc/utils.c | 41 |
9 files changed, 335 insertions, 203 deletions
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index 7e1006a3bfa5..a6f6f21d8a59 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile | |||
@@ -20,7 +20,8 @@ af-rxrpc-y := \ | |||
20 | recvmsg.o \ | 20 | recvmsg.o \ |
21 | security.o \ | 21 | security.o \ |
22 | skbuff.o \ | 22 | skbuff.o \ |
23 | transport.o | 23 | transport.o \ |
24 | utils.o | ||
24 | 25 | ||
25 | af-rxrpc-$(CONFIG_PROC_FS) += proc.o | 26 | af-rxrpc-$(CONFIG_PROC_FS) += proc.o |
26 | af-rxrpc-$(CONFIG_RXKAD) += rxkad.o | 27 | af-rxrpc-$(CONFIG_RXKAD) += rxkad.o |
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index a1bcb0e17250..ba373caddbeb 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c | |||
@@ -244,7 +244,7 @@ struct rxrpc_transport *rxrpc_name_to_transport(struct rxrpc_sock *rx, | |||
244 | return ERR_PTR(-EAFNOSUPPORT); | 244 | return ERR_PTR(-EAFNOSUPPORT); |
245 | 245 | ||
246 | /* find a remote transport endpoint from the local one */ | 246 | /* find a remote transport endpoint from the local one */ |
247 | peer = rxrpc_get_peer(srx, gfp); | 247 | peer = rxrpc_lookup_peer(rx->local, srx, gfp); |
248 | if (IS_ERR(peer)) | 248 | if (IS_ERR(peer)) |
249 | return ERR_CAST(peer); | 249 | return ERR_CAST(peer); |
250 | 250 | ||
@@ -835,7 +835,6 @@ static void __exit af_rxrpc_exit(void) | |||
835 | rxrpc_destroy_all_calls(); | 835 | rxrpc_destroy_all_calls(); |
836 | rxrpc_destroy_all_connections(); | 836 | rxrpc_destroy_all_connections(); |
837 | rxrpc_destroy_all_transports(); | 837 | rxrpc_destroy_all_transports(); |
838 | rxrpc_destroy_all_peers(); | ||
839 | rxrpc_destroy_all_locals(); | 838 | rxrpc_destroy_all_locals(); |
840 | 839 | ||
841 | ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0); | 840 | ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0); |
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 03919b9a8a31..7dba6677b9d5 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h | |||
@@ -9,7 +9,9 @@ | |||
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/atomic.h> | ||
12 | #include <net/sock.h> | 13 | #include <net/sock.h> |
14 | #include <net/af_rxrpc.h> | ||
13 | #include <rxrpc/packet.h> | 15 | #include <rxrpc/packet.h> |
14 | 16 | ||
15 | #if 0 | 17 | #if 0 |
@@ -193,15 +195,16 @@ struct rxrpc_local { | |||
193 | 195 | ||
194 | /* | 196 | /* |
195 | * RxRPC remote transport endpoint definition | 197 | * RxRPC remote transport endpoint definition |
196 | * - matched by remote port, address and protocol type | 198 | * - matched by local endpoint, remote port, address and protocol type |
197 | * - holds the connection ID counter for connections between the two endpoints | ||
198 | */ | 199 | */ |
199 | struct rxrpc_peer { | 200 | struct rxrpc_peer { |
200 | struct work_struct destroyer; /* peer destroyer */ | 201 | struct rcu_head rcu; /* This must be first */ |
201 | struct list_head link; /* link in master peer list */ | 202 | atomic_t usage; |
203 | unsigned long hash_key; | ||
204 | struct hlist_node hash_link; | ||
205 | struct rxrpc_local *local; | ||
202 | struct list_head error_targets; /* targets for net error distribution */ | 206 | struct list_head error_targets; /* targets for net error distribution */ |
203 | spinlock_t lock; /* access lock */ | 207 | spinlock_t lock; /* access lock */ |
204 | atomic_t usage; | ||
205 | unsigned int if_mtu; /* interface MTU for this peer */ | 208 | unsigned int if_mtu; /* interface MTU for this peer */ |
206 | unsigned int mtu; /* network MTU for this peer */ | 209 | unsigned int mtu; /* network MTU for this peer */ |
207 | unsigned int maxdata; /* data size (MTU - hdrsize) */ | 210 | unsigned int maxdata; /* data size (MTU - hdrsize) */ |
@@ -611,10 +614,29 @@ void rxrpc_UDP_error_handler(struct work_struct *); | |||
611 | /* | 614 | /* |
612 | * peer_object.c | 615 | * peer_object.c |
613 | */ | 616 | */ |
614 | struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t); | 617 | struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *, |
615 | void rxrpc_put_peer(struct rxrpc_peer *); | 618 | const struct sockaddr_rxrpc *); |
616 | struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *, __be32, __be16); | 619 | struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *, |
617 | void __exit rxrpc_destroy_all_peers(void); | 620 | struct sockaddr_rxrpc *, gfp_t); |
621 | struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t); | ||
622 | |||
623 | static inline void rxrpc_get_peer(struct rxrpc_peer *peer) | ||
624 | { | ||
625 | atomic_inc(&peer->usage); | ||
626 | } | ||
627 | |||
628 | static inline | ||
629 | struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *peer) | ||
630 | { | ||
631 | return atomic_inc_not_zero(&peer->usage) ? peer : NULL; | ||
632 | } | ||
633 | |||
634 | extern void __rxrpc_put_peer(struct rxrpc_peer *peer); | ||
635 | static inline void rxrpc_put_peer(struct rxrpc_peer *peer) | ||
636 | { | ||
637 | if (atomic_dec_and_test(&peer->usage)) | ||
638 | __rxrpc_put_peer(peer); | ||
639 | } | ||
618 | 640 | ||
619 | /* | 641 | /* |
620 | * proc.c | 642 | * proc.c |
@@ -673,6 +695,12 @@ struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *, | |||
673 | struct rxrpc_peer *); | 695 | struct rxrpc_peer *); |
674 | 696 | ||
675 | /* | 697 | /* |
698 | * utils.c | ||
699 | */ | ||
700 | void rxrpc_get_addr_from_skb(struct rxrpc_local *, const struct sk_buff *, | ||
701 | struct sockaddr_rxrpc *); | ||
702 | |||
703 | /* | ||
676 | * debug tracing | 704 | * debug tracing |
677 | */ | 705 | */ |
678 | extern unsigned int rxrpc_debug; | 706 | extern unsigned int rxrpc_debug; |
diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index eea5f4a5d8b1..e5723f4dce89 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c | |||
@@ -95,7 +95,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, | |||
95 | rxrpc_new_skb(notification); | 95 | rxrpc_new_skb(notification); |
96 | notification->mark = RXRPC_SKB_MARK_NEW_CALL; | 96 | notification->mark = RXRPC_SKB_MARK_NEW_CALL; |
97 | 97 | ||
98 | peer = rxrpc_get_peer(srx, GFP_NOIO); | 98 | peer = rxrpc_lookup_peer(local, srx, GFP_NOIO); |
99 | if (IS_ERR(peer)) { | 99 | if (IS_ERR(peer)) { |
100 | _debug("no peer"); | 100 | _debug("no peer"); |
101 | ret = -EBUSY; | 101 | ret = -EBUSY; |
diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index e0815a033999..3b405dbf3a05 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c | |||
@@ -635,14 +635,16 @@ static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local, | |||
635 | struct rxrpc_peer *peer; | 635 | struct rxrpc_peer *peer; |
636 | struct rxrpc_transport *trans; | 636 | struct rxrpc_transport *trans; |
637 | struct rxrpc_connection *conn; | 637 | struct rxrpc_connection *conn; |
638 | struct sockaddr_rxrpc srx; | ||
638 | 639 | ||
639 | peer = rxrpc_find_peer(local, ip_hdr(skb)->saddr, | 640 | rxrpc_get_addr_from_skb(local, skb, &srx); |
640 | udp_hdr(skb)->source); | 641 | rcu_read_lock(); |
642 | peer = rxrpc_lookup_peer_rcu(local, &srx); | ||
641 | if (IS_ERR(peer)) | 643 | if (IS_ERR(peer)) |
642 | goto cant_find_conn; | 644 | goto cant_find_peer; |
643 | 645 | ||
644 | trans = rxrpc_find_transport(local, peer); | 646 | trans = rxrpc_find_transport(local, peer); |
645 | rxrpc_put_peer(peer); | 647 | rcu_read_unlock(); |
646 | if (!trans) | 648 | if (!trans) |
647 | goto cant_find_conn; | 649 | goto cant_find_conn; |
648 | 650 | ||
@@ -652,6 +654,9 @@ static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local, | |||
652 | goto cant_find_conn; | 654 | goto cant_find_conn; |
653 | 655 | ||
654 | return conn; | 656 | return conn; |
657 | |||
658 | cant_find_peer: | ||
659 | rcu_read_unlock(); | ||
655 | cant_find_conn: | 660 | cant_find_conn: |
656 | return NULL; | 661 | return NULL; |
657 | } | 662 | } |
diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index 3e82d6f0313c..24f5ec0fcd20 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c | |||
@@ -23,6 +23,55 @@ | |||
23 | #include "ar-internal.h" | 23 | #include "ar-internal.h" |
24 | 24 | ||
25 | /* | 25 | /* |
26 | * Find the peer associated with an ICMP packet. | ||
27 | */ | ||
28 | static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, | ||
29 | const struct sk_buff *skb) | ||
30 | { | ||
31 | struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); | ||
32 | struct sockaddr_rxrpc srx; | ||
33 | |||
34 | _enter(""); | ||
35 | |||
36 | memset(&srx, 0, sizeof(srx)); | ||
37 | srx.transport_type = local->srx.transport_type; | ||
38 | srx.transport.family = local->srx.transport.family; | ||
39 | |||
40 | /* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice | ||
41 | * versa? | ||
42 | */ | ||
43 | switch (srx.transport.family) { | ||
44 | case AF_INET: | ||
45 | srx.transport.sin.sin_port = serr->port; | ||
46 | srx.transport_len = sizeof(struct sockaddr_in); | ||
47 | switch (serr->ee.ee_origin) { | ||
48 | case SO_EE_ORIGIN_ICMP: | ||
49 | _net("Rx ICMP"); | ||
50 | memcpy(&srx.transport.sin.sin_addr, | ||
51 | skb_network_header(skb) + serr->addr_offset, | ||
52 | sizeof(struct in_addr)); | ||
53 | break; | ||
54 | case SO_EE_ORIGIN_ICMP6: | ||
55 | _net("Rx ICMP6 on v4 sock"); | ||
56 | memcpy(&srx.transport.sin.sin_addr, | ||
57 | skb_network_header(skb) + serr->addr_offset + 12, | ||
58 | sizeof(struct in_addr)); | ||
59 | break; | ||
60 | default: | ||
61 | memcpy(&srx.transport.sin.sin_addr, &ip_hdr(skb)->saddr, | ||
62 | sizeof(struct in_addr)); | ||
63 | break; | ||
64 | } | ||
65 | break; | ||
66 | |||
67 | default: | ||
68 | BUG(); | ||
69 | } | ||
70 | |||
71 | return rxrpc_lookup_peer_rcu(local, &srx); | ||
72 | } | ||
73 | |||
74 | /* | ||
26 | * handle an error received on the local endpoint | 75 | * handle an error received on the local endpoint |
27 | */ | 76 | */ |
28 | void rxrpc_UDP_error_report(struct sock *sk) | 77 | void rxrpc_UDP_error_report(struct sock *sk) |
@@ -57,8 +106,12 @@ void rxrpc_UDP_error_report(struct sock *sk) | |||
57 | _net("Rx UDP Error from %pI4:%hu", &addr, ntohs(port)); | 106 | _net("Rx UDP Error from %pI4:%hu", &addr, ntohs(port)); |
58 | _debug("Msg l:%d d:%d", skb->len, skb->data_len); | 107 | _debug("Msg l:%d d:%d", skb->len, skb->data_len); |
59 | 108 | ||
60 | peer = rxrpc_find_peer(local, addr, port); | 109 | rcu_read_lock(); |
61 | if (IS_ERR(peer)) { | 110 | peer = rxrpc_lookup_peer_icmp_rcu(local, skb); |
111 | if (peer && !rxrpc_get_peer_maybe(peer)) | ||
112 | peer = NULL; | ||
113 | if (!peer) { | ||
114 | rcu_read_unlock(); | ||
62 | rxrpc_free_skb(skb); | 115 | rxrpc_free_skb(skb); |
63 | _leave(" [no peer]"); | 116 | _leave(" [no peer]"); |
64 | return; | 117 | return; |
@@ -66,6 +119,7 @@ void rxrpc_UDP_error_report(struct sock *sk) | |||
66 | 119 | ||
67 | trans = rxrpc_find_transport(local, peer); | 120 | trans = rxrpc_find_transport(local, peer); |
68 | if (!trans) { | 121 | if (!trans) { |
122 | rcu_read_unlock(); | ||
69 | rxrpc_put_peer(peer); | 123 | rxrpc_put_peer(peer); |
70 | rxrpc_free_skb(skb); | 124 | rxrpc_free_skb(skb); |
71 | _leave(" [no trans]"); | 125 | _leave(" [no trans]"); |
@@ -110,6 +164,7 @@ void rxrpc_UDP_error_report(struct sock *sk) | |||
110 | } | 164 | } |
111 | } | 165 | } |
112 | 166 | ||
167 | rcu_read_unlock(); | ||
113 | rxrpc_put_peer(peer); | 168 | rxrpc_put_peer(peer); |
114 | 169 | ||
115 | /* pass the transport ref to error_handler to release */ | 170 | /* pass the transport ref to error_handler to release */ |
diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index 0b54cda3d8e5..7fc50dc7d333 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* RxRPC remote transport endpoint management | 1 | /* RxRPC remote transport endpoint record management |
2 | * | 2 | * |
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | 3 | * Copyright (C) 2007, 2016 Red Hat, Inc. All Rights Reserved. |
4 | * Written by David Howells (dhowells@redhat.com) | 4 | * Written by David Howells (dhowells@redhat.com) |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
@@ -16,20 +16,132 @@ | |||
16 | #include <linux/skbuff.h> | 16 | #include <linux/skbuff.h> |
17 | #include <linux/udp.h> | 17 | #include <linux/udp.h> |
18 | #include <linux/in.h> | 18 | #include <linux/in.h> |
19 | #include <linux/in6.h> | ||
20 | #include <linux/icmp.h> | ||
21 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/hashtable.h> | ||
22 | #include <net/sock.h> | 21 | #include <net/sock.h> |
23 | #include <net/af_rxrpc.h> | 22 | #include <net/af_rxrpc.h> |
24 | #include <net/ip.h> | 23 | #include <net/ip.h> |
25 | #include <net/route.h> | 24 | #include <net/route.h> |
26 | #include "ar-internal.h" | 25 | #include "ar-internal.h" |
27 | 26 | ||
28 | static LIST_HEAD(rxrpc_peers); | 27 | static DEFINE_HASHTABLE(rxrpc_peer_hash, 10); |
29 | static DEFINE_RWLOCK(rxrpc_peer_lock); | 28 | static DEFINE_SPINLOCK(rxrpc_peer_hash_lock); |
30 | static DECLARE_WAIT_QUEUE_HEAD(rxrpc_peer_wq); | ||
31 | 29 | ||
32 | static void rxrpc_destroy_peer(struct work_struct *work); | 30 | /* |
31 | * Hash a peer key. | ||
32 | */ | ||
33 | static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local, | ||
34 | const struct sockaddr_rxrpc *srx) | ||
35 | { | ||
36 | const u16 *p; | ||
37 | unsigned int i, size; | ||
38 | unsigned long hash_key; | ||
39 | |||
40 | _enter(""); | ||
41 | |||
42 | hash_key = (unsigned long)local / __alignof__(*local); | ||
43 | hash_key += srx->transport_type; | ||
44 | hash_key += srx->transport_len; | ||
45 | hash_key += srx->transport.family; | ||
46 | |||
47 | switch (srx->transport.family) { | ||
48 | case AF_INET: | ||
49 | hash_key += (u16 __force)srx->transport.sin.sin_port; | ||
50 | size = sizeof(srx->transport.sin.sin_addr); | ||
51 | p = (u16 *)&srx->transport.sin.sin_addr; | ||
52 | break; | ||
53 | } | ||
54 | |||
55 | /* Step through the peer address in 16-bit portions for speed */ | ||
56 | for (i = 0; i < size; i += sizeof(*p), p++) | ||
57 | hash_key += *p; | ||
58 | |||
59 | _leave(" 0x%lx", hash_key); | ||
60 | return hash_key; | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * Compare a peer to a key. Return -ve, 0 or +ve to indicate less than, same | ||
65 | * or greater than. | ||
66 | * | ||
67 | * Unfortunately, the primitives in linux/hashtable.h don't allow for sorted | ||
68 | * buckets and mid-bucket insertion, so we don't make full use of this | ||
69 | * information at this point. | ||
70 | */ | ||
71 | static long rxrpc_peer_cmp_key(const struct rxrpc_peer *peer, | ||
72 | struct rxrpc_local *local, | ||
73 | const struct sockaddr_rxrpc *srx, | ||
74 | unsigned long hash_key) | ||
75 | { | ||
76 | long diff; | ||
77 | |||
78 | diff = ((peer->hash_key - hash_key) ?: | ||
79 | ((unsigned long)peer->local - (unsigned long)local) ?: | ||
80 | (peer->srx.transport_type - srx->transport_type) ?: | ||
81 | (peer->srx.transport_len - srx->transport_len) ?: | ||
82 | (peer->srx.transport.family - srx->transport.family)); | ||
83 | if (diff != 0) | ||
84 | return diff; | ||
85 | |||
86 | switch (srx->transport.family) { | ||
87 | case AF_INET: | ||
88 | return ((u16 __force)peer->srx.transport.sin.sin_port - | ||
89 | (u16 __force)srx->transport.sin.sin_port) ?: | ||
90 | memcmp(&peer->srx.transport.sin.sin_addr, | ||
91 | &srx->transport.sin.sin_addr, | ||
92 | sizeof(struct in_addr)); | ||
93 | default: | ||
94 | BUG(); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * Look up a remote transport endpoint for the specified address using RCU. | ||
100 | */ | ||
101 | static struct rxrpc_peer *__rxrpc_lookup_peer_rcu( | ||
102 | struct rxrpc_local *local, | ||
103 | const struct sockaddr_rxrpc *srx, | ||
104 | unsigned long hash_key) | ||
105 | { | ||
106 | struct rxrpc_peer *peer; | ||
107 | |||
108 | hash_for_each_possible_rcu(rxrpc_peer_hash, peer, hash_link, hash_key) { | ||
109 | if (rxrpc_peer_cmp_key(peer, local, srx, hash_key) == 0) { | ||
110 | if (atomic_read(&peer->usage) == 0) | ||
111 | return NULL; | ||
112 | return peer; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | return NULL; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Look up a remote transport endpoint for the specified address using RCU. | ||
121 | */ | ||
122 | struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local, | ||
123 | const struct sockaddr_rxrpc *srx) | ||
124 | { | ||
125 | struct rxrpc_peer *peer; | ||
126 | unsigned long hash_key = rxrpc_peer_hash_key(local, srx); | ||
127 | |||
128 | peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key); | ||
129 | if (peer) { | ||
130 | switch (srx->transport.family) { | ||
131 | case AF_INET: | ||
132 | _net("PEER %d {%d,%u,%pI4+%hu}", | ||
133 | peer->debug_id, | ||
134 | peer->srx.transport_type, | ||
135 | peer->srx.transport.family, | ||
136 | &peer->srx.transport.sin.sin_addr, | ||
137 | ntohs(peer->srx.transport.sin.sin_port)); | ||
138 | break; | ||
139 | } | ||
140 | |||
141 | _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage)); | ||
142 | } | ||
143 | return peer; | ||
144 | } | ||
33 | 145 | ||
34 | /* | 146 | /* |
35 | * assess the MTU size for the network interface through which this peer is | 147 | * assess the MTU size for the network interface through which this peer is |
@@ -58,10 +170,9 @@ static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer) | |||
58 | } | 170 | } |
59 | 171 | ||
60 | /* | 172 | /* |
61 | * allocate a new peer | 173 | * Allocate a peer. |
62 | */ | 174 | */ |
63 | static struct rxrpc_peer *rxrpc_alloc_peer(struct sockaddr_rxrpc *srx, | 175 | struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp) |
64 | gfp_t gfp) | ||
65 | { | 176 | { |
66 | struct rxrpc_peer *peer; | 177 | struct rxrpc_peer *peer; |
67 | 178 | ||
@@ -69,12 +180,32 @@ static struct rxrpc_peer *rxrpc_alloc_peer(struct sockaddr_rxrpc *srx, | |||
69 | 180 | ||
70 | peer = kzalloc(sizeof(struct rxrpc_peer), gfp); | 181 | peer = kzalloc(sizeof(struct rxrpc_peer), gfp); |
71 | if (peer) { | 182 | if (peer) { |
72 | INIT_WORK(&peer->destroyer, &rxrpc_destroy_peer); | 183 | atomic_set(&peer->usage, 1); |
73 | INIT_LIST_HEAD(&peer->link); | 184 | peer->local = local; |
74 | INIT_LIST_HEAD(&peer->error_targets); | 185 | INIT_LIST_HEAD(&peer->error_targets); |
75 | spin_lock_init(&peer->lock); | 186 | spin_lock_init(&peer->lock); |
76 | atomic_set(&peer->usage, 1); | ||
77 | peer->debug_id = atomic_inc_return(&rxrpc_debug_id); | 187 | peer->debug_id = atomic_inc_return(&rxrpc_debug_id); |
188 | } | ||
189 | |||
190 | _leave(" = %p", peer); | ||
191 | return peer; | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * Set up a new peer. | ||
196 | */ | ||
197 | static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_local *local, | ||
198 | struct sockaddr_rxrpc *srx, | ||
199 | unsigned long hash_key, | ||
200 | gfp_t gfp) | ||
201 | { | ||
202 | struct rxrpc_peer *peer; | ||
203 | |||
204 | _enter(""); | ||
205 | |||
206 | peer = rxrpc_alloc_peer(local, gfp); | ||
207 | if (peer) { | ||
208 | peer->hash_key = hash_key; | ||
78 | memcpy(&peer->srx, srx, sizeof(*srx)); | 209 | memcpy(&peer->srx, srx, sizeof(*srx)); |
79 | 210 | ||
80 | rxrpc_assess_MTU_size(peer); | 211 | rxrpc_assess_MTU_size(peer); |
@@ -105,11 +236,11 @@ static struct rxrpc_peer *rxrpc_alloc_peer(struct sockaddr_rxrpc *srx, | |||
105 | /* | 236 | /* |
106 | * obtain a remote transport endpoint for the specified address | 237 | * obtain a remote transport endpoint for the specified address |
107 | */ | 238 | */ |
108 | struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp) | 239 | struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local, |
240 | struct sockaddr_rxrpc *srx, gfp_t gfp) | ||
109 | { | 241 | { |
110 | struct rxrpc_peer *peer, *candidate; | 242 | struct rxrpc_peer *peer, *candidate; |
111 | const char *new = "old"; | 243 | unsigned long hash_key = rxrpc_peer_hash_key(local, srx); |
112 | int usage; | ||
113 | 244 | ||
114 | _enter("{%d,%d,%pI4+%hu}", | 245 | _enter("{%d,%d,%pI4+%hu}", |
115 | srx->transport_type, | 246 | srx->transport_type, |
@@ -118,188 +249,60 @@ struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp) | |||
118 | ntohs(srx->transport.sin.sin_port)); | 249 | ntohs(srx->transport.sin.sin_port)); |
119 | 250 | ||
120 | /* search the peer list first */ | 251 | /* search the peer list first */ |
121 | read_lock_bh(&rxrpc_peer_lock); | 252 | rcu_read_lock(); |
122 | list_for_each_entry(peer, &rxrpc_peers, link) { | 253 | peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key); |
123 | _debug("check PEER %d { u=%d t=%d l=%d }", | 254 | if (peer && !rxrpc_get_peer_maybe(peer)) |
124 | peer->debug_id, | 255 | peer = NULL; |
125 | atomic_read(&peer->usage), | 256 | rcu_read_unlock(); |
126 | peer->srx.transport_type, | 257 | |
127 | peer->srx.transport_len); | 258 | if (!peer) { |
128 | 259 | /* The peer is not yet present in hash - create a candidate | |
129 | if (atomic_read(&peer->usage) > 0 && | 260 | * for a new record and then redo the search. |
130 | peer->srx.transport_type == srx->transport_type && | 261 | */ |
131 | peer->srx.transport_len == srx->transport_len && | 262 | candidate = rxrpc_create_peer(local, srx, hash_key, gfp); |
132 | memcmp(&peer->srx.transport, | 263 | if (!candidate) { |
133 | &srx->transport, | 264 | _leave(" = NULL [nomem]"); |
134 | srx->transport_len) == 0) | 265 | return NULL; |
135 | goto found_extant_peer; | 266 | } |
136 | } | ||
137 | read_unlock_bh(&rxrpc_peer_lock); | ||
138 | |||
139 | /* not yet present - create a candidate for a new record and then | ||
140 | * redo the search */ | ||
141 | candidate = rxrpc_alloc_peer(srx, gfp); | ||
142 | if (!candidate) { | ||
143 | _leave(" = -ENOMEM"); | ||
144 | return ERR_PTR(-ENOMEM); | ||
145 | } | ||
146 | 267 | ||
147 | write_lock_bh(&rxrpc_peer_lock); | 268 | spin_lock(&rxrpc_peer_hash_lock); |
148 | 269 | ||
149 | list_for_each_entry(peer, &rxrpc_peers, link) { | 270 | /* Need to check that we aren't racing with someone else */ |
150 | if (atomic_read(&peer->usage) > 0 && | 271 | peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key); |
151 | peer->srx.transport_type == srx->transport_type && | 272 | if (peer && !rxrpc_get_peer_maybe(peer)) |
152 | peer->srx.transport_len == srx->transport_len && | 273 | peer = NULL; |
153 | memcmp(&peer->srx.transport, | 274 | if (!peer) |
154 | &srx->transport, | 275 | hash_add_rcu(rxrpc_peer_hash, |
155 | srx->transport_len) == 0) | 276 | &candidate->hash_link, hash_key); |
156 | goto found_extant_second; | ||
157 | } | ||
158 | 277 | ||
159 | /* we can now add the new candidate to the list */ | 278 | spin_unlock(&rxrpc_peer_hash_lock); |
160 | peer = candidate; | ||
161 | candidate = NULL; | ||
162 | usage = atomic_read(&peer->usage); | ||
163 | 279 | ||
164 | list_add_tail(&peer->link, &rxrpc_peers); | 280 | if (peer) |
165 | write_unlock_bh(&rxrpc_peer_lock); | 281 | kfree(candidate); |
166 | new = "new"; | 282 | else |
283 | peer = candidate; | ||
284 | } | ||
167 | 285 | ||
168 | success: | 286 | _net("PEER %d {%d,%pI4+%hu}", |
169 | _net("PEER %s %d {%d,%u,%pI4+%hu}", | ||
170 | new, | ||
171 | peer->debug_id, | 287 | peer->debug_id, |
172 | peer->srx.transport_type, | 288 | peer->srx.transport_type, |
173 | peer->srx.transport.family, | ||
174 | &peer->srx.transport.sin.sin_addr, | 289 | &peer->srx.transport.sin.sin_addr, |
175 | ntohs(peer->srx.transport.sin.sin_port)); | 290 | ntohs(peer->srx.transport.sin.sin_port)); |
176 | 291 | ||
177 | _leave(" = %p {u=%d}", peer, usage); | 292 | _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage)); |
178 | return peer; | ||
179 | |||
180 | /* we found the peer in the list immediately */ | ||
181 | found_extant_peer: | ||
182 | usage = atomic_inc_return(&peer->usage); | ||
183 | read_unlock_bh(&rxrpc_peer_lock); | ||
184 | goto success; | ||
185 | |||
186 | /* we found the peer on the second time through the list */ | ||
187 | found_extant_second: | ||
188 | usage = atomic_inc_return(&peer->usage); | ||
189 | write_unlock_bh(&rxrpc_peer_lock); | ||
190 | kfree(candidate); | ||
191 | goto success; | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * find the peer associated with a packet | ||
196 | */ | ||
197 | struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *local, | ||
198 | __be32 addr, __be16 port) | ||
199 | { | ||
200 | struct rxrpc_peer *peer; | ||
201 | |||
202 | _enter(""); | ||
203 | |||
204 | /* search the peer list */ | ||
205 | read_lock_bh(&rxrpc_peer_lock); | ||
206 | |||
207 | if (local->srx.transport.family == AF_INET && | ||
208 | local->srx.transport_type == SOCK_DGRAM | ||
209 | ) { | ||
210 | list_for_each_entry(peer, &rxrpc_peers, link) { | ||
211 | if (atomic_read(&peer->usage) > 0 && | ||
212 | peer->srx.transport_type == SOCK_DGRAM && | ||
213 | peer->srx.transport.family == AF_INET && | ||
214 | peer->srx.transport.sin.sin_port == port && | ||
215 | peer->srx.transport.sin.sin_addr.s_addr == addr) | ||
216 | goto found_UDP_peer; | ||
217 | } | ||
218 | |||
219 | goto new_UDP_peer; | ||
220 | } | ||
221 | |||
222 | read_unlock_bh(&rxrpc_peer_lock); | ||
223 | _leave(" = -EAFNOSUPPORT"); | ||
224 | return ERR_PTR(-EAFNOSUPPORT); | ||
225 | |||
226 | found_UDP_peer: | ||
227 | _net("Rx UDP DGRAM from peer %d", peer->debug_id); | ||
228 | atomic_inc(&peer->usage); | ||
229 | read_unlock_bh(&rxrpc_peer_lock); | ||
230 | _leave(" = %p", peer); | ||
231 | return peer; | 293 | return peer; |
232 | |||
233 | new_UDP_peer: | ||
234 | _net("Rx UDP DGRAM from NEW peer"); | ||
235 | read_unlock_bh(&rxrpc_peer_lock); | ||
236 | _leave(" = -EBUSY [new]"); | ||
237 | return ERR_PTR(-EBUSY); | ||
238 | } | 294 | } |
239 | 295 | ||
240 | /* | 296 | /* |
241 | * release a remote transport endpoint | 297 | * Discard a ref on a remote peer record. |
242 | */ | 298 | */ |
243 | void rxrpc_put_peer(struct rxrpc_peer *peer) | 299 | void __rxrpc_put_peer(struct rxrpc_peer *peer) |
244 | { | 300 | { |
245 | _enter("%p{u=%d}", peer, atomic_read(&peer->usage)); | 301 | ASSERT(list_empty(&peer->error_targets)); |
246 | 302 | ||
247 | ASSERTCMP(atomic_read(&peer->usage), >, 0); | 303 | spin_lock(&rxrpc_peer_hash_lock); |
248 | 304 | hash_del_rcu(&peer->hash_link); | |
249 | if (likely(!atomic_dec_and_test(&peer->usage))) { | 305 | spin_unlock(&rxrpc_peer_hash_lock); |
250 | _leave(" [in use]"); | ||
251 | return; | ||
252 | } | ||
253 | |||
254 | rxrpc_queue_work(&peer->destroyer); | ||
255 | _leave(""); | ||
256 | } | ||
257 | |||
258 | /* | ||
259 | * destroy a remote transport endpoint | ||
260 | */ | ||
261 | static void rxrpc_destroy_peer(struct work_struct *work) | ||
262 | { | ||
263 | struct rxrpc_peer *peer = | ||
264 | container_of(work, struct rxrpc_peer, destroyer); | ||
265 | |||
266 | _enter("%p{%d}", peer, atomic_read(&peer->usage)); | ||
267 | |||
268 | write_lock_bh(&rxrpc_peer_lock); | ||
269 | list_del(&peer->link); | ||
270 | write_unlock_bh(&rxrpc_peer_lock); | ||
271 | |||
272 | _net("DESTROY PEER %d", peer->debug_id); | ||
273 | kfree(peer); | ||
274 | |||
275 | if (list_empty(&rxrpc_peers)) | ||
276 | wake_up_all(&rxrpc_peer_wq); | ||
277 | _leave(""); | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * preemptively destroy all the peer records from a transport endpoint rather | ||
282 | * than waiting for them to time out | ||
283 | */ | ||
284 | void __exit rxrpc_destroy_all_peers(void) | ||
285 | { | ||
286 | DECLARE_WAITQUEUE(myself,current); | ||
287 | |||
288 | _enter(""); | ||
289 | |||
290 | /* we simply have to wait for them to go away */ | ||
291 | if (!list_empty(&rxrpc_peers)) { | ||
292 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
293 | add_wait_queue(&rxrpc_peer_wq, &myself); | ||
294 | |||
295 | while (!list_empty(&rxrpc_peers)) { | ||
296 | schedule(); | ||
297 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
298 | } | ||
299 | |||
300 | remove_wait_queue(&rxrpc_peer_wq, &myself); | ||
301 | set_current_state(TASK_RUNNING); | ||
302 | } | ||
303 | 306 | ||
304 | _leave(""); | 307 | kfree_rcu(peer, rcu); |
305 | } | 308 | } |
diff --git a/net/rxrpc/transport.c b/net/rxrpc/transport.c index a1b65183b07d..d33387dec0ce 100644 --- a/net/rxrpc/transport.c +++ b/net/rxrpc/transport.c | |||
@@ -121,7 +121,7 @@ struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *local, | |||
121 | usage = atomic_read(&trans->usage); | 121 | usage = atomic_read(&trans->usage); |
122 | 122 | ||
123 | rxrpc_get_local(trans->local); | 123 | rxrpc_get_local(trans->local); |
124 | atomic_inc(&trans->peer->usage); | 124 | rxrpc_get_peer(trans->peer); |
125 | list_add_tail(&trans->link, &rxrpc_transports); | 125 | list_add_tail(&trans->link, &rxrpc_transports); |
126 | write_unlock_bh(&rxrpc_transport_lock); | 126 | write_unlock_bh(&rxrpc_transport_lock); |
127 | new = "new"; | 127 | new = "new"; |
diff --git a/net/rxrpc/utils.c b/net/rxrpc/utils.c new file mode 100644 index 000000000000..f28122a15a24 --- /dev/null +++ b/net/rxrpc/utils.c | |||
@@ -0,0 +1,41 @@ | |||
1 | /* Utility routines | ||
2 | * | ||
3 | * Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/ip.h> | ||
13 | #include <linux/udp.h> | ||
14 | #include "ar-internal.h" | ||
15 | |||
16 | /* | ||
17 | * Set up an RxRPC address from a socket buffer. | ||
18 | */ | ||
19 | void rxrpc_get_addr_from_skb(struct rxrpc_local *local, | ||
20 | const struct sk_buff *skb, | ||
21 | struct sockaddr_rxrpc *srx) | ||
22 | { | ||
23 | memset(srx, 0, sizeof(*srx)); | ||
24 | srx->transport_type = local->srx.transport_type; | ||
25 | srx->transport.family = local->srx.transport.family; | ||
26 | |||
27 | /* Can we see an ipv4 UDP packet on an ipv6 UDP socket? and vice | ||
28 | * versa? | ||
29 | */ | ||
30 | switch (srx->transport.family) { | ||
31 | case AF_INET: | ||
32 | srx->transport.sin.sin_port = udp_hdr(skb)->source; | ||
33 | srx->transport_len = sizeof(struct sockaddr_in); | ||
34 | memcpy(&srx->transport.sin.sin_addr, &ip_hdr(skb)->saddr, | ||
35 | sizeof(struct in_addr)); | ||
36 | break; | ||
37 | |||
38 | default: | ||
39 | BUG(); | ||
40 | } | ||
41 | } | ||