.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* Local endpoint object management |
---|
2 | 3 | * |
---|
3 | 4 | * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. |
---|
4 | 5 | * 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 | 6 | */ |
---|
11 | 7 | |
---|
12 | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
.. | .. |
---|
82 | 78 | |
---|
83 | 79 | local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL); |
---|
84 | 80 | if (local) { |
---|
85 | | - atomic_set(&local->usage, 1); |
---|
| 81 | + refcount_set(&local->ref, 1); |
---|
86 | 82 | atomic_set(&local->active_users, 1); |
---|
87 | 83 | local->rxnet = rxnet; |
---|
88 | | - INIT_LIST_HEAD(&local->link); |
---|
| 84 | + INIT_HLIST_NODE(&local->link); |
---|
89 | 85 | INIT_WORK(&local->processor, rxrpc_local_processor); |
---|
90 | 86 | init_rwsem(&local->defrag_sem); |
---|
91 | 87 | skb_queue_head_init(&local->reject_queue); |
---|
92 | 88 | skb_queue_head_init(&local->event_queue); |
---|
93 | | - local->client_conns = RB_ROOT; |
---|
94 | | - spin_lock_init(&local->client_conns_lock); |
---|
| 89 | + local->client_bundles = RB_ROOT; |
---|
| 90 | + spin_lock_init(&local->client_bundles_lock); |
---|
95 | 91 | spin_lock_init(&local->lock); |
---|
96 | 92 | rwlock_init(&local->services_lock); |
---|
97 | 93 | local->debug_id = atomic_inc_return(&rxrpc_debug_id); |
---|
.. | .. |
---|
111 | 107 | static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net) |
---|
112 | 108 | { |
---|
113 | 109 | struct sock *usk; |
---|
114 | | - int ret, opt; |
---|
| 110 | + int ret; |
---|
115 | 111 | |
---|
116 | 112 | _enter("%p{%d,%d}", |
---|
117 | 113 | local, local->srx.transport_type, local->srx.transport.family); |
---|
.. | .. |
---|
161 | 157 | switch (local->srx.transport.family) { |
---|
162 | 158 | case AF_INET6: |
---|
163 | 159 | /* we want to receive ICMPv6 errors */ |
---|
164 | | - opt = 1; |
---|
165 | | - ret = kernel_setsockopt(local->socket, SOL_IPV6, IPV6_RECVERR, |
---|
166 | | - (char *) &opt, sizeof(opt)); |
---|
167 | | - if (ret < 0) { |
---|
168 | | - _debug("setsockopt failed"); |
---|
169 | | - goto error; |
---|
170 | | - } |
---|
| 160 | + ip6_sock_set_recverr(local->socket->sk); |
---|
171 | 161 | |
---|
172 | 162 | /* Fall through and set IPv4 options too otherwise we don't get |
---|
173 | 163 | * errors from IPv4 packets sent through the IPv6 socket. |
---|
174 | 164 | */ |
---|
175 | | - |
---|
| 165 | + fallthrough; |
---|
176 | 166 | case AF_INET: |
---|
177 | 167 | /* we want to receive ICMP errors */ |
---|
178 | | - opt = 1; |
---|
179 | | - ret = kernel_setsockopt(local->socket, SOL_IP, IP_RECVERR, |
---|
180 | | - (char *) &opt, sizeof(opt)); |
---|
181 | | - if (ret < 0) { |
---|
182 | | - _debug("setsockopt failed"); |
---|
183 | | - goto error; |
---|
184 | | - } |
---|
| 168 | + ip_sock_set_recverr(local->socket->sk); |
---|
185 | 169 | |
---|
186 | 170 | /* we want to set the don't fragment bit */ |
---|
187 | | - opt = IP_PMTUDISC_DO; |
---|
188 | | - ret = kernel_setsockopt(local->socket, SOL_IP, IP_MTU_DISCOVER, |
---|
189 | | - (char *) &opt, sizeof(opt)); |
---|
190 | | - if (ret < 0) { |
---|
191 | | - _debug("setsockopt failed"); |
---|
192 | | - goto error; |
---|
193 | | - } |
---|
| 171 | + ip_sock_set_mtu_discover(local->socket->sk, IP_PMTUDISC_DO); |
---|
194 | 172 | |
---|
195 | 173 | /* We want receive timestamps. */ |
---|
196 | | - opt = 1; |
---|
197 | | - ret = kernel_setsockopt(local->socket, SOL_SOCKET, SO_TIMESTAMPNS, |
---|
198 | | - (char *)&opt, sizeof(opt)); |
---|
199 | | - if (ret < 0) { |
---|
200 | | - _debug("setsockopt failed"); |
---|
201 | | - goto error; |
---|
202 | | - } |
---|
| 174 | + sock_enable_timestamps(local->socket->sk); |
---|
203 | 175 | break; |
---|
204 | 176 | |
---|
205 | 177 | default: |
---|
.. | .. |
---|
227 | 199 | { |
---|
228 | 200 | struct rxrpc_local *local; |
---|
229 | 201 | struct rxrpc_net *rxnet = rxrpc_net(net); |
---|
230 | | - struct list_head *cursor; |
---|
| 202 | + struct hlist_node *cursor; |
---|
231 | 203 | const char *age; |
---|
232 | 204 | long diff; |
---|
233 | 205 | int ret; |
---|
.. | .. |
---|
237 | 209 | |
---|
238 | 210 | mutex_lock(&rxnet->local_mutex); |
---|
239 | 211 | |
---|
240 | | - for (cursor = rxnet->local_endpoints.next; |
---|
241 | | - cursor != &rxnet->local_endpoints; |
---|
242 | | - cursor = cursor->next) { |
---|
243 | | - local = list_entry(cursor, struct rxrpc_local, link); |
---|
| 212 | + hlist_for_each(cursor, &rxnet->local_endpoints) { |
---|
| 213 | + local = hlist_entry(cursor, struct rxrpc_local, link); |
---|
244 | 214 | |
---|
245 | 215 | diff = rxrpc_local_cmp_key(local, srx); |
---|
246 | | - if (diff < 0) |
---|
| 216 | + if (diff != 0) |
---|
247 | 217 | continue; |
---|
248 | | - if (diff > 0) |
---|
249 | | - break; |
---|
250 | 218 | |
---|
251 | 219 | /* Services aren't allowed to share transport sockets, so |
---|
252 | 220 | * reject that here. It is possible that the object is dying - |
---|
.. | .. |
---|
258 | 226 | goto addr_in_use; |
---|
259 | 227 | } |
---|
260 | 228 | |
---|
261 | | - /* Found a match. We replace a dying object. Attempting to |
---|
262 | | - * bind the transport socket may still fail if we're attempting |
---|
263 | | - * to use a local address that the dying object is still using. |
---|
| 229 | + /* Found a match. We want to replace a dying object. |
---|
| 230 | + * Attempting to bind the transport socket may still fail if |
---|
| 231 | + * we're attempting to use a local address that the dying |
---|
| 232 | + * object is still using. |
---|
264 | 233 | */ |
---|
265 | 234 | if (!rxrpc_use_local(local)) |
---|
266 | 235 | break; |
---|
.. | .. |
---|
277 | 246 | if (ret < 0) |
---|
278 | 247 | goto sock_error; |
---|
279 | 248 | |
---|
280 | | - if (cursor != &rxnet->local_endpoints) |
---|
281 | | - list_replace_init(cursor, &local->link); |
---|
282 | | - else |
---|
283 | | - list_add_tail(&local->link, cursor); |
---|
| 249 | + if (cursor) { |
---|
| 250 | + hlist_replace_rcu(cursor, &local->link); |
---|
| 251 | + cursor->pprev = NULL; |
---|
| 252 | + } else { |
---|
| 253 | + hlist_add_head_rcu(&local->link, &rxnet->local_endpoints); |
---|
| 254 | + } |
---|
284 | 255 | age = "new"; |
---|
285 | 256 | |
---|
286 | 257 | found: |
---|
.. | .. |
---|
313 | 284 | struct rxrpc_local *rxrpc_get_local(struct rxrpc_local *local) |
---|
314 | 285 | { |
---|
315 | 286 | const void *here = __builtin_return_address(0); |
---|
316 | | - int n; |
---|
| 287 | + int r; |
---|
317 | 288 | |
---|
318 | | - n = atomic_inc_return(&local->usage); |
---|
319 | | - trace_rxrpc_local(local->debug_id, rxrpc_local_got, n, here); |
---|
| 289 | + __refcount_inc(&local->ref, &r); |
---|
| 290 | + trace_rxrpc_local(local->debug_id, rxrpc_local_got, r + 1, here); |
---|
320 | 291 | return local; |
---|
321 | 292 | } |
---|
322 | 293 | |
---|
.. | .. |
---|
326 | 297 | struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local) |
---|
327 | 298 | { |
---|
328 | 299 | const void *here = __builtin_return_address(0); |
---|
| 300 | + int r; |
---|
329 | 301 | |
---|
330 | 302 | if (local) { |
---|
331 | | - int n = atomic_fetch_add_unless(&local->usage, 1, 0); |
---|
332 | | - if (n > 0) |
---|
| 303 | + if (__refcount_inc_not_zero(&local->ref, &r)) |
---|
333 | 304 | trace_rxrpc_local(local->debug_id, rxrpc_local_got, |
---|
334 | | - n + 1, here); |
---|
| 305 | + r + 1, here); |
---|
335 | 306 | else |
---|
336 | 307 | local = NULL; |
---|
337 | 308 | } |
---|
.. | .. |
---|
345 | 316 | { |
---|
346 | 317 | const void *here = __builtin_return_address(0); |
---|
347 | 318 | unsigned int debug_id = local->debug_id; |
---|
348 | | - int n = atomic_read(&local->usage); |
---|
| 319 | + int r = refcount_read(&local->ref); |
---|
349 | 320 | |
---|
350 | 321 | if (rxrpc_queue_work(&local->processor)) |
---|
351 | | - trace_rxrpc_local(debug_id, rxrpc_local_queued, n, here); |
---|
| 322 | + trace_rxrpc_local(debug_id, rxrpc_local_queued, r + 1, here); |
---|
352 | 323 | else |
---|
353 | 324 | rxrpc_put_local(local); |
---|
354 | 325 | } |
---|
.. | .. |
---|
360 | 331 | { |
---|
361 | 332 | const void *here = __builtin_return_address(0); |
---|
362 | 333 | unsigned int debug_id; |
---|
363 | | - int n; |
---|
| 334 | + bool dead; |
---|
| 335 | + int r; |
---|
364 | 336 | |
---|
365 | 337 | if (local) { |
---|
366 | 338 | debug_id = local->debug_id; |
---|
367 | 339 | |
---|
368 | | - n = atomic_dec_return(&local->usage); |
---|
369 | | - trace_rxrpc_local(debug_id, rxrpc_local_put, n, here); |
---|
| 340 | + dead = __refcount_dec_and_test(&local->ref, &r); |
---|
| 341 | + trace_rxrpc_local(debug_id, rxrpc_local_put, r, here); |
---|
370 | 342 | |
---|
371 | | - if (n == 0) |
---|
| 343 | + if (dead) |
---|
372 | 344 | call_rcu(&local->rcu, rxrpc_local_rcu); |
---|
373 | 345 | } |
---|
374 | 346 | } |
---|
.. | .. |
---|
421 | 393 | local->dead = true; |
---|
422 | 394 | |
---|
423 | 395 | mutex_lock(&rxnet->local_mutex); |
---|
424 | | - list_del_init(&local->link); |
---|
| 396 | + hlist_del_init_rcu(&local->link); |
---|
425 | 397 | mutex_unlock(&rxnet->local_mutex); |
---|
426 | 398 | |
---|
427 | 399 | rxrpc_clean_up_local_conns(local); |
---|
.. | .. |
---|
452 | 424 | container_of(work, struct rxrpc_local, processor); |
---|
453 | 425 | bool again; |
---|
454 | 426 | |
---|
| 427 | + if (local->dead) |
---|
| 428 | + return; |
---|
| 429 | + |
---|
455 | 430 | trace_rxrpc_local(local->debug_id, rxrpc_local_processing, |
---|
456 | | - atomic_read(&local->usage), NULL); |
---|
| 431 | + refcount_read(&local->ref), NULL); |
---|
457 | 432 | |
---|
458 | 433 | do { |
---|
459 | 434 | again = false; |
---|
.. | .. |
---|
505 | 480 | |
---|
506 | 481 | flush_workqueue(rxrpc_workqueue); |
---|
507 | 482 | |
---|
508 | | - if (!list_empty(&rxnet->local_endpoints)) { |
---|
| 483 | + if (!hlist_empty(&rxnet->local_endpoints)) { |
---|
509 | 484 | mutex_lock(&rxnet->local_mutex); |
---|
510 | | - list_for_each_entry(local, &rxnet->local_endpoints, link) { |
---|
| 485 | + hlist_for_each_entry(local, &rxnet->local_endpoints, link) { |
---|
511 | 486 | pr_err("AF_RXRPC: Leaked local %p {%d}\n", |
---|
512 | | - local, atomic_read(&local->usage)); |
---|
| 487 | + local, refcount_read(&local->ref)); |
---|
513 | 488 | } |
---|
514 | 489 | mutex_unlock(&rxnet->local_mutex); |
---|
515 | 490 | BUG(); |
---|