Commit | Line | Data |
---|---|---|
b4d0d230 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
87563616 | 2 | /* Local endpoint object management |
17926a79 | 3 | * |
4f95dd78 | 4 | * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. |
17926a79 | 5 | * Written by David Howells (dhowells@redhat.com) |
17926a79 DH |
6 | */ |
7 | ||
9b6d5398 JP |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | ||
17926a79 DH |
10 | #include <linux/module.h> |
11 | #include <linux/net.h> | |
12 | #include <linux/skbuff.h> | |
5a0e3ad6 | 13 | #include <linux/slab.h> |
44ba0698 DH |
14 | #include <linux/udp.h> |
15 | #include <linux/ip.h> | |
4f95dd78 | 16 | #include <linux/hashtable.h> |
17926a79 | 17 | #include <net/sock.h> |
5271953c | 18 | #include <net/udp.h> |
5d30c626 | 19 | #include <net/udp_tunnel.h> |
17926a79 DH |
20 | #include <net/af_rxrpc.h> |
21 | #include "ar-internal.h" | |
22 | ||
4f95dd78 | 23 | static void rxrpc_local_rcu(struct rcu_head *); |
17926a79 | 24 | |
b6c66c43 DH |
25 | /* |
26 | * Handle an ICMP/ICMP6 error turning up at the tunnel. Push it through the | |
27 | * usual mechanism so that it gets parsed and presented through the UDP | |
28 | * socket's error_report(). | |
29 | */ | |
30 | static void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err, | |
31 | __be16 port, u32 info, u8 *payload) | |
32 | { | |
33 | if (ip_hdr(skb)->version == IPVERSION) | |
34 | return ip_icmp_error(sk, skb, err, port, info, payload); | |
41cf3a91 DH |
35 | if (IS_ENABLED(CONFIG_AF_RXRPC_IPV6)) |
36 | return ipv6_icmp_error(sk, skb, err, port, info, payload); | |
b6c66c43 DH |
37 | } |
38 | ||
87220143 DH |
39 | /* |
40 | * Set or clear the Don't Fragment flag on a socket. | |
41 | */ | |
42 | void rxrpc_local_dont_fragment(const struct rxrpc_local *local, bool set) | |
43 | { | |
44 | if (set) | |
45 | ip_sock_set_mtu_discover(local->socket->sk, IP_PMTUDISC_DO); | |
46 | else | |
47 | ip_sock_set_mtu_discover(local->socket->sk, IP_PMTUDISC_DONT); | |
48 | } | |
49 | ||
17926a79 | 50 | /* |
4f95dd78 DH |
51 | * Compare a local to an address. Return -ve, 0 or +ve to indicate less than, |
52 | * same or greater than. | |
53 | * | |
54 | * We explicitly don't compare the RxRPC service ID as we want to reject | |
55 | * conflicting uses by differing services. Further, we don't want to share | |
56 | * addresses with different options (IPv6), so we don't compare those bits | |
57 | * either. | |
17926a79 | 58 | */ |
4f95dd78 DH |
59 | static long rxrpc_local_cmp_key(const struct rxrpc_local *local, |
60 | const struct sockaddr_rxrpc *srx) | |
61 | { | |
62 | long diff; | |
63 | ||
64 | diff = ((local->srx.transport_type - srx->transport_type) ?: | |
65 | (local->srx.transport_len - srx->transport_len) ?: | |
66 | (local->srx.transport.family - srx->transport.family)); | |
67 | if (diff != 0) | |
68 | return diff; | |
69 | ||
70 | switch (srx->transport.family) { | |
71 | case AF_INET: | |
72 | /* If the choice of UDP port is left up to the transport, then | |
73 | * the endpoint record doesn't match. | |
74 | */ | |
75 | return ((u16 __force)local->srx.transport.sin.sin_port - | |
76 | (u16 __force)srx->transport.sin.sin_port) ?: | |
77 | memcmp(&local->srx.transport.sin.sin_addr, | |
78 | &srx->transport.sin.sin_addr, | |
79 | sizeof(struct in_addr)); | |
d1912747 | 80 | #ifdef CONFIG_AF_RXRPC_IPV6 |
75b54cb5 DH |
81 | case AF_INET6: |
82 | /* If the choice of UDP6 port is left up to the transport, then | |
83 | * the endpoint record doesn't match. | |
84 | */ | |
85 | return ((u16 __force)local->srx.transport.sin6.sin6_port - | |
86 | (u16 __force)srx->transport.sin6.sin6_port) ?: | |
87 | memcmp(&local->srx.transport.sin6.sin6_addr, | |
88 | &srx->transport.sin6.sin6_addr, | |
89 | sizeof(struct in6_addr)); | |
d1912747 | 90 | #endif |
4f95dd78 DH |
91 | default: |
92 | BUG(); | |
93 | } | |
94 | } | |
95 | ||
0d6bf319 DH |
96 | static void rxrpc_client_conn_reap_timeout(struct timer_list *timer) |
97 | { | |
98 | struct rxrpc_local *local = | |
99 | container_of(timer, struct rxrpc_local, client_conn_reap_timer); | |
100 | ||
61e4a866 | 101 | if (!local->kill_all_client_conns && |
0d6bf319 DH |
102 | test_and_set_bit(RXRPC_CLIENT_CONN_REAP_TIMER, &local->client_conn_flags)) |
103 | rxrpc_wake_up_io_thread(local); | |
104 | } | |
105 | ||
4f95dd78 DH |
106 | /* |
107 | * Allocate a new local endpoint. | |
108 | */ | |
8a758d98 | 109 | static struct rxrpc_local *rxrpc_alloc_local(struct net *net, |
2baec2c3 | 110 | const struct sockaddr_rxrpc *srx) |
17926a79 DH |
111 | { |
112 | struct rxrpc_local *local; | |
f06cb291 | 113 | u32 tmp; |
17926a79 DH |
114 | |
115 | local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL); | |
116 | if (local) { | |
a0575429 | 117 | refcount_set(&local->ref, 1); |
730c5fd4 | 118 | atomic_set(&local->active_users, 1); |
8a758d98 DH |
119 | local->net = net; |
120 | local->rxnet = rxrpc_net(net); | |
33912c26 | 121 | INIT_HLIST_NODE(&local->link); |
8fbcc833 | 122 | init_completion(&local->io_thread_ready); |
af094824 DH |
123 | #ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY |
124 | skb_queue_head_init(&local->rx_delay_queue); | |
125 | #endif | |
a275da62 | 126 | skb_queue_head_init(&local->rx_queue); |
f2cce89a | 127 | INIT_LIST_HEAD(&local->conn_attend_q); |
15f661dc | 128 | INIT_LIST_HEAD(&local->call_attend_q); |
0d6bf319 | 129 | |
245500d8 DH |
130 | local->client_bundles = RB_ROOT; |
131 | spin_lock_init(&local->client_bundles_lock); | |
0d6bf319 | 132 | local->kill_all_client_conns = false; |
0d6bf319 DH |
133 | INIT_LIST_HEAD(&local->idle_client_conns); |
134 | timer_setup(&local->client_conn_reap_timer, | |
135 | rxrpc_client_conn_reap_timeout, 0); | |
136 | ||
17926a79 DH |
137 | spin_lock_init(&local->lock); |
138 | rwlock_init(&local->services_lock); | |
17926a79 DH |
139 | local->debug_id = atomic_inc_return(&rxrpc_debug_id); |
140 | memcpy(&local->srx, srx, sizeof(*srx)); | |
28036f44 | 141 | local->srx.srx_service = 0; |
f06cb291 DH |
142 | idr_init(&local->conn_ids); |
143 | get_random_bytes(&tmp, sizeof(tmp)); | |
144 | tmp &= 0x3fffffff; | |
145 | if (tmp == 0) | |
146 | tmp = 1; | |
147 | idr_set_cursor(&local->conn_ids, tmp); | |
9d35d880 DH |
148 | INIT_LIST_HEAD(&local->new_client_calls); |
149 | spin_lock_init(&local->client_call_lock); | |
f06cb291 | 150 | |
0fde882f | 151 | trace_rxrpc_local(local->debug_id, rxrpc_local_new, 1, 1); |
17926a79 DH |
152 | } |
153 | ||
154 | _leave(" = %p", local); | |
155 | return local; | |
156 | } | |
157 | ||
158 | /* | |
159 | * create the local socket | |
4f95dd78 | 160 | * - must be called with rxrpc_local_mutex locked |
17926a79 | 161 | */ |
2baec2c3 | 162 | static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net) |
17926a79 | 163 | { |
1a9b86c9 XL |
164 | struct udp_tunnel_sock_cfg tuncfg = {NULL}; |
165 | struct sockaddr_rxrpc *srx = &local->srx; | |
166 | struct udp_port_cfg udp_conf = {0}; | |
a275da62 | 167 | struct task_struct *io_thread; |
5271953c | 168 | struct sock *usk; |
fce93494 | 169 | int ret; |
17926a79 | 170 | |
75b54cb5 | 171 | _enter("%p{%d,%d}", |
1a9b86c9 XL |
172 | local, srx->transport_type, srx->transport.family); |
173 | ||
174 | udp_conf.family = srx->transport.family; | |
39cb9faa | 175 | udp_conf.use_udp_checksums = true; |
1a9b86c9 XL |
176 | if (udp_conf.family == AF_INET) { |
177 | udp_conf.local_ip = srx->transport.sin.sin_addr; | |
178 | udp_conf.local_udp_port = srx->transport.sin.sin_port; | |
295f830e | 179 | #if IS_ENABLED(CONFIG_AF_RXRPC_IPV6) |
1a9b86c9 XL |
180 | } else { |
181 | udp_conf.local_ip6 = srx->transport.sin6.sin6_addr; | |
182 | udp_conf.local_udp_port = srx->transport.sin6.sin6_port; | |
39cb9faa DH |
183 | udp_conf.use_udp6_tx_checksums = true; |
184 | udp_conf.use_udp6_rx_checksums = true; | |
295f830e | 185 | #endif |
1a9b86c9 XL |
186 | } |
187 | ret = udp_sock_create(net, &udp_conf, &local->socket); | |
17926a79 DH |
188 | if (ret < 0) { |
189 | _leave(" = %d [socket]", ret); | |
190 | return ret; | |
191 | } | |
192 | ||
1a9b86c9 | 193 | tuncfg.encap_type = UDP_ENCAP_RXRPC; |
446b3e14 | 194 | tuncfg.encap_rcv = rxrpc_encap_rcv; |
ac56a0b4 | 195 | tuncfg.encap_err_rcv = rxrpc_encap_err_rcv; |
1a9b86c9 XL |
196 | tuncfg.sk_user_data = local; |
197 | setup_udp_tunnel_sock(net, local->socket, &tuncfg); | |
198 | ||
2cfa2271 | 199 | /* set the socket up */ |
5271953c | 200 | usk = local->socket->sk; |
5271953c | 201 | usk->sk_error_report = rxrpc_error_report; |
2cfa2271 | 202 | |
1a9b86c9 | 203 | switch (srx->transport.family) { |
37a675e7 DH |
204 | case AF_INET6: |
205 | /* we want to receive ICMPv6 errors */ | |
1a9b86c9 | 206 | ip6_sock_set_recverr(usk); |
17926a79 | 207 | |
37a675e7 DH |
208 | /* Fall through and set IPv4 options too otherwise we don't get |
209 | * errors from IPv4 packets sent through the IPv6 socket. | |
210 | */ | |
df561f66 | 211 | fallthrough; |
37a675e7 | 212 | case AF_INET: |
f2aeed3a | 213 | /* we want to receive ICMP errors */ |
1a9b86c9 | 214 | ip_sock_set_recverr(usk); |
f2aeed3a DH |
215 | |
216 | /* we want to set the don't fragment bit */ | |
87220143 | 217 | rxrpc_local_dont_fragment(local, true); |
f2aeed3a DH |
218 | break; |
219 | ||
220 | default: | |
221 | BUG(); | |
17926a79 DH |
222 | } |
223 | ||
a275da62 DH |
224 | io_thread = kthread_run(rxrpc_io_thread, local, |
225 | "krxrpcio/%u", ntohs(udp_conf.local_udp_port)); | |
226 | if (IS_ERR(io_thread)) { | |
227 | ret = PTR_ERR(io_thread); | |
228 | goto error_sock; | |
229 | } | |
230 | ||
8fbcc833 | 231 | wait_for_completion(&local->io_thread_ready); |
bc212465 | 232 | WRITE_ONCE(local->io_thread, io_thread); |
17926a79 DH |
233 | _leave(" = 0"); |
234 | return 0; | |
a275da62 DH |
235 | |
236 | error_sock: | |
237 | kernel_sock_shutdown(local->socket, SHUT_RDWR); | |
238 | local->socket->sk->sk_user_data = NULL; | |
239 | sock_release(local->socket); | |
240 | local->socket = NULL; | |
241 | return ret; | |
17926a79 DH |
242 | } |
243 | ||
244 | /* | |
4f95dd78 | 245 | * Look up or create a new local endpoint using the specified local address. |
17926a79 | 246 | */ |
2baec2c3 DH |
247 | struct rxrpc_local *rxrpc_lookup_local(struct net *net, |
248 | const struct sockaddr_rxrpc *srx) | |
17926a79 DH |
249 | { |
250 | struct rxrpc_local *local; | |
2baec2c3 | 251 | struct rxrpc_net *rxnet = rxrpc_net(net); |
33912c26 | 252 | struct hlist_node *cursor; |
4f95dd78 | 253 | long diff; |
17926a79 DH |
254 | int ret; |
255 | ||
75b54cb5 DH |
256 | _enter("{%d,%d,%pISp}", |
257 | srx->transport_type, srx->transport.family, &srx->transport); | |
17926a79 | 258 | |
2baec2c3 | 259 | mutex_lock(&rxnet->local_mutex); |
17926a79 | 260 | |
33912c26 DH |
261 | hlist_for_each(cursor, &rxnet->local_endpoints) { |
262 | local = hlist_entry(cursor, struct rxrpc_local, link); | |
17926a79 | 263 | |
4f95dd78 | 264 | diff = rxrpc_local_cmp_key(local, srx); |
33912c26 | 265 | if (diff != 0) |
17926a79 | 266 | continue; |
4f95dd78 DH |
267 | |
268 | /* Services aren't allowed to share transport sockets, so | |
269 | * reject that here. It is possible that the object is dying - | |
270 | * but it may also still have the local transport address that | |
271 | * we want bound. | |
272 | */ | |
273 | if (srx->srx_service) { | |
274 | local = NULL; | |
275 | goto addr_in_use; | |
276 | } | |
17926a79 | 277 | |
33912c26 DH |
278 | /* Found a match. We want to replace a dying object. |
279 | * Attempting to bind the transport socket may still fail if | |
280 | * we're attempting to use a local address that the dying | |
281 | * object is still using. | |
4f95dd78 | 282 | */ |
0fde882f | 283 | if (!rxrpc_use_local(local, rxrpc_local_use_lookup)) |
4f95dd78 | 284 | break; |
17926a79 | 285 | |
4f95dd78 DH |
286 | goto found; |
287 | } | |
17926a79 | 288 | |
8a758d98 | 289 | local = rxrpc_alloc_local(net, srx); |
4f95dd78 DH |
290 | if (!local) |
291 | goto nomem; | |
17926a79 | 292 | |
2baec2c3 | 293 | ret = rxrpc_open_socket(local, net); |
4f95dd78 DH |
294 | if (ret < 0) |
295 | goto sock_error; | |
296 | ||
33912c26 DH |
297 | if (cursor) { |
298 | hlist_replace_rcu(cursor, &local->link); | |
299 | cursor->pprev = NULL; | |
300 | } else { | |
301 | hlist_add_head_rcu(&local->link, &rxnet->local_endpoints); | |
302 | } | |
17926a79 | 303 | |
4f95dd78 | 304 | found: |
2baec2c3 | 305 | mutex_unlock(&rxnet->local_mutex); |
4f95dd78 | 306 | _leave(" = %p", local); |
17926a79 DH |
307 | return local; |
308 | ||
4f95dd78 DH |
309 | nomem: |
310 | ret = -ENOMEM; | |
311 | sock_error: | |
2baec2c3 | 312 | mutex_unlock(&rxnet->local_mutex); |
032be5f1 ED |
313 | if (local) |
314 | call_rcu(&local->rcu, rxrpc_local_rcu); | |
4f95dd78 DH |
315 | _leave(" = %d", ret); |
316 | return ERR_PTR(ret); | |
17926a79 | 317 | |
4f95dd78 | 318 | addr_in_use: |
2baec2c3 | 319 | mutex_unlock(&rxnet->local_mutex); |
4f95dd78 DH |
320 | _leave(" = -EADDRINUSE"); |
321 | return ERR_PTR(-EADDRINUSE); | |
322 | } | |
17926a79 | 323 | |
09d2bf59 DH |
324 | /* |
325 | * Get a ref on a local endpoint. | |
326 | */ | |
0fde882f DH |
327 | struct rxrpc_local *rxrpc_get_local(struct rxrpc_local *local, |
328 | enum rxrpc_local_trace why) | |
09d2bf59 | 329 | { |
0fde882f | 330 | int r, u; |
09d2bf59 | 331 | |
0fde882f | 332 | u = atomic_read(&local->active_users); |
a0575429 | 333 | __refcount_inc(&local->ref, &r); |
0fde882f | 334 | trace_rxrpc_local(local->debug_id, why, r + 1, u); |
09d2bf59 DH |
335 | return local; |
336 | } | |
337 | ||
338 | /* | |
339 | * Get a ref on a local endpoint unless its usage has already reached 0. | |
340 | */ | |
0fde882f DH |
341 | struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local, |
342 | enum rxrpc_local_trace why) | |
09d2bf59 | 343 | { |
0fde882f | 344 | int r, u; |
09d2bf59 | 345 | |
0fde882f DH |
346 | if (local && __refcount_inc_not_zero(&local->ref, &r)) { |
347 | u = atomic_read(&local->active_users); | |
348 | trace_rxrpc_local(local->debug_id, why, r + 1, u); | |
349 | return local; | |
09d2bf59 | 350 | } |
0fde882f DH |
351 | |
352 | return NULL; | |
09d2bf59 DH |
353 | } |
354 | ||
09d2bf59 DH |
355 | /* |
356 | * Drop a ref on a local endpoint. | |
357 | */ | |
0fde882f | 358 | void rxrpc_put_local(struct rxrpc_local *local, enum rxrpc_local_trace why) |
09d2bf59 | 359 | { |
fac20b9e | 360 | unsigned int debug_id; |
a0575429 | 361 | bool dead; |
0fde882f | 362 | int r, u; |
09d2bf59 DH |
363 | |
364 | if (local) { | |
fac20b9e DH |
365 | debug_id = local->debug_id; |
366 | ||
0fde882f | 367 | u = atomic_read(&local->active_users); |
a0575429 | 368 | dead = __refcount_dec_and_test(&local->ref, &r); |
0fde882f | 369 | trace_rxrpc_local(debug_id, why, r, u); |
09d2bf59 | 370 | |
a0575429 | 371 | if (dead) |
730c5fd4 | 372 | call_rcu(&local->rcu, rxrpc_local_rcu); |
09d2bf59 DH |
373 | } |
374 | } | |
375 | ||
730c5fd4 DH |
376 | /* |
377 | * Start using a local endpoint. | |
378 | */ | |
0fde882f DH |
379 | struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local, |
380 | enum rxrpc_local_trace why) | |
730c5fd4 | 381 | { |
0fde882f | 382 | local = rxrpc_get_local_maybe(local, rxrpc_local_get_for_use); |
730c5fd4 DH |
383 | if (!local) |
384 | return NULL; | |
385 | ||
0fde882f DH |
386 | if (!__rxrpc_use_local(local, why)) { |
387 | rxrpc_put_local(local, rxrpc_local_put_for_use); | |
730c5fd4 DH |
388 | return NULL; |
389 | } | |
390 | ||
391 | return local; | |
392 | } | |
393 | ||
394 | /* | |
395 | * Cease using a local endpoint. Once the number of active users reaches 0, we | |
5e6ef4f1 | 396 | * start the closure of the transport in the I/O thread.. |
730c5fd4 | 397 | */ |
0fde882f | 398 | void rxrpc_unuse_local(struct rxrpc_local *local, enum rxrpc_local_trace why) |
730c5fd4 | 399 | { |
eaa02390 | 400 | unsigned int debug_id; |
a2cf3264 DH |
401 | int r, u; |
402 | ||
403 | if (local) { | |
eaa02390 | 404 | debug_id = local->debug_id; |
a2cf3264 DH |
405 | r = refcount_read(&local->ref); |
406 | u = atomic_dec_return(&local->active_users); | |
407 | trace_rxrpc_local(debug_id, why, r, u); | |
408 | if (u == 0) | |
409 | kthread_stop(local->io_thread); | |
410 | } | |
730c5fd4 DH |
411 | } |
412 | ||
17926a79 | 413 | /* |
4f95dd78 DH |
414 | * Destroy a local endpoint's socket and then hand the record to RCU to dispose |
415 | * of. | |
416 | * | |
417 | * Closing the socket cannot be done from bottom half context or RCU callback | |
418 | * context because it might sleep. | |
17926a79 | 419 | */ |
a275da62 | 420 | void rxrpc_destroy_local(struct rxrpc_local *local) |
17926a79 | 421 | { |
4f95dd78 | 422 | struct socket *socket = local->socket; |
2baec2c3 | 423 | struct rxrpc_net *rxnet = local->rxnet; |
17926a79 | 424 | |
4f95dd78 | 425 | _enter("%d", local->debug_id); |
17926a79 | 426 | |
d12040b6 DH |
427 | local->dead = true; |
428 | ||
2baec2c3 | 429 | mutex_lock(&rxnet->local_mutex); |
33912c26 | 430 | hlist_del_init_rcu(&local->link); |
2baec2c3 | 431 | mutex_unlock(&rxnet->local_mutex); |
4f95dd78 | 432 | |
d12040b6 DH |
433 | rxrpc_clean_up_local_conns(local); |
434 | rxrpc_service_connection_reaper(&rxnet->service_conn_reaper); | |
1e9e5c95 | 435 | ASSERT(!local->service); |
4f95dd78 DH |
436 | |
437 | if (socket) { | |
438 | local->socket = NULL; | |
439 | kernel_sock_shutdown(socket, SHUT_RDWR); | |
440 | socket->sk->sk_user_data = NULL; | |
441 | sock_release(socket); | |
442 | } | |
443 | ||
444 | /* At this point, there should be no more packets coming in to the | |
445 | * local endpoint. | |
446 | */ | |
af094824 DH |
447 | #ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY |
448 | rxrpc_purge_queue(&local->rx_delay_queue); | |
449 | #endif | |
a275da62 | 450 | rxrpc_purge_queue(&local->rx_queue); |
9d35d880 | 451 | rxrpc_purge_client_connections(local); |
3d18dfe6 | 452 | page_frag_cache_drain(&local->tx_alloc); |
17926a79 DH |
453 | } |
454 | ||
4f95dd78 DH |
455 | /* |
456 | * Destroy a local endpoint after the RCU grace period expires. | |
457 | */ | |
458 | static void rxrpc_local_rcu(struct rcu_head *rcu) | |
459 | { | |
460 | struct rxrpc_local *local = container_of(rcu, struct rxrpc_local, rcu); | |
17926a79 | 461 | |
0fde882f | 462 | rxrpc_see_local(local, rxrpc_local_free); |
17926a79 | 463 | kfree(local); |
17926a79 DH |
464 | } |
465 | ||
466 | /* | |
4f95dd78 | 467 | * Verify the local endpoint list is empty by this point. |
17926a79 | 468 | */ |
2baec2c3 | 469 | void rxrpc_destroy_all_locals(struct rxrpc_net *rxnet) |
17926a79 | 470 | { |
4f95dd78 | 471 | struct rxrpc_local *local; |
17926a79 DH |
472 | |
473 | _enter(""); | |
474 | ||
dee46364 | 475 | flush_workqueue(rxrpc_workqueue); |
17926a79 | 476 | |
33912c26 | 477 | if (!hlist_empty(&rxnet->local_endpoints)) { |
2baec2c3 | 478 | mutex_lock(&rxnet->local_mutex); |
33912c26 | 479 | hlist_for_each_entry(local, &rxnet->local_endpoints, link) { |
dee46364 | 480 | pr_err("AF_RXRPC: Leaked local %p {%d}\n", |
a0575429 | 481 | local, refcount_read(&local->ref)); |
dee46364 | 482 | } |
2baec2c3 | 483 | mutex_unlock(&rxnet->local_mutex); |
dee46364 | 484 | BUG(); |
17926a79 | 485 | } |
17926a79 | 486 | } |