162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2006, 2019 Oracle and/or its affiliates. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * OpenIB.org BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci#include <linux/module.h> 3462306a36Sopenharmony_ci#include <linux/errno.h> 3562306a36Sopenharmony_ci#include <linux/kernel.h> 3662306a36Sopenharmony_ci#include <linux/gfp.h> 3762306a36Sopenharmony_ci#include <linux/in.h> 3862306a36Sopenharmony_ci#include <linux/ipv6.h> 3962306a36Sopenharmony_ci#include <linux/poll.h> 4062306a36Sopenharmony_ci#include <net/sock.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "rds.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* this is just used for stats gathering :/ */ 4562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(rds_sock_lock); 4662306a36Sopenharmony_cistatic unsigned long rds_sock_count; 4762306a36Sopenharmony_cistatic LIST_HEAD(rds_sock_list); 4862306a36Sopenharmony_ciDECLARE_WAIT_QUEUE_HEAD(rds_poll_waitq); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * This is called as the final descriptor referencing this socket is closed. 5262306a36Sopenharmony_ci * We have to unbind the socket so that another socket can be bound to the 5362306a36Sopenharmony_ci * address it was using. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * We have to be careful about racing with the incoming path. sock_orphan() 5662306a36Sopenharmony_ci * sets SOCK_DEAD and we use that as an indicator to the rx path that new 5762306a36Sopenharmony_ci * messages shouldn't be queued. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic int rds_release(struct socket *sock) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct sock *sk = sock->sk; 6262306a36Sopenharmony_ci struct rds_sock *rs; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (!sk) 6562306a36Sopenharmony_ci goto out; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci rs = rds_sk_to_rs(sk); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci sock_orphan(sk); 7062306a36Sopenharmony_ci /* Note - rds_clear_recv_queue grabs rs_recv_lock, so 7162306a36Sopenharmony_ci * that ensures the recv path has completed messing 7262306a36Sopenharmony_ci * with the socket. */ 7362306a36Sopenharmony_ci rds_clear_recv_queue(rs); 7462306a36Sopenharmony_ci rds_cong_remove_socket(rs); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci rds_remove_bound(rs); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci rds_send_drop_to(rs, NULL); 7962306a36Sopenharmony_ci rds_rdma_drop_keys(rs); 8062306a36Sopenharmony_ci rds_notify_queue_get(rs, NULL); 8162306a36Sopenharmony_ci rds_notify_msg_zcopy_purge(&rs->rs_zcookie_queue); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci spin_lock_bh(&rds_sock_lock); 8462306a36Sopenharmony_ci list_del_init(&rs->rs_item); 8562306a36Sopenharmony_ci rds_sock_count--; 8662306a36Sopenharmony_ci spin_unlock_bh(&rds_sock_lock); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci rds_trans_put(rs->rs_transport); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci sock->sk = NULL; 9162306a36Sopenharmony_ci sock_put(sk); 9262306a36Sopenharmony_ciout: 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * Careful not to race with rds_release -> sock_orphan which clears sk_sleep. 9862306a36Sopenharmony_ci * _bh() isn't OK here, we're called from interrupt handlers. It's probably OK 9962306a36Sopenharmony_ci * to wake the waitqueue after sk_sleep is clear as we hold a sock ref, but 10062306a36Sopenharmony_ci * this seems more conservative. 10162306a36Sopenharmony_ci * NB - normally, one would use sk_callback_lock for this, but we can 10262306a36Sopenharmony_ci * get here from interrupts, whereas the network code grabs sk_callback_lock 10362306a36Sopenharmony_ci * with _lock_bh only - so relying on sk_callback_lock introduces livelocks. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_civoid rds_wake_sk_sleep(struct rds_sock *rs) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci unsigned long flags; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci read_lock_irqsave(&rs->rs_recv_lock, flags); 11062306a36Sopenharmony_ci __rds_wake_sk_sleep(rds_rs_to_sk(rs)); 11162306a36Sopenharmony_ci read_unlock_irqrestore(&rs->rs_recv_lock, flags); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int rds_getname(struct socket *sock, struct sockaddr *uaddr, 11562306a36Sopenharmony_ci int peer) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct rds_sock *rs = rds_sk_to_rs(sock->sk); 11862306a36Sopenharmony_ci struct sockaddr_in6 *sin6; 11962306a36Sopenharmony_ci struct sockaddr_in *sin; 12062306a36Sopenharmony_ci int uaddr_len; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* racey, don't care */ 12362306a36Sopenharmony_ci if (peer) { 12462306a36Sopenharmony_ci if (ipv6_addr_any(&rs->rs_conn_addr)) 12562306a36Sopenharmony_ci return -ENOTCONN; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (ipv6_addr_v4mapped(&rs->rs_conn_addr)) { 12862306a36Sopenharmony_ci sin = (struct sockaddr_in *)uaddr; 12962306a36Sopenharmony_ci memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 13062306a36Sopenharmony_ci sin->sin_family = AF_INET; 13162306a36Sopenharmony_ci sin->sin_port = rs->rs_conn_port; 13262306a36Sopenharmony_ci sin->sin_addr.s_addr = rs->rs_conn_addr_v4; 13362306a36Sopenharmony_ci uaddr_len = sizeof(*sin); 13462306a36Sopenharmony_ci } else { 13562306a36Sopenharmony_ci sin6 = (struct sockaddr_in6 *)uaddr; 13662306a36Sopenharmony_ci sin6->sin6_family = AF_INET6; 13762306a36Sopenharmony_ci sin6->sin6_port = rs->rs_conn_port; 13862306a36Sopenharmony_ci sin6->sin6_addr = rs->rs_conn_addr; 13962306a36Sopenharmony_ci sin6->sin6_flowinfo = 0; 14062306a36Sopenharmony_ci /* scope_id is the same as in the bound address. */ 14162306a36Sopenharmony_ci sin6->sin6_scope_id = rs->rs_bound_scope_id; 14262306a36Sopenharmony_ci uaddr_len = sizeof(*sin6); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } else { 14562306a36Sopenharmony_ci /* If socket is not yet bound and the socket is connected, 14662306a36Sopenharmony_ci * set the return address family to be the same as the 14762306a36Sopenharmony_ci * connected address, but with 0 address value. If it is not 14862306a36Sopenharmony_ci * connected, set the family to be AF_UNSPEC (value 0) and 14962306a36Sopenharmony_ci * the address size to be that of an IPv4 address. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci if (ipv6_addr_any(&rs->rs_bound_addr)) { 15262306a36Sopenharmony_ci if (ipv6_addr_any(&rs->rs_conn_addr)) { 15362306a36Sopenharmony_ci sin = (struct sockaddr_in *)uaddr; 15462306a36Sopenharmony_ci memset(sin, 0, sizeof(*sin)); 15562306a36Sopenharmony_ci sin->sin_family = AF_UNSPEC; 15662306a36Sopenharmony_ci return sizeof(*sin); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 16062306a36Sopenharmony_ci if (!(ipv6_addr_type(&rs->rs_conn_addr) & 16162306a36Sopenharmony_ci IPV6_ADDR_MAPPED)) { 16262306a36Sopenharmony_ci sin6 = (struct sockaddr_in6 *)uaddr; 16362306a36Sopenharmony_ci memset(sin6, 0, sizeof(*sin6)); 16462306a36Sopenharmony_ci sin6->sin6_family = AF_INET6; 16562306a36Sopenharmony_ci return sizeof(*sin6); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci#endif 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci sin = (struct sockaddr_in *)uaddr; 17062306a36Sopenharmony_ci memset(sin, 0, sizeof(*sin)); 17162306a36Sopenharmony_ci sin->sin_family = AF_INET; 17262306a36Sopenharmony_ci return sizeof(*sin); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci if (ipv6_addr_v4mapped(&rs->rs_bound_addr)) { 17562306a36Sopenharmony_ci sin = (struct sockaddr_in *)uaddr; 17662306a36Sopenharmony_ci memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 17762306a36Sopenharmony_ci sin->sin_family = AF_INET; 17862306a36Sopenharmony_ci sin->sin_port = rs->rs_bound_port; 17962306a36Sopenharmony_ci sin->sin_addr.s_addr = rs->rs_bound_addr_v4; 18062306a36Sopenharmony_ci uaddr_len = sizeof(*sin); 18162306a36Sopenharmony_ci } else { 18262306a36Sopenharmony_ci sin6 = (struct sockaddr_in6 *)uaddr; 18362306a36Sopenharmony_ci sin6->sin6_family = AF_INET6; 18462306a36Sopenharmony_ci sin6->sin6_port = rs->rs_bound_port; 18562306a36Sopenharmony_ci sin6->sin6_addr = rs->rs_bound_addr; 18662306a36Sopenharmony_ci sin6->sin6_flowinfo = 0; 18762306a36Sopenharmony_ci sin6->sin6_scope_id = rs->rs_bound_scope_id; 18862306a36Sopenharmony_ci uaddr_len = sizeof(*sin6); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return uaddr_len; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * RDS' poll is without a doubt the least intuitive part of the interface, 19762306a36Sopenharmony_ci * as EPOLLIN and EPOLLOUT do not behave entirely as you would expect from 19862306a36Sopenharmony_ci * a network protocol. 19962306a36Sopenharmony_ci * 20062306a36Sopenharmony_ci * EPOLLIN is asserted if 20162306a36Sopenharmony_ci * - there is data on the receive queue. 20262306a36Sopenharmony_ci * - to signal that a previously congested destination may have become 20362306a36Sopenharmony_ci * uncongested 20462306a36Sopenharmony_ci * - A notification has been queued to the socket (this can be a congestion 20562306a36Sopenharmony_ci * update, or a RDMA completion, or a MSG_ZEROCOPY completion). 20662306a36Sopenharmony_ci * 20762306a36Sopenharmony_ci * EPOLLOUT is asserted if there is room on the send queue. This does not mean 20862306a36Sopenharmony_ci * however, that the next sendmsg() call will succeed. If the application tries 20962306a36Sopenharmony_ci * to send to a congested destination, the system call may still fail (and 21062306a36Sopenharmony_ci * return ENOBUFS). 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_cistatic __poll_t rds_poll(struct file *file, struct socket *sock, 21362306a36Sopenharmony_ci poll_table *wait) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct sock *sk = sock->sk; 21662306a36Sopenharmony_ci struct rds_sock *rs = rds_sk_to_rs(sk); 21762306a36Sopenharmony_ci __poll_t mask = 0; 21862306a36Sopenharmony_ci unsigned long flags; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci poll_wait(file, sk_sleep(sk), wait); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (rs->rs_seen_congestion) 22362306a36Sopenharmony_ci poll_wait(file, &rds_poll_waitq, wait); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci read_lock_irqsave(&rs->rs_recv_lock, flags); 22662306a36Sopenharmony_ci if (!rs->rs_cong_monitor) { 22762306a36Sopenharmony_ci /* When a congestion map was updated, we signal EPOLLIN for 22862306a36Sopenharmony_ci * "historical" reasons. Applications can also poll for 22962306a36Sopenharmony_ci * WRBAND instead. */ 23062306a36Sopenharmony_ci if (rds_cong_updated_since(&rs->rs_cong_track)) 23162306a36Sopenharmony_ci mask |= (EPOLLIN | EPOLLRDNORM | EPOLLWRBAND); 23262306a36Sopenharmony_ci } else { 23362306a36Sopenharmony_ci spin_lock(&rs->rs_lock); 23462306a36Sopenharmony_ci if (rs->rs_cong_notify) 23562306a36Sopenharmony_ci mask |= (EPOLLIN | EPOLLRDNORM); 23662306a36Sopenharmony_ci spin_unlock(&rs->rs_lock); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci if (!list_empty(&rs->rs_recv_queue) || 23962306a36Sopenharmony_ci !list_empty(&rs->rs_notify_queue) || 24062306a36Sopenharmony_ci !list_empty(&rs->rs_zcookie_queue.zcookie_head)) 24162306a36Sopenharmony_ci mask |= (EPOLLIN | EPOLLRDNORM); 24262306a36Sopenharmony_ci if (rs->rs_snd_bytes < rds_sk_sndbuf(rs)) 24362306a36Sopenharmony_ci mask |= (EPOLLOUT | EPOLLWRNORM); 24462306a36Sopenharmony_ci if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) 24562306a36Sopenharmony_ci mask |= POLLERR; 24662306a36Sopenharmony_ci read_unlock_irqrestore(&rs->rs_recv_lock, flags); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* clear state any time we wake a seen-congested socket */ 24962306a36Sopenharmony_ci if (mask) 25062306a36Sopenharmony_ci rs->rs_seen_congestion = 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return mask; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int rds_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct rds_sock *rs = rds_sk_to_rs(sock->sk); 25862306a36Sopenharmony_ci rds_tos_t utos, tos = 0; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci switch (cmd) { 26162306a36Sopenharmony_ci case SIOCRDSSETTOS: 26262306a36Sopenharmony_ci if (get_user(utos, (rds_tos_t __user *)arg)) 26362306a36Sopenharmony_ci return -EFAULT; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (rs->rs_transport && 26662306a36Sopenharmony_ci rs->rs_transport->get_tos_map) 26762306a36Sopenharmony_ci tos = rs->rs_transport->get_tos_map(utos); 26862306a36Sopenharmony_ci else 26962306a36Sopenharmony_ci return -ENOIOCTLCMD; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci spin_lock_bh(&rds_sock_lock); 27262306a36Sopenharmony_ci if (rs->rs_tos || rs->rs_conn) { 27362306a36Sopenharmony_ci spin_unlock_bh(&rds_sock_lock); 27462306a36Sopenharmony_ci return -EINVAL; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci rs->rs_tos = tos; 27762306a36Sopenharmony_ci spin_unlock_bh(&rds_sock_lock); 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci case SIOCRDSGETTOS: 28062306a36Sopenharmony_ci spin_lock_bh(&rds_sock_lock); 28162306a36Sopenharmony_ci tos = rs->rs_tos; 28262306a36Sopenharmony_ci spin_unlock_bh(&rds_sock_lock); 28362306a36Sopenharmony_ci if (put_user(tos, (rds_tos_t __user *)arg)) 28462306a36Sopenharmony_ci return -EFAULT; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci default: 28762306a36Sopenharmony_ci return -ENOIOCTLCMD; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int rds_cancel_sent_to(struct rds_sock *rs, sockptr_t optval, int len) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct sockaddr_in6 sin6; 29662306a36Sopenharmony_ci struct sockaddr_in sin; 29762306a36Sopenharmony_ci int ret = 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* racing with another thread binding seems ok here */ 30062306a36Sopenharmony_ci if (ipv6_addr_any(&rs->rs_bound_addr)) { 30162306a36Sopenharmony_ci ret = -ENOTCONN; /* XXX not a great errno */ 30262306a36Sopenharmony_ci goto out; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (len < sizeof(struct sockaddr_in)) { 30662306a36Sopenharmony_ci ret = -EINVAL; 30762306a36Sopenharmony_ci goto out; 30862306a36Sopenharmony_ci } else if (len < sizeof(struct sockaddr_in6)) { 30962306a36Sopenharmony_ci /* Assume IPv4 */ 31062306a36Sopenharmony_ci if (copy_from_sockptr(&sin, optval, 31162306a36Sopenharmony_ci sizeof(struct sockaddr_in))) { 31262306a36Sopenharmony_ci ret = -EFAULT; 31362306a36Sopenharmony_ci goto out; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci ipv6_addr_set_v4mapped(sin.sin_addr.s_addr, &sin6.sin6_addr); 31662306a36Sopenharmony_ci sin6.sin6_port = sin.sin_port; 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci if (copy_from_sockptr(&sin6, optval, 31962306a36Sopenharmony_ci sizeof(struct sockaddr_in6))) { 32062306a36Sopenharmony_ci ret = -EFAULT; 32162306a36Sopenharmony_ci goto out; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci rds_send_drop_to(rs, &sin6); 32662306a36Sopenharmony_ciout: 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int rds_set_bool_option(unsigned char *optvar, sockptr_t optval, 33162306a36Sopenharmony_ci int optlen) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci int value; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (optlen < sizeof(int)) 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci if (copy_from_sockptr(&value, optval, sizeof(int))) 33862306a36Sopenharmony_ci return -EFAULT; 33962306a36Sopenharmony_ci *optvar = !!value; 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int rds_cong_monitor(struct rds_sock *rs, sockptr_t optval, int optlen) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = rds_set_bool_option(&rs->rs_cong_monitor, optval, optlen); 34862306a36Sopenharmony_ci if (ret == 0) { 34962306a36Sopenharmony_ci if (rs->rs_cong_monitor) { 35062306a36Sopenharmony_ci rds_cong_add_socket(rs); 35162306a36Sopenharmony_ci } else { 35262306a36Sopenharmony_ci rds_cong_remove_socket(rs); 35362306a36Sopenharmony_ci rs->rs_cong_mask = 0; 35462306a36Sopenharmony_ci rs->rs_cong_notify = 0; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci return ret; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int rds_set_transport(struct rds_sock *rs, sockptr_t optval, int optlen) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci int t_type; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (rs->rs_transport) 36562306a36Sopenharmony_ci return -EOPNOTSUPP; /* previously attached to transport */ 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (optlen != sizeof(int)) 36862306a36Sopenharmony_ci return -EINVAL; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (copy_from_sockptr(&t_type, optval, sizeof(t_type))) 37162306a36Sopenharmony_ci return -EFAULT; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (t_type < 0 || t_type >= RDS_TRANS_COUNT) 37462306a36Sopenharmony_ci return -EINVAL; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci rs->rs_transport = rds_trans_get(t_type); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return rs->rs_transport ? 0 : -ENOPROTOOPT; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int rds_enable_recvtstamp(struct sock *sk, sockptr_t optval, 38262306a36Sopenharmony_ci int optlen, int optname) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci int val, valbool; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (optlen != sizeof(int)) 38762306a36Sopenharmony_ci return -EFAULT; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(int))) 39062306a36Sopenharmony_ci return -EFAULT; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci valbool = val ? 1 : 0; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (optname == SO_TIMESTAMP_NEW) 39562306a36Sopenharmony_ci sock_set_flag(sk, SOCK_TSTAMP_NEW); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (valbool) 39862306a36Sopenharmony_ci sock_set_flag(sk, SOCK_RCVTSTAMP); 39962306a36Sopenharmony_ci else 40062306a36Sopenharmony_ci sock_reset_flag(sk, SOCK_RCVTSTAMP); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int rds_recv_track_latency(struct rds_sock *rs, sockptr_t optval, 40662306a36Sopenharmony_ci int optlen) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct rds_rx_trace_so trace; 40962306a36Sopenharmony_ci int i; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (optlen != sizeof(struct rds_rx_trace_so)) 41262306a36Sopenharmony_ci return -EFAULT; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (copy_from_sockptr(&trace, optval, sizeof(trace))) 41562306a36Sopenharmony_ci return -EFAULT; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (trace.rx_traces > RDS_MSG_RX_DGRAM_TRACE_MAX) 41862306a36Sopenharmony_ci return -EFAULT; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci rs->rs_rx_traces = trace.rx_traces; 42162306a36Sopenharmony_ci for (i = 0; i < rs->rs_rx_traces; i++) { 42262306a36Sopenharmony_ci if (trace.rx_trace_pos[i] >= RDS_MSG_RX_DGRAM_TRACE_MAX) { 42362306a36Sopenharmony_ci rs->rs_rx_traces = 0; 42462306a36Sopenharmony_ci return -EFAULT; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci rs->rs_rx_trace[i] = trace.rx_trace_pos[i]; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int rds_setsockopt(struct socket *sock, int level, int optname, 43362306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct rds_sock *rs = rds_sk_to_rs(sock->sk); 43662306a36Sopenharmony_ci int ret; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (level != SOL_RDS) { 43962306a36Sopenharmony_ci ret = -ENOPROTOOPT; 44062306a36Sopenharmony_ci goto out; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci switch (optname) { 44462306a36Sopenharmony_ci case RDS_CANCEL_SENT_TO: 44562306a36Sopenharmony_ci ret = rds_cancel_sent_to(rs, optval, optlen); 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci case RDS_GET_MR: 44862306a36Sopenharmony_ci ret = rds_get_mr(rs, optval, optlen); 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case RDS_GET_MR_FOR_DEST: 45162306a36Sopenharmony_ci ret = rds_get_mr_for_dest(rs, optval, optlen); 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case RDS_FREE_MR: 45462306a36Sopenharmony_ci ret = rds_free_mr(rs, optval, optlen); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case RDS_RECVERR: 45762306a36Sopenharmony_ci ret = rds_set_bool_option(&rs->rs_recverr, optval, optlen); 45862306a36Sopenharmony_ci break; 45962306a36Sopenharmony_ci case RDS_CONG_MONITOR: 46062306a36Sopenharmony_ci ret = rds_cong_monitor(rs, optval, optlen); 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case SO_RDS_TRANSPORT: 46362306a36Sopenharmony_ci lock_sock(sock->sk); 46462306a36Sopenharmony_ci ret = rds_set_transport(rs, optval, optlen); 46562306a36Sopenharmony_ci release_sock(sock->sk); 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci case SO_TIMESTAMP_OLD: 46862306a36Sopenharmony_ci case SO_TIMESTAMP_NEW: 46962306a36Sopenharmony_ci lock_sock(sock->sk); 47062306a36Sopenharmony_ci ret = rds_enable_recvtstamp(sock->sk, optval, optlen, optname); 47162306a36Sopenharmony_ci release_sock(sock->sk); 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case SO_RDS_MSG_RXPATH_LATENCY: 47462306a36Sopenharmony_ci ret = rds_recv_track_latency(rs, optval, optlen); 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci default: 47762306a36Sopenharmony_ci ret = -ENOPROTOOPT; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ciout: 48062306a36Sopenharmony_ci return ret; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int rds_getsockopt(struct socket *sock, int level, int optname, 48462306a36Sopenharmony_ci char __user *optval, int __user *optlen) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct rds_sock *rs = rds_sk_to_rs(sock->sk); 48762306a36Sopenharmony_ci int ret = -ENOPROTOOPT, len; 48862306a36Sopenharmony_ci int trans; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (level != SOL_RDS) 49162306a36Sopenharmony_ci goto out; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (get_user(len, optlen)) { 49462306a36Sopenharmony_ci ret = -EFAULT; 49562306a36Sopenharmony_ci goto out; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci switch (optname) { 49962306a36Sopenharmony_ci case RDS_INFO_FIRST ... RDS_INFO_LAST: 50062306a36Sopenharmony_ci ret = rds_info_getsockopt(sock, optname, optval, 50162306a36Sopenharmony_ci optlen); 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci case RDS_RECVERR: 50562306a36Sopenharmony_ci if (len < sizeof(int)) 50662306a36Sopenharmony_ci ret = -EINVAL; 50762306a36Sopenharmony_ci else 50862306a36Sopenharmony_ci if (put_user(rs->rs_recverr, (int __user *) optval) || 50962306a36Sopenharmony_ci put_user(sizeof(int), optlen)) 51062306a36Sopenharmony_ci ret = -EFAULT; 51162306a36Sopenharmony_ci else 51262306a36Sopenharmony_ci ret = 0; 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci case SO_RDS_TRANSPORT: 51562306a36Sopenharmony_ci if (len < sizeof(int)) { 51662306a36Sopenharmony_ci ret = -EINVAL; 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci trans = (rs->rs_transport ? rs->rs_transport->t_type : 52062306a36Sopenharmony_ci RDS_TRANS_NONE); /* unbound */ 52162306a36Sopenharmony_ci if (put_user(trans, (int __user *)optval) || 52262306a36Sopenharmony_ci put_user(sizeof(int), optlen)) 52362306a36Sopenharmony_ci ret = -EFAULT; 52462306a36Sopenharmony_ci else 52562306a36Sopenharmony_ci ret = 0; 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci default: 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ciout: 53262306a36Sopenharmony_ci return ret; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int rds_connect(struct socket *sock, struct sockaddr *uaddr, 53762306a36Sopenharmony_ci int addr_len, int flags) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct sock *sk = sock->sk; 54062306a36Sopenharmony_ci struct sockaddr_in *sin; 54162306a36Sopenharmony_ci struct rds_sock *rs = rds_sk_to_rs(sk); 54262306a36Sopenharmony_ci int ret = 0; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (addr_len < offsetofend(struct sockaddr, sa_family)) 54562306a36Sopenharmony_ci return -EINVAL; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci lock_sock(sk); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci switch (uaddr->sa_family) { 55062306a36Sopenharmony_ci case AF_INET: 55162306a36Sopenharmony_ci sin = (struct sockaddr_in *)uaddr; 55262306a36Sopenharmony_ci if (addr_len < sizeof(struct sockaddr_in)) { 55362306a36Sopenharmony_ci ret = -EINVAL; 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci if (sin->sin_addr.s_addr == htonl(INADDR_ANY)) { 55762306a36Sopenharmony_ci ret = -EDESTADDRREQ; 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci if (ipv4_is_multicast(sin->sin_addr.s_addr) || 56162306a36Sopenharmony_ci sin->sin_addr.s_addr == htonl(INADDR_BROADCAST)) { 56262306a36Sopenharmony_ci ret = -EINVAL; 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &rs->rs_conn_addr); 56662306a36Sopenharmony_ci rs->rs_conn_port = sin->sin_port; 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 57062306a36Sopenharmony_ci case AF_INET6: { 57162306a36Sopenharmony_ci struct sockaddr_in6 *sin6; 57262306a36Sopenharmony_ci int addr_type; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci sin6 = (struct sockaddr_in6 *)uaddr; 57562306a36Sopenharmony_ci if (addr_len < sizeof(struct sockaddr_in6)) { 57662306a36Sopenharmony_ci ret = -EINVAL; 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci addr_type = ipv6_addr_type(&sin6->sin6_addr); 58062306a36Sopenharmony_ci if (!(addr_type & IPV6_ADDR_UNICAST)) { 58162306a36Sopenharmony_ci __be32 addr4; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (!(addr_type & IPV6_ADDR_MAPPED)) { 58462306a36Sopenharmony_ci ret = -EPROTOTYPE; 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* It is a mapped address. Need to do some sanity 58962306a36Sopenharmony_ci * checks. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_ci addr4 = sin6->sin6_addr.s6_addr32[3]; 59262306a36Sopenharmony_ci if (addr4 == htonl(INADDR_ANY) || 59362306a36Sopenharmony_ci addr4 == htonl(INADDR_BROADCAST) || 59462306a36Sopenharmony_ci ipv4_is_multicast(addr4)) { 59562306a36Sopenharmony_ci ret = -EPROTOTYPE; 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (addr_type & IPV6_ADDR_LINKLOCAL) { 60162306a36Sopenharmony_ci /* If socket is arleady bound to a link local address, 60262306a36Sopenharmony_ci * the peer address must be on the same link. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci if (sin6->sin6_scope_id == 0 || 60562306a36Sopenharmony_ci (!ipv6_addr_any(&rs->rs_bound_addr) && 60662306a36Sopenharmony_ci rs->rs_bound_scope_id && 60762306a36Sopenharmony_ci sin6->sin6_scope_id != rs->rs_bound_scope_id)) { 60862306a36Sopenharmony_ci ret = -EINVAL; 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci /* Remember the connected address scope ID. It will 61262306a36Sopenharmony_ci * be checked against the binding local address when 61362306a36Sopenharmony_ci * the socket is bound. 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_ci rs->rs_bound_scope_id = sin6->sin6_scope_id; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci rs->rs_conn_addr = sin6->sin6_addr; 61862306a36Sopenharmony_ci rs->rs_conn_port = sin6->sin6_port; 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci#endif 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci default: 62462306a36Sopenharmony_ci ret = -EAFNOSUPPORT; 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci release_sock(sk); 62962306a36Sopenharmony_ci return ret; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic struct proto rds_proto = { 63362306a36Sopenharmony_ci .name = "RDS", 63462306a36Sopenharmony_ci .owner = THIS_MODULE, 63562306a36Sopenharmony_ci .obj_size = sizeof(struct rds_sock), 63662306a36Sopenharmony_ci}; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic const struct proto_ops rds_proto_ops = { 63962306a36Sopenharmony_ci .family = AF_RDS, 64062306a36Sopenharmony_ci .owner = THIS_MODULE, 64162306a36Sopenharmony_ci .release = rds_release, 64262306a36Sopenharmony_ci .bind = rds_bind, 64362306a36Sopenharmony_ci .connect = rds_connect, 64462306a36Sopenharmony_ci .socketpair = sock_no_socketpair, 64562306a36Sopenharmony_ci .accept = sock_no_accept, 64662306a36Sopenharmony_ci .getname = rds_getname, 64762306a36Sopenharmony_ci .poll = rds_poll, 64862306a36Sopenharmony_ci .ioctl = rds_ioctl, 64962306a36Sopenharmony_ci .listen = sock_no_listen, 65062306a36Sopenharmony_ci .shutdown = sock_no_shutdown, 65162306a36Sopenharmony_ci .setsockopt = rds_setsockopt, 65262306a36Sopenharmony_ci .getsockopt = rds_getsockopt, 65362306a36Sopenharmony_ci .sendmsg = rds_sendmsg, 65462306a36Sopenharmony_ci .recvmsg = rds_recvmsg, 65562306a36Sopenharmony_ci .mmap = sock_no_mmap, 65662306a36Sopenharmony_ci}; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic void rds_sock_destruct(struct sock *sk) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct rds_sock *rs = rds_sk_to_rs(sk); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci WARN_ON((&rs->rs_item != rs->rs_item.next || 66362306a36Sopenharmony_ci &rs->rs_item != rs->rs_item.prev)); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int __rds_create(struct socket *sock, struct sock *sk, int protocol) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct rds_sock *rs; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci sock_init_data(sock, sk); 67162306a36Sopenharmony_ci sock->ops = &rds_proto_ops; 67262306a36Sopenharmony_ci sk->sk_protocol = protocol; 67362306a36Sopenharmony_ci sk->sk_destruct = rds_sock_destruct; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci rs = rds_sk_to_rs(sk); 67662306a36Sopenharmony_ci spin_lock_init(&rs->rs_lock); 67762306a36Sopenharmony_ci rwlock_init(&rs->rs_recv_lock); 67862306a36Sopenharmony_ci INIT_LIST_HEAD(&rs->rs_send_queue); 67962306a36Sopenharmony_ci INIT_LIST_HEAD(&rs->rs_recv_queue); 68062306a36Sopenharmony_ci INIT_LIST_HEAD(&rs->rs_notify_queue); 68162306a36Sopenharmony_ci INIT_LIST_HEAD(&rs->rs_cong_list); 68262306a36Sopenharmony_ci rds_message_zcopy_queue_init(&rs->rs_zcookie_queue); 68362306a36Sopenharmony_ci spin_lock_init(&rs->rs_rdma_lock); 68462306a36Sopenharmony_ci rs->rs_rdma_keys = RB_ROOT; 68562306a36Sopenharmony_ci rs->rs_rx_traces = 0; 68662306a36Sopenharmony_ci rs->rs_tos = 0; 68762306a36Sopenharmony_ci rs->rs_conn = NULL; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci spin_lock_bh(&rds_sock_lock); 69062306a36Sopenharmony_ci list_add_tail(&rs->rs_item, &rds_sock_list); 69162306a36Sopenharmony_ci rds_sock_count++; 69262306a36Sopenharmony_ci spin_unlock_bh(&rds_sock_lock); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return 0; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int rds_create(struct net *net, struct socket *sock, int protocol, 69862306a36Sopenharmony_ci int kern) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct sock *sk; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (sock->type != SOCK_SEQPACKET || protocol) 70362306a36Sopenharmony_ci return -ESOCKTNOSUPPORT; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci sk = sk_alloc(net, AF_RDS, GFP_KERNEL, &rds_proto, kern); 70662306a36Sopenharmony_ci if (!sk) 70762306a36Sopenharmony_ci return -ENOMEM; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return __rds_create(sock, sk, protocol); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_civoid rds_sock_addref(struct rds_sock *rs) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci sock_hold(rds_rs_to_sk(rs)); 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_civoid rds_sock_put(struct rds_sock *rs) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci sock_put(rds_rs_to_sk(rs)); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic const struct net_proto_family rds_family_ops = { 72362306a36Sopenharmony_ci .family = AF_RDS, 72462306a36Sopenharmony_ci .create = rds_create, 72562306a36Sopenharmony_ci .owner = THIS_MODULE, 72662306a36Sopenharmony_ci}; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic void rds_sock_inc_info(struct socket *sock, unsigned int len, 72962306a36Sopenharmony_ci struct rds_info_iterator *iter, 73062306a36Sopenharmony_ci struct rds_info_lengths *lens) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct rds_sock *rs; 73362306a36Sopenharmony_ci struct rds_incoming *inc; 73462306a36Sopenharmony_ci unsigned int total = 0; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci len /= sizeof(struct rds_info_message); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci spin_lock_bh(&rds_sock_lock); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci list_for_each_entry(rs, &rds_sock_list, rs_item) { 74162306a36Sopenharmony_ci /* This option only supports IPv4 sockets. */ 74262306a36Sopenharmony_ci if (!ipv6_addr_v4mapped(&rs->rs_bound_addr)) 74362306a36Sopenharmony_ci continue; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci read_lock(&rs->rs_recv_lock); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* XXX too lazy to maintain counts.. */ 74862306a36Sopenharmony_ci list_for_each_entry(inc, &rs->rs_recv_queue, i_item) { 74962306a36Sopenharmony_ci total++; 75062306a36Sopenharmony_ci if (total <= len) 75162306a36Sopenharmony_ci rds_inc_info_copy(inc, iter, 75262306a36Sopenharmony_ci inc->i_saddr.s6_addr32[3], 75362306a36Sopenharmony_ci rs->rs_bound_addr_v4, 75462306a36Sopenharmony_ci 1); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci read_unlock(&rs->rs_recv_lock); 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci spin_unlock_bh(&rds_sock_lock); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci lens->nr = total; 76362306a36Sopenharmony_ci lens->each = sizeof(struct rds_info_message); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 76762306a36Sopenharmony_cistatic void rds6_sock_inc_info(struct socket *sock, unsigned int len, 76862306a36Sopenharmony_ci struct rds_info_iterator *iter, 76962306a36Sopenharmony_ci struct rds_info_lengths *lens) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct rds_incoming *inc; 77262306a36Sopenharmony_ci unsigned int total = 0; 77362306a36Sopenharmony_ci struct rds_sock *rs; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci len /= sizeof(struct rds6_info_message); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci spin_lock_bh(&rds_sock_lock); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci list_for_each_entry(rs, &rds_sock_list, rs_item) { 78062306a36Sopenharmony_ci read_lock(&rs->rs_recv_lock); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci list_for_each_entry(inc, &rs->rs_recv_queue, i_item) { 78362306a36Sopenharmony_ci total++; 78462306a36Sopenharmony_ci if (total <= len) 78562306a36Sopenharmony_ci rds6_inc_info_copy(inc, iter, &inc->i_saddr, 78662306a36Sopenharmony_ci &rs->rs_bound_addr, 1); 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci read_unlock(&rs->rs_recv_lock); 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci spin_unlock_bh(&rds_sock_lock); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci lens->nr = total; 79562306a36Sopenharmony_ci lens->each = sizeof(struct rds6_info_message); 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci#endif 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic void rds_sock_info(struct socket *sock, unsigned int len, 80062306a36Sopenharmony_ci struct rds_info_iterator *iter, 80162306a36Sopenharmony_ci struct rds_info_lengths *lens) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct rds_info_socket sinfo; 80462306a36Sopenharmony_ci unsigned int cnt = 0; 80562306a36Sopenharmony_ci struct rds_sock *rs; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci len /= sizeof(struct rds_info_socket); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci spin_lock_bh(&rds_sock_lock); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (len < rds_sock_count) { 81262306a36Sopenharmony_ci cnt = rds_sock_count; 81362306a36Sopenharmony_ci goto out; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci list_for_each_entry(rs, &rds_sock_list, rs_item) { 81762306a36Sopenharmony_ci /* This option only supports IPv4 sockets. */ 81862306a36Sopenharmony_ci if (!ipv6_addr_v4mapped(&rs->rs_bound_addr)) 81962306a36Sopenharmony_ci continue; 82062306a36Sopenharmony_ci sinfo.sndbuf = rds_sk_sndbuf(rs); 82162306a36Sopenharmony_ci sinfo.rcvbuf = rds_sk_rcvbuf(rs); 82262306a36Sopenharmony_ci sinfo.bound_addr = rs->rs_bound_addr_v4; 82362306a36Sopenharmony_ci sinfo.connected_addr = rs->rs_conn_addr_v4; 82462306a36Sopenharmony_ci sinfo.bound_port = rs->rs_bound_port; 82562306a36Sopenharmony_ci sinfo.connected_port = rs->rs_conn_port; 82662306a36Sopenharmony_ci sinfo.inum = sock_i_ino(rds_rs_to_sk(rs)); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci rds_info_copy(iter, &sinfo, sizeof(sinfo)); 82962306a36Sopenharmony_ci cnt++; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ciout: 83362306a36Sopenharmony_ci lens->nr = cnt; 83462306a36Sopenharmony_ci lens->each = sizeof(struct rds_info_socket); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci spin_unlock_bh(&rds_sock_lock); 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 84062306a36Sopenharmony_cistatic void rds6_sock_info(struct socket *sock, unsigned int len, 84162306a36Sopenharmony_ci struct rds_info_iterator *iter, 84262306a36Sopenharmony_ci struct rds_info_lengths *lens) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct rds6_info_socket sinfo6; 84562306a36Sopenharmony_ci struct rds_sock *rs; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci len /= sizeof(struct rds6_info_socket); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci spin_lock_bh(&rds_sock_lock); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (len < rds_sock_count) 85262306a36Sopenharmony_ci goto out; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci list_for_each_entry(rs, &rds_sock_list, rs_item) { 85562306a36Sopenharmony_ci sinfo6.sndbuf = rds_sk_sndbuf(rs); 85662306a36Sopenharmony_ci sinfo6.rcvbuf = rds_sk_rcvbuf(rs); 85762306a36Sopenharmony_ci sinfo6.bound_addr = rs->rs_bound_addr; 85862306a36Sopenharmony_ci sinfo6.connected_addr = rs->rs_conn_addr; 85962306a36Sopenharmony_ci sinfo6.bound_port = rs->rs_bound_port; 86062306a36Sopenharmony_ci sinfo6.connected_port = rs->rs_conn_port; 86162306a36Sopenharmony_ci sinfo6.inum = sock_i_ino(rds_rs_to_sk(rs)); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci rds_info_copy(iter, &sinfo6, sizeof(sinfo6)); 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci out: 86762306a36Sopenharmony_ci lens->nr = rds_sock_count; 86862306a36Sopenharmony_ci lens->each = sizeof(struct rds6_info_socket); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci spin_unlock_bh(&rds_sock_lock); 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci#endif 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic void rds_exit(void) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci sock_unregister(rds_family_ops.family); 87762306a36Sopenharmony_ci proto_unregister(&rds_proto); 87862306a36Sopenharmony_ci rds_conn_exit(); 87962306a36Sopenharmony_ci rds_cong_exit(); 88062306a36Sopenharmony_ci rds_sysctl_exit(); 88162306a36Sopenharmony_ci rds_threads_exit(); 88262306a36Sopenharmony_ci rds_stats_exit(); 88362306a36Sopenharmony_ci rds_page_exit(); 88462306a36Sopenharmony_ci rds_bind_lock_destroy(); 88562306a36Sopenharmony_ci rds_info_deregister_func(RDS_INFO_SOCKETS, rds_sock_info); 88662306a36Sopenharmony_ci rds_info_deregister_func(RDS_INFO_RECV_MESSAGES, rds_sock_inc_info); 88762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 88862306a36Sopenharmony_ci rds_info_deregister_func(RDS6_INFO_SOCKETS, rds6_sock_info); 88962306a36Sopenharmony_ci rds_info_deregister_func(RDS6_INFO_RECV_MESSAGES, rds6_sock_inc_info); 89062306a36Sopenharmony_ci#endif 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_cimodule_exit(rds_exit); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ciu32 rds_gen_num; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic int __init rds_init(void) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci int ret; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci net_get_random_once(&rds_gen_num, sizeof(rds_gen_num)); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci ret = rds_bind_lock_init(); 90362306a36Sopenharmony_ci if (ret) 90462306a36Sopenharmony_ci goto out; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci ret = rds_conn_init(); 90762306a36Sopenharmony_ci if (ret) 90862306a36Sopenharmony_ci goto out_bind; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci ret = rds_threads_init(); 91162306a36Sopenharmony_ci if (ret) 91262306a36Sopenharmony_ci goto out_conn; 91362306a36Sopenharmony_ci ret = rds_sysctl_init(); 91462306a36Sopenharmony_ci if (ret) 91562306a36Sopenharmony_ci goto out_threads; 91662306a36Sopenharmony_ci ret = rds_stats_init(); 91762306a36Sopenharmony_ci if (ret) 91862306a36Sopenharmony_ci goto out_sysctl; 91962306a36Sopenharmony_ci ret = proto_register(&rds_proto, 1); 92062306a36Sopenharmony_ci if (ret) 92162306a36Sopenharmony_ci goto out_stats; 92262306a36Sopenharmony_ci ret = sock_register(&rds_family_ops); 92362306a36Sopenharmony_ci if (ret) 92462306a36Sopenharmony_ci goto out_proto; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci rds_info_register_func(RDS_INFO_SOCKETS, rds_sock_info); 92762306a36Sopenharmony_ci rds_info_register_func(RDS_INFO_RECV_MESSAGES, rds_sock_inc_info); 92862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 92962306a36Sopenharmony_ci rds_info_register_func(RDS6_INFO_SOCKETS, rds6_sock_info); 93062306a36Sopenharmony_ci rds_info_register_func(RDS6_INFO_RECV_MESSAGES, rds6_sock_inc_info); 93162306a36Sopenharmony_ci#endif 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci goto out; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ciout_proto: 93662306a36Sopenharmony_ci proto_unregister(&rds_proto); 93762306a36Sopenharmony_ciout_stats: 93862306a36Sopenharmony_ci rds_stats_exit(); 93962306a36Sopenharmony_ciout_sysctl: 94062306a36Sopenharmony_ci rds_sysctl_exit(); 94162306a36Sopenharmony_ciout_threads: 94262306a36Sopenharmony_ci rds_threads_exit(); 94362306a36Sopenharmony_ciout_conn: 94462306a36Sopenharmony_ci rds_conn_exit(); 94562306a36Sopenharmony_ci rds_cong_exit(); 94662306a36Sopenharmony_ci rds_page_exit(); 94762306a36Sopenharmony_ciout_bind: 94862306a36Sopenharmony_ci rds_bind_lock_destroy(); 94962306a36Sopenharmony_ciout: 95062306a36Sopenharmony_ci return ret; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_cimodule_init(rds_init); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci#define DRV_VERSION "4.0" 95562306a36Sopenharmony_ci#define DRV_RELDATE "Feb 12, 2009" 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ciMODULE_AUTHOR("Oracle Corporation <rds-devel@oss.oracle.com>"); 95862306a36Sopenharmony_ciMODULE_DESCRIPTION("RDS: Reliable Datagram Sockets" 95962306a36Sopenharmony_ci " v" DRV_VERSION " (" DRV_RELDATE ")"); 96062306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 96162306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 96262306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_RDS); 963