18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Local endpoint object management 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/net.h> 128c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/udp.h> 158c2ecf20Sopenharmony_ci#include <linux/ip.h> 168c2ecf20Sopenharmony_ci#include <linux/hashtable.h> 178c2ecf20Sopenharmony_ci#include <net/sock.h> 188c2ecf20Sopenharmony_ci#include <net/udp.h> 198c2ecf20Sopenharmony_ci#include <net/af_rxrpc.h> 208c2ecf20Sopenharmony_ci#include "ar-internal.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic void rxrpc_local_processor(struct work_struct *); 238c2ecf20Sopenharmony_cistatic void rxrpc_local_rcu(struct rcu_head *); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Compare a local to an address. Return -ve, 0 or +ve to indicate less than, 278c2ecf20Sopenharmony_ci * same or greater than. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * We explicitly don't compare the RxRPC service ID as we want to reject 308c2ecf20Sopenharmony_ci * conflicting uses by differing services. Further, we don't want to share 318c2ecf20Sopenharmony_ci * addresses with different options (IPv6), so we don't compare those bits 328c2ecf20Sopenharmony_ci * either. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cistatic long rxrpc_local_cmp_key(const struct rxrpc_local *local, 358c2ecf20Sopenharmony_ci const struct sockaddr_rxrpc *srx) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci long diff; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci diff = ((local->srx.transport_type - srx->transport_type) ?: 408c2ecf20Sopenharmony_ci (local->srx.transport_len - srx->transport_len) ?: 418c2ecf20Sopenharmony_ci (local->srx.transport.family - srx->transport.family)); 428c2ecf20Sopenharmony_ci if (diff != 0) 438c2ecf20Sopenharmony_ci return diff; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci switch (srx->transport.family) { 468c2ecf20Sopenharmony_ci case AF_INET: 478c2ecf20Sopenharmony_ci /* If the choice of UDP port is left up to the transport, then 488c2ecf20Sopenharmony_ci * the endpoint record doesn't match. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci return ((u16 __force)local->srx.transport.sin.sin_port - 518c2ecf20Sopenharmony_ci (u16 __force)srx->transport.sin.sin_port) ?: 528c2ecf20Sopenharmony_ci memcmp(&local->srx.transport.sin.sin_addr, 538c2ecf20Sopenharmony_ci &srx->transport.sin.sin_addr, 548c2ecf20Sopenharmony_ci sizeof(struct in_addr)); 558c2ecf20Sopenharmony_ci#ifdef CONFIG_AF_RXRPC_IPV6 568c2ecf20Sopenharmony_ci case AF_INET6: 578c2ecf20Sopenharmony_ci /* If the choice of UDP6 port is left up to the transport, then 588c2ecf20Sopenharmony_ci * the endpoint record doesn't match. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci return ((u16 __force)local->srx.transport.sin6.sin6_port - 618c2ecf20Sopenharmony_ci (u16 __force)srx->transport.sin6.sin6_port) ?: 628c2ecf20Sopenharmony_ci memcmp(&local->srx.transport.sin6.sin6_addr, 638c2ecf20Sopenharmony_ci &srx->transport.sin6.sin6_addr, 648c2ecf20Sopenharmony_ci sizeof(struct in6_addr)); 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci default: 678c2ecf20Sopenharmony_ci BUG(); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * Allocate a new local endpoint. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet, 758c2ecf20Sopenharmony_ci const struct sockaddr_rxrpc *srx) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct rxrpc_local *local; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL); 808c2ecf20Sopenharmony_ci if (local) { 818c2ecf20Sopenharmony_ci refcount_set(&local->ref, 1); 828c2ecf20Sopenharmony_ci atomic_set(&local->active_users, 1); 838c2ecf20Sopenharmony_ci local->rxnet = rxnet; 848c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&local->link); 858c2ecf20Sopenharmony_ci INIT_WORK(&local->processor, rxrpc_local_processor); 868c2ecf20Sopenharmony_ci init_rwsem(&local->defrag_sem); 878c2ecf20Sopenharmony_ci skb_queue_head_init(&local->reject_queue); 888c2ecf20Sopenharmony_ci skb_queue_head_init(&local->event_queue); 898c2ecf20Sopenharmony_ci local->client_bundles = RB_ROOT; 908c2ecf20Sopenharmony_ci spin_lock_init(&local->client_bundles_lock); 918c2ecf20Sopenharmony_ci spin_lock_init(&local->lock); 928c2ecf20Sopenharmony_ci rwlock_init(&local->services_lock); 938c2ecf20Sopenharmony_ci local->debug_id = atomic_inc_return(&rxrpc_debug_id); 948c2ecf20Sopenharmony_ci memcpy(&local->srx, srx, sizeof(*srx)); 958c2ecf20Sopenharmony_ci local->srx.srx_service = 0; 968c2ecf20Sopenharmony_ci trace_rxrpc_local(local->debug_id, rxrpc_local_new, 1, NULL); 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci _leave(" = %p", local); 1008c2ecf20Sopenharmony_ci return local; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * create the local socket 1058c2ecf20Sopenharmony_ci * - must be called with rxrpc_local_mutex locked 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_cistatic int rxrpc_open_socket(struct rxrpc_local *local, struct net *net) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct sock *usk; 1108c2ecf20Sopenharmony_ci int ret; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci _enter("%p{%d,%d}", 1138c2ecf20Sopenharmony_ci local, local->srx.transport_type, local->srx.transport.family); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* create a socket to represent the local endpoint */ 1168c2ecf20Sopenharmony_ci ret = sock_create_kern(net, local->srx.transport.family, 1178c2ecf20Sopenharmony_ci local->srx.transport_type, 0, &local->socket); 1188c2ecf20Sopenharmony_ci if (ret < 0) { 1198c2ecf20Sopenharmony_ci _leave(" = %d [socket]", ret); 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* set the socket up */ 1248c2ecf20Sopenharmony_ci usk = local->socket->sk; 1258c2ecf20Sopenharmony_ci inet_sk(usk)->mc_loop = 0; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Enable CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE conversion */ 1288c2ecf20Sopenharmony_ci inet_inc_convert_csum(usk); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci rcu_assign_sk_user_data(usk, local); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci udp_sk(usk)->encap_type = UDP_ENCAP_RXRPC; 1338c2ecf20Sopenharmony_ci udp_sk(usk)->encap_rcv = rxrpc_input_packet; 1348c2ecf20Sopenharmony_ci udp_sk(usk)->encap_destroy = NULL; 1358c2ecf20Sopenharmony_ci udp_sk(usk)->gro_receive = NULL; 1368c2ecf20Sopenharmony_ci udp_sk(usk)->gro_complete = NULL; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci udp_encap_enable(); 1398c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_AF_RXRPC_IPV6) 1408c2ecf20Sopenharmony_ci if (local->srx.transport.family == AF_INET6) 1418c2ecf20Sopenharmony_ci udpv6_encap_enable(); 1428c2ecf20Sopenharmony_ci#endif 1438c2ecf20Sopenharmony_ci usk->sk_error_report = rxrpc_error_report; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* if a local address was supplied then bind it */ 1468c2ecf20Sopenharmony_ci if (local->srx.transport_len > sizeof(sa_family_t)) { 1478c2ecf20Sopenharmony_ci _debug("bind"); 1488c2ecf20Sopenharmony_ci ret = kernel_bind(local->socket, 1498c2ecf20Sopenharmony_ci (struct sockaddr *)&local->srx.transport, 1508c2ecf20Sopenharmony_ci local->srx.transport_len); 1518c2ecf20Sopenharmony_ci if (ret < 0) { 1528c2ecf20Sopenharmony_ci _debug("bind failed %d", ret); 1538c2ecf20Sopenharmony_ci goto error; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci switch (local->srx.transport.family) { 1588c2ecf20Sopenharmony_ci case AF_INET6: 1598c2ecf20Sopenharmony_ci /* we want to receive ICMPv6 errors */ 1608c2ecf20Sopenharmony_ci ip6_sock_set_recverr(local->socket->sk); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* Fall through and set IPv4 options too otherwise we don't get 1638c2ecf20Sopenharmony_ci * errors from IPv4 packets sent through the IPv6 socket. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci fallthrough; 1668c2ecf20Sopenharmony_ci case AF_INET: 1678c2ecf20Sopenharmony_ci /* we want to receive ICMP errors */ 1688c2ecf20Sopenharmony_ci ip_sock_set_recverr(local->socket->sk); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* we want to set the don't fragment bit */ 1718c2ecf20Sopenharmony_ci ip_sock_set_mtu_discover(local->socket->sk, IP_PMTUDISC_DO); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* We want receive timestamps. */ 1748c2ecf20Sopenharmony_ci sock_enable_timestamps(local->socket->sk); 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci default: 1788c2ecf20Sopenharmony_ci BUG(); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci _leave(" = 0"); 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cierror: 1858c2ecf20Sopenharmony_ci kernel_sock_shutdown(local->socket, SHUT_RDWR); 1868c2ecf20Sopenharmony_ci local->socket->sk->sk_user_data = NULL; 1878c2ecf20Sopenharmony_ci sock_release(local->socket); 1888c2ecf20Sopenharmony_ci local->socket = NULL; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci _leave(" = %d", ret); 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* 1958c2ecf20Sopenharmony_ci * Look up or create a new local endpoint using the specified local address. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistruct rxrpc_local *rxrpc_lookup_local(struct net *net, 1988c2ecf20Sopenharmony_ci const struct sockaddr_rxrpc *srx) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct rxrpc_local *local; 2018c2ecf20Sopenharmony_ci struct rxrpc_net *rxnet = rxrpc_net(net); 2028c2ecf20Sopenharmony_ci struct hlist_node *cursor; 2038c2ecf20Sopenharmony_ci const char *age; 2048c2ecf20Sopenharmony_ci long diff; 2058c2ecf20Sopenharmony_ci int ret; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci _enter("{%d,%d,%pISp}", 2088c2ecf20Sopenharmony_ci srx->transport_type, srx->transport.family, &srx->transport); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci mutex_lock(&rxnet->local_mutex); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci hlist_for_each(cursor, &rxnet->local_endpoints) { 2138c2ecf20Sopenharmony_ci local = hlist_entry(cursor, struct rxrpc_local, link); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci diff = rxrpc_local_cmp_key(local, srx); 2168c2ecf20Sopenharmony_ci if (diff != 0) 2178c2ecf20Sopenharmony_ci continue; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Services aren't allowed to share transport sockets, so 2208c2ecf20Sopenharmony_ci * reject that here. It is possible that the object is dying - 2218c2ecf20Sopenharmony_ci * but it may also still have the local transport address that 2228c2ecf20Sopenharmony_ci * we want bound. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci if (srx->srx_service) { 2258c2ecf20Sopenharmony_ci local = NULL; 2268c2ecf20Sopenharmony_ci goto addr_in_use; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* Found a match. We want to replace a dying object. 2308c2ecf20Sopenharmony_ci * Attempting to bind the transport socket may still fail if 2318c2ecf20Sopenharmony_ci * we're attempting to use a local address that the dying 2328c2ecf20Sopenharmony_ci * object is still using. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci if (!rxrpc_use_local(local)) 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci age = "old"; 2388c2ecf20Sopenharmony_ci goto found; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci local = rxrpc_alloc_local(rxnet, srx); 2428c2ecf20Sopenharmony_ci if (!local) 2438c2ecf20Sopenharmony_ci goto nomem; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ret = rxrpc_open_socket(local, net); 2468c2ecf20Sopenharmony_ci if (ret < 0) 2478c2ecf20Sopenharmony_ci goto sock_error; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (cursor) { 2508c2ecf20Sopenharmony_ci hlist_replace_rcu(cursor, &local->link); 2518c2ecf20Sopenharmony_ci cursor->pprev = NULL; 2528c2ecf20Sopenharmony_ci } else { 2538c2ecf20Sopenharmony_ci hlist_add_head_rcu(&local->link, &rxnet->local_endpoints); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci age = "new"; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cifound: 2588c2ecf20Sopenharmony_ci mutex_unlock(&rxnet->local_mutex); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci _net("LOCAL %s %d {%pISp}", 2618c2ecf20Sopenharmony_ci age, local->debug_id, &local->srx.transport); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci _leave(" = %p", local); 2648c2ecf20Sopenharmony_ci return local; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cinomem: 2678c2ecf20Sopenharmony_ci ret = -ENOMEM; 2688c2ecf20Sopenharmony_cisock_error: 2698c2ecf20Sopenharmony_ci mutex_unlock(&rxnet->local_mutex); 2708c2ecf20Sopenharmony_ci if (local) 2718c2ecf20Sopenharmony_ci call_rcu(&local->rcu, rxrpc_local_rcu); 2728c2ecf20Sopenharmony_ci _leave(" = %d", ret); 2738c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ciaddr_in_use: 2768c2ecf20Sopenharmony_ci mutex_unlock(&rxnet->local_mutex); 2778c2ecf20Sopenharmony_ci _leave(" = -EADDRINUSE"); 2788c2ecf20Sopenharmony_ci return ERR_PTR(-EADDRINUSE); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* 2828c2ecf20Sopenharmony_ci * Get a ref on a local endpoint. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistruct rxrpc_local *rxrpc_get_local(struct rxrpc_local *local) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci const void *here = __builtin_return_address(0); 2878c2ecf20Sopenharmony_ci int r; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci __refcount_inc(&local->ref, &r); 2908c2ecf20Sopenharmony_ci trace_rxrpc_local(local->debug_id, rxrpc_local_got, r + 1, here); 2918c2ecf20Sopenharmony_ci return local; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * Get a ref on a local endpoint unless its usage has already reached 0. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistruct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci const void *here = __builtin_return_address(0); 3008c2ecf20Sopenharmony_ci int r; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (local) { 3038c2ecf20Sopenharmony_ci if (__refcount_inc_not_zero(&local->ref, &r)) 3048c2ecf20Sopenharmony_ci trace_rxrpc_local(local->debug_id, rxrpc_local_got, 3058c2ecf20Sopenharmony_ci r + 1, here); 3068c2ecf20Sopenharmony_ci else 3078c2ecf20Sopenharmony_ci local = NULL; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci return local; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* 3138c2ecf20Sopenharmony_ci * Queue a local endpoint and pass the caller's reference to the work item. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_civoid rxrpc_queue_local(struct rxrpc_local *local) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci const void *here = __builtin_return_address(0); 3188c2ecf20Sopenharmony_ci unsigned int debug_id = local->debug_id; 3198c2ecf20Sopenharmony_ci int r = refcount_read(&local->ref); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (rxrpc_queue_work(&local->processor)) 3228c2ecf20Sopenharmony_ci trace_rxrpc_local(debug_id, rxrpc_local_queued, r + 1, here); 3238c2ecf20Sopenharmony_ci else 3248c2ecf20Sopenharmony_ci rxrpc_put_local(local); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* 3288c2ecf20Sopenharmony_ci * Drop a ref on a local endpoint. 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_civoid rxrpc_put_local(struct rxrpc_local *local) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci const void *here = __builtin_return_address(0); 3338c2ecf20Sopenharmony_ci unsigned int debug_id; 3348c2ecf20Sopenharmony_ci bool dead; 3358c2ecf20Sopenharmony_ci int r; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (local) { 3388c2ecf20Sopenharmony_ci debug_id = local->debug_id; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci dead = __refcount_dec_and_test(&local->ref, &r); 3418c2ecf20Sopenharmony_ci trace_rxrpc_local(debug_id, rxrpc_local_put, r, here); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (dead) 3448c2ecf20Sopenharmony_ci call_rcu(&local->rcu, rxrpc_local_rcu); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* 3498c2ecf20Sopenharmony_ci * Start using a local endpoint. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistruct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci local = rxrpc_get_local_maybe(local); 3548c2ecf20Sopenharmony_ci if (!local) 3558c2ecf20Sopenharmony_ci return NULL; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (!__rxrpc_use_local(local)) { 3588c2ecf20Sopenharmony_ci rxrpc_put_local(local); 3598c2ecf20Sopenharmony_ci return NULL; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return local; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/* 3668c2ecf20Sopenharmony_ci * Cease using a local endpoint. Once the number of active users reaches 0, we 3678c2ecf20Sopenharmony_ci * start the closure of the transport in the work processor. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_civoid rxrpc_unuse_local(struct rxrpc_local *local) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci if (local) { 3728c2ecf20Sopenharmony_ci if (__rxrpc_unuse_local(local)) { 3738c2ecf20Sopenharmony_ci rxrpc_get_local(local); 3748c2ecf20Sopenharmony_ci rxrpc_queue_local(local); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/* 3808c2ecf20Sopenharmony_ci * Destroy a local endpoint's socket and then hand the record to RCU to dispose 3818c2ecf20Sopenharmony_ci * of. 3828c2ecf20Sopenharmony_ci * 3838c2ecf20Sopenharmony_ci * Closing the socket cannot be done from bottom half context or RCU callback 3848c2ecf20Sopenharmony_ci * context because it might sleep. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_cistatic void rxrpc_local_destroyer(struct rxrpc_local *local) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct socket *socket = local->socket; 3898c2ecf20Sopenharmony_ci struct rxrpc_net *rxnet = local->rxnet; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci _enter("%d", local->debug_id); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci local->dead = true; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci mutex_lock(&rxnet->local_mutex); 3968c2ecf20Sopenharmony_ci hlist_del_init_rcu(&local->link); 3978c2ecf20Sopenharmony_ci mutex_unlock(&rxnet->local_mutex); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci rxrpc_clean_up_local_conns(local); 4008c2ecf20Sopenharmony_ci rxrpc_service_connection_reaper(&rxnet->service_conn_reaper); 4018c2ecf20Sopenharmony_ci ASSERT(!local->service); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (socket) { 4048c2ecf20Sopenharmony_ci local->socket = NULL; 4058c2ecf20Sopenharmony_ci kernel_sock_shutdown(socket, SHUT_RDWR); 4068c2ecf20Sopenharmony_ci socket->sk->sk_user_data = NULL; 4078c2ecf20Sopenharmony_ci sock_release(socket); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* At this point, there should be no more packets coming in to the 4118c2ecf20Sopenharmony_ci * local endpoint. 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci rxrpc_purge_queue(&local->reject_queue); 4148c2ecf20Sopenharmony_ci rxrpc_purge_queue(&local->event_queue); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/* 4188c2ecf20Sopenharmony_ci * Process events on an endpoint. The work item carries a ref which 4198c2ecf20Sopenharmony_ci * we must release. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_cistatic void rxrpc_local_processor(struct work_struct *work) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct rxrpc_local *local = 4248c2ecf20Sopenharmony_ci container_of(work, struct rxrpc_local, processor); 4258c2ecf20Sopenharmony_ci bool again; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (local->dead) 4288c2ecf20Sopenharmony_ci return; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci trace_rxrpc_local(local->debug_id, rxrpc_local_processing, 4318c2ecf20Sopenharmony_ci refcount_read(&local->ref), NULL); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci do { 4348c2ecf20Sopenharmony_ci again = false; 4358c2ecf20Sopenharmony_ci if (!__rxrpc_use_local(local)) { 4368c2ecf20Sopenharmony_ci rxrpc_local_destroyer(local); 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (!skb_queue_empty(&local->reject_queue)) { 4418c2ecf20Sopenharmony_ci rxrpc_reject_packets(local); 4428c2ecf20Sopenharmony_ci again = true; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (!skb_queue_empty(&local->event_queue)) { 4468c2ecf20Sopenharmony_ci rxrpc_process_local_events(local); 4478c2ecf20Sopenharmony_ci again = true; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci __rxrpc_unuse_local(local); 4518c2ecf20Sopenharmony_ci } while (again); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci rxrpc_put_local(local); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* 4578c2ecf20Sopenharmony_ci * Destroy a local endpoint after the RCU grace period expires. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_cistatic void rxrpc_local_rcu(struct rcu_head *rcu) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct rxrpc_local *local = container_of(rcu, struct rxrpc_local, rcu); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci _enter("%d", local->debug_id); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci ASSERT(!work_pending(&local->processor)); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci _net("DESTROY LOCAL %d", local->debug_id); 4688c2ecf20Sopenharmony_ci kfree(local); 4698c2ecf20Sopenharmony_ci _leave(""); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/* 4738c2ecf20Sopenharmony_ci * Verify the local endpoint list is empty by this point. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_civoid rxrpc_destroy_all_locals(struct rxrpc_net *rxnet) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct rxrpc_local *local; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci _enter(""); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci flush_workqueue(rxrpc_workqueue); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!hlist_empty(&rxnet->local_endpoints)) { 4848c2ecf20Sopenharmony_ci mutex_lock(&rxnet->local_mutex); 4858c2ecf20Sopenharmony_ci hlist_for_each_entry(local, &rxnet->local_endpoints, link) { 4868c2ecf20Sopenharmony_ci pr_err("AF_RXRPC: Leaked local %p {%d}\n", 4878c2ecf20Sopenharmony_ci local, refcount_read(&local->ref)); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci mutex_unlock(&rxnet->local_mutex); 4908c2ecf20Sopenharmony_ci BUG(); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci} 493