Commit 176bd861 authored by David S. Miller's avatar David S. Miller

Merge branch 'rds-ipv6'

Ka-Cheong Poon says:

====================
rds: IPv6 support

This patch set adds IPv6 support to the kernel RDS and related
modules.  Existing RDS apps using IPv4 address continue to run without
any problem.  New RDS apps which want to use IPv6 address can do so by
passing the address in struct sockaddr_in6 to bind(), connect() or
sendmsg().  And those apps also need to use the new IPv6 equivalents
of some of the existing socket options as the existing options use a
32 bit integer to store IP address.

All RDS code now use struct in6_addr to store IP address.  IPv4
address is stored as an IPv4 mapped address.

Header file changes

There are many data structures (RDS socket options) used by RDS apps
which use a 32 bit integer to store IP address. To support IPv6,
struct in6_addr needs to be used. To ensure backward compatibility, a
new data structure is introduced for each of those data structures
which use a 32 bit integer to represent an IP address. And new socket
options are introduced to use those new structures. This means that
existing apps should work without a problem with the new RDS module.
For apps which want to use IPv6, those new data structures and socket
options can be used. IPv4 mapped address is used to represent IPv4
address in the new data structures.

Internally, all RDS data structures which contain an IP address are
changed to use struct in6_addr to store the address. IPv4 address is
stored as an IPv4 mapped address. All the functions which take an IP
address as argument are also changed to use struct in6_addr.

RDS/RDMA/IB uses a private data (struct rds_ib_connect_private)
exchange between endpoints at RDS connection establishment time to
support RDMA. This private data exchange uses a 32 bit integer to
represent an IP address. This needs to be changed in order to support
IPv6. A new private data struct rds6_ib_connect_private is introduced
to handle this. To ensure backward compatibility, an IPv6 capable RDS
stack uses another RDMA listener port (RDS_CM_PORT) to accept IPv6
connection. And it continues to use the original RDS_PORT for IPv4 RDS
connections. When it needs to communicate with an IPv6 peer, it uses
the RDS_TCP_PORT to send the connection set up request.

RDS/TCP changes

TCP related code is changed to support IPv6.  Note that only an IPv6
TCP listener on port RDS_TCP_PORT is created as it can accept both
IPv4 and IPv6 connection requests.

IB/RDMA changes

The initial private data exchange between IB endpoints using RDMA is
changed to support IPv6 address instead, if the peer address is IPv6.
To ensure backward compatibility, annother RDMA listener port
(RDS_CM_PORT) is used to accept IPv6 connection. An IPv6 capable RDS
module continues to use the original RDS_PORT for IPv4 RDS
connections. When it needs to communicate with an IPv6 peer, it uses
the RDS_CM_PORT to send the connection set up request.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a6c90dd3 b7ff8b10
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR Linux-OpenIB) */ /* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR Linux-OpenIB) */
/* /*
* Copyright (c) 2008 Oracle. All rights reserved. * Copyright (c) 2008, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -118,7 +118,17 @@ ...@@ -118,7 +118,17 @@
#define RDS_INFO_IB_CONNECTIONS 10008 #define RDS_INFO_IB_CONNECTIONS 10008
#define RDS_INFO_CONNECTION_STATS 10009 #define RDS_INFO_CONNECTION_STATS 10009
#define RDS_INFO_IWARP_CONNECTIONS 10010 #define RDS_INFO_IWARP_CONNECTIONS 10010
#define RDS_INFO_LAST 10010
/* PF_RDS6 options */
#define RDS6_INFO_CONNECTIONS 10011
#define RDS6_INFO_SEND_MESSAGES 10012
#define RDS6_INFO_RETRANS_MESSAGES 10013
#define RDS6_INFO_RECV_MESSAGES 10014
#define RDS6_INFO_SOCKETS 10015
#define RDS6_INFO_TCP_SOCKETS 10016
#define RDS6_INFO_IB_CONNECTIONS 10017
#define RDS_INFO_LAST 10017
struct rds_info_counter { struct rds_info_counter {
__u8 name[32]; __u8 name[32];
...@@ -140,6 +150,15 @@ struct rds_info_connection { ...@@ -140,6 +150,15 @@ struct rds_info_connection {
__u8 flags; __u8 flags;
} __attribute__((packed)); } __attribute__((packed));
struct rds6_info_connection {
__u64 next_tx_seq;
__u64 next_rx_seq;
struct in6_addr laddr;
struct in6_addr faddr;
__u8 transport[TRANSNAMSIZ]; /* null term ascii */
__u8 flags;
} __attribute__((packed));
#define RDS_INFO_MESSAGE_FLAG_ACK 0x01 #define RDS_INFO_MESSAGE_FLAG_ACK 0x01
#define RDS_INFO_MESSAGE_FLAG_FAST_ACK 0x02 #define RDS_INFO_MESSAGE_FLAG_FAST_ACK 0x02
...@@ -153,6 +172,17 @@ struct rds_info_message { ...@@ -153,6 +172,17 @@ struct rds_info_message {
__u8 flags; __u8 flags;
} __attribute__((packed)); } __attribute__((packed));
struct rds6_info_message {
__u64 seq;
__u32 len;
struct in6_addr laddr;
struct in6_addr faddr;
__be16 lport;
__be16 fport;
__u8 flags;
__u8 tos;
} __attribute__((packed));
struct rds_info_socket { struct rds_info_socket {
__u32 sndbuf; __u32 sndbuf;
__be32 bound_addr; __be32 bound_addr;
...@@ -163,6 +193,16 @@ struct rds_info_socket { ...@@ -163,6 +193,16 @@ struct rds_info_socket {
__u64 inum; __u64 inum;
} __attribute__((packed)); } __attribute__((packed));
struct rds6_info_socket {
__u32 sndbuf;
struct in6_addr bound_addr;
struct in6_addr connected_addr;
__be16 bound_port;
__be16 connected_port;
__u32 rcvbuf;
__u64 inum;
} __attribute__((packed));
struct rds_info_tcp_socket { struct rds_info_tcp_socket {
__be32 local_addr; __be32 local_addr;
__be16 local_port; __be16 local_port;
...@@ -175,6 +215,18 @@ struct rds_info_tcp_socket { ...@@ -175,6 +215,18 @@ struct rds_info_tcp_socket {
__u32 last_seen_una; __u32 last_seen_una;
} __attribute__((packed)); } __attribute__((packed));
struct rds6_info_tcp_socket {
struct in6_addr local_addr;
__be16 local_port;
struct in6_addr peer_addr;
__be16 peer_port;
__u64 hdr_rem;
__u64 data_rem;
__u32 last_sent_nxt;
__u32 last_expected_una;
__u32 last_seen_una;
} __attribute__((packed));
#define RDS_IB_GID_LEN 16 #define RDS_IB_GID_LEN 16
struct rds_info_rdma_connection { struct rds_info_rdma_connection {
__be32 src_addr; __be32 src_addr;
...@@ -189,6 +241,19 @@ struct rds_info_rdma_connection { ...@@ -189,6 +241,19 @@ struct rds_info_rdma_connection {
__u32 rdma_mr_size; __u32 rdma_mr_size;
}; };
struct rds6_info_rdma_connection {
struct in6_addr src_addr;
struct in6_addr dst_addr;
__u8 src_gid[RDS_IB_GID_LEN];
__u8 dst_gid[RDS_IB_GID_LEN];
__u32 max_send_wr;
__u32 max_recv_wr;
__u32 max_send_sge;
__u32 rdma_mr_max;
__u32 rdma_mr_size;
};
/* RDS message Receive Path Latency points */ /* RDS message Receive Path Latency points */
enum rds_message_rxpath_latency { enum rds_message_rxpath_latency {
RDS_MSG_RX_HDR_TO_DGRAM_START = 0, RDS_MSG_RX_HDR_TO_DGRAM_START = 0,
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/ipv6.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <net/sock.h> #include <net/sock.h>
...@@ -113,26 +114,80 @@ void rds_wake_sk_sleep(struct rds_sock *rs) ...@@ -113,26 +114,80 @@ void rds_wake_sk_sleep(struct rds_sock *rs)
static int rds_getname(struct socket *sock, struct sockaddr *uaddr, static int rds_getname(struct socket *sock, struct sockaddr *uaddr,
int peer) int peer)
{ {
struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
struct rds_sock *rs = rds_sk_to_rs(sock->sk); struct rds_sock *rs = rds_sk_to_rs(sock->sk);
struct sockaddr_in6 *sin6;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); struct sockaddr_in *sin;
int uaddr_len;
/* racey, don't care */ /* racey, don't care */
if (peer) { if (peer) {
if (!rs->rs_conn_addr) if (ipv6_addr_any(&rs->rs_conn_addr))
return -ENOTCONN; return -ENOTCONN;
if (ipv6_addr_v4mapped(&rs->rs_conn_addr)) {
sin = (struct sockaddr_in *)uaddr;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
sin->sin_family = AF_INET;
sin->sin_port = rs->rs_conn_port; sin->sin_port = rs->rs_conn_port;
sin->sin_addr.s_addr = rs->rs_conn_addr; sin->sin_addr.s_addr = rs->rs_conn_addr_v4;
uaddr_len = sizeof(*sin);
} else { } else {
sin->sin_port = rs->rs_bound_port; sin6 = (struct sockaddr_in6 *)uaddr;
sin->sin_addr.s_addr = rs->rs_bound_addr; sin6->sin6_family = AF_INET6;
sin6->sin6_port = rs->rs_conn_port;
sin6->sin6_addr = rs->rs_conn_addr;
sin6->sin6_flowinfo = 0;
/* scope_id is the same as in the bound address. */
sin6->sin6_scope_id = rs->rs_bound_scope_id;
uaddr_len = sizeof(*sin6);
}
} else {
/* If socket is not yet bound and the socket is connected,
* set the return address family to be the same as the
* connected address, but with 0 address value. If it is not
* connected, set the family to be AF_UNSPEC (value 0) and
* the address size to be that of an IPv4 address.
*/
if (ipv6_addr_any(&rs->rs_bound_addr)) {
if (ipv6_addr_any(&rs->rs_conn_addr)) {
sin = (struct sockaddr_in *)uaddr;
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_UNSPEC;
return sizeof(*sin);
} }
if (ipv6_addr_type(&rs->rs_conn_addr) &
IPV6_ADDR_MAPPED) {
sin = (struct sockaddr_in *)uaddr;
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET; sin->sin_family = AF_INET;
return sizeof(*sin); return sizeof(*sin);
}
sin6 = (struct sockaddr_in6 *)uaddr;
memset(sin6, 0, sizeof(*sin6));
sin6->sin6_family = AF_INET6;
return sizeof(*sin6);
}
if (ipv6_addr_v4mapped(&rs->rs_bound_addr)) {
sin = (struct sockaddr_in *)uaddr;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
sin->sin_family = AF_INET;
sin->sin_port = rs->rs_bound_port;
sin->sin_addr.s_addr = rs->rs_bound_addr_v4;
uaddr_len = sizeof(*sin);
} else {
sin6 = (struct sockaddr_in6 *)uaddr;
sin6->sin6_family = AF_INET6;
sin6->sin6_port = rs->rs_bound_port;
sin6->sin6_addr = rs->rs_bound_addr;
sin6->sin6_flowinfo = 0;
sin6->sin6_scope_id = rs->rs_bound_scope_id;
uaddr_len = sizeof(*sin6);
}
}
return uaddr_len;
} }
/* /*
...@@ -203,11 +258,12 @@ static int rds_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -203,11 +258,12 @@ static int rds_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval, static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval,
int len) int len)
{ {
struct sockaddr_in6 sin6;
struct sockaddr_in sin; struct sockaddr_in sin;
int ret = 0; int ret = 0;
/* racing with another thread binding seems ok here */ /* racing with another thread binding seems ok here */
if (rs->rs_bound_addr == 0) { if (ipv6_addr_any(&rs->rs_bound_addr)) {
ret = -ENOTCONN; /* XXX not a great errno */ ret = -ENOTCONN; /* XXX not a great errno */
goto out; goto out;
} }
...@@ -215,14 +271,23 @@ static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval, ...@@ -215,14 +271,23 @@ static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval,
if (len < sizeof(struct sockaddr_in)) { if (len < sizeof(struct sockaddr_in)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} else if (len < sizeof(struct sockaddr_in6)) {
/* Assume IPv4 */
if (copy_from_user(&sin, optval, sizeof(struct sockaddr_in))) {
ret = -EFAULT;
goto out;
} }
ipv6_addr_set_v4mapped(sin.sin_addr.s_addr, &sin6.sin6_addr);
if (copy_from_user(&sin, optval, sizeof(sin))) { sin6.sin6_port = sin.sin_port;
} else {
if (copy_from_user(&sin6, optval,
sizeof(struct sockaddr_in6))) {
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;
} }
}
rds_send_drop_to(rs, &sin); rds_send_drop_to(rs, &sin6);
out: out:
return ret; return ret;
} }
...@@ -435,31 +500,87 @@ static int rds_connect(struct socket *sock, struct sockaddr *uaddr, ...@@ -435,31 +500,87 @@ static int rds_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags) int addr_len, int flags)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct sockaddr_in *sin = (struct sockaddr_in *)uaddr; struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
struct rds_sock *rs = rds_sk_to_rs(sk); struct rds_sock *rs = rds_sk_to_rs(sk);
int addr_type;
int ret = 0; int ret = 0;
lock_sock(sk); lock_sock(sk);
if (addr_len != sizeof(struct sockaddr_in)) { switch (uaddr->sa_family) {
case AF_INET:
sin = (struct sockaddr_in *)uaddr;
if (addr_len < sizeof(struct sockaddr_in)) {
ret = -EINVAL; ret = -EINVAL;
goto out; break;
}
if (sin->sin_addr.s_addr == htonl(INADDR_ANY)) {
ret = -EDESTADDRREQ;
break;
} }
if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) ||
sin->sin_addr.s_addr == htonl(INADDR_BROADCAST)) {
ret = -EINVAL;
break;
}
ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &rs->rs_conn_addr);
rs->rs_conn_port = sin->sin_port;
break;
if (sin->sin_family != AF_INET) { case AF_INET6:
ret = -EAFNOSUPPORT; sin6 = (struct sockaddr_in6 *)uaddr;
goto out; if (addr_len < sizeof(struct sockaddr_in6)) {
ret = -EINVAL;
break;
} }
addr_type = ipv6_addr_type(&sin6->sin6_addr);
if (!(addr_type & IPV6_ADDR_UNICAST)) {
__be32 addr4;
if (sin->sin_addr.s_addr == htonl(INADDR_ANY)) { if (!(addr_type & IPV6_ADDR_MAPPED)) {
ret = -EDESTADDRREQ; ret = -EPROTOTYPE;
goto out; break;
} }
rs->rs_conn_addr = sin->sin_addr.s_addr; /* It is a mapped address. Need to do some sanity
rs->rs_conn_port = sin->sin_port; * checks.
*/
addr4 = sin6->sin6_addr.s6_addr32[3];
if (addr4 == htonl(INADDR_ANY) ||
addr4 == htonl(INADDR_BROADCAST) ||
IN_MULTICAST(ntohl(addr4))) {
ret = -EPROTOTYPE;
break;
}
}
if (addr_type & IPV6_ADDR_LINKLOCAL) {
/* If socket is arleady bound to a link local address,
* the peer address must be on the same link.
*/
if (sin6->sin6_scope_id == 0 ||
(!ipv6_addr_any(&rs->rs_bound_addr) &&
rs->rs_bound_scope_id &&
sin6->sin6_scope_id != rs->rs_bound_scope_id)) {
ret = -EINVAL;
break;
}
/* Remember the connected address scope ID. It will
* be checked against the binding local address when
* the socket is bound.
*/
rs->rs_bound_scope_id = sin6->sin6_scope_id;
}
rs->rs_conn_addr = sin6->sin6_addr;
rs->rs_conn_port = sin6->sin6_port;
break;
default:
ret = -EAFNOSUPPORT;
break;
}
out:
release_sock(sk); release_sock(sk);
return ret; return ret;
} }
...@@ -578,8 +699,10 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len, ...@@ -578,8 +699,10 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len,
list_for_each_entry(inc, &rs->rs_recv_queue, i_item) { list_for_each_entry(inc, &rs->rs_recv_queue, i_item) {
total++; total++;
if (total <= len) if (total <= len)
rds_inc_info_copy(inc, iter, inc->i_saddr, rds_inc_info_copy(inc, iter,
rs->rs_bound_addr, 1); inc->i_saddr.s6_addr32[3],
rs->rs_bound_addr_v4,
1);
} }
read_unlock(&rs->rs_recv_lock); read_unlock(&rs->rs_recv_lock);
...@@ -608,8 +731,8 @@ static void rds_sock_info(struct socket *sock, unsigned int len, ...@@ -608,8 +731,8 @@ static void rds_sock_info(struct socket *sock, unsigned int len,
list_for_each_entry(rs, &rds_sock_list, rs_item) { list_for_each_entry(rs, &rds_sock_list, rs_item) {
sinfo.sndbuf = rds_sk_sndbuf(rs); sinfo.sndbuf = rds_sk_sndbuf(rs);
sinfo.rcvbuf = rds_sk_rcvbuf(rs); sinfo.rcvbuf = rds_sk_rcvbuf(rs);
sinfo.bound_addr = rs->rs_bound_addr; sinfo.bound_addr = rs->rs_bound_addr_v4;
sinfo.connected_addr = rs->rs_conn_addr; sinfo.connected_addr = rs->rs_conn_addr_v4;
sinfo.bound_port = rs->rs_bound_port; sinfo.bound_port = rs->rs_bound_port;
sinfo.connected_port = rs->rs_conn_port; sinfo.connected_port = rs->rs_conn_port;
sinfo.inum = sock_i_ino(rds_rs_to_sk(rs)); sinfo.inum = sock_i_ino(rds_rs_to_sk(rs));
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <net/sock.h> #include <net/sock.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/ipv6.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/jhash.h> #include <linux/jhash.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
...@@ -42,42 +43,58 @@ static struct rhashtable bind_hash_table; ...@@ -42,42 +43,58 @@ static struct rhashtable bind_hash_table;
static const struct rhashtable_params ht_parms = { static const struct rhashtable_params ht_parms = {
.nelem_hint = 768, .nelem_hint = 768,
.key_len = sizeof(u64), .key_len = RDS_BOUND_KEY_LEN,
.key_offset = offsetof(struct rds_sock, rs_bound_key), .key_offset = offsetof(struct rds_sock, rs_bound_key),
.head_offset = offsetof(struct rds_sock, rs_bound_node), .head_offset = offsetof(struct rds_sock, rs_bound_node),
.max_size = 16384, .max_size = 16384,
.min_size = 1024, .min_size = 1024,
}; };
/* Create a key for the bind hash table manipulation. Port is in network byte
* order.
*/
static inline void __rds_create_bind_key(u8 *key, const struct in6_addr *addr,
__be16 port, __u32 scope_id)
{
memcpy(key, addr, sizeof(*addr));
key += sizeof(*addr);
memcpy(key, &port, sizeof(port));
key += sizeof(port);
memcpy(key, &scope_id, sizeof(scope_id));
}
/* /*
* Return the rds_sock bound at the given local address. * Return the rds_sock bound at the given local address.
* *
* The rx path can race with rds_release. We notice if rds_release() has * The rx path can race with rds_release. We notice if rds_release() has
* marked this socket and don't return a rs ref to the rx path. * marked this socket and don't return a rs ref to the rx path.
*/ */
struct rds_sock *rds_find_bound(__be32 addr, __be16 port) struct rds_sock *rds_find_bound(const struct in6_addr *addr, __be16 port,
__u32 scope_id)
{ {
u64 key = ((u64)addr << 32) | port; u8 key[RDS_BOUND_KEY_LEN];
struct rds_sock *rs; struct rds_sock *rs;
rs = rhashtable_lookup_fast(&bind_hash_table, &key, ht_parms); __rds_create_bind_key(key, addr, port, scope_id);
rs = rhashtable_lookup_fast(&bind_hash_table, key, ht_parms);
if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD)) if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD))
rds_sock_addref(rs); rds_sock_addref(rs);
else else
rs = NULL; rs = NULL;
rdsdebug("returning rs %p for %pI4:%u\n", rs, &addr, rdsdebug("returning rs %p for %pI6c:%u\n", rs, addr,
ntohs(port)); ntohs(port));
return rs; return rs;
} }
/* returns -ve errno or +ve port */ /* returns -ve errno or +ve port */
static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) static int rds_add_bound(struct rds_sock *rs, const struct in6_addr *addr,
__be16 *port, __u32 scope_id)
{ {
int ret = -EADDRINUSE; int ret = -EADDRINUSE;
u16 rover, last; u16 rover, last;
u64 key; u8 key[RDS_BOUND_KEY_LEN];
if (*port != 0) { if (*port != 0) {
rover = be16_to_cpu(*port); rover = be16_to_cpu(*port);
...@@ -95,12 +112,13 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) ...@@ -95,12 +112,13 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
if (rover == RDS_FLAG_PROBE_PORT) if (rover == RDS_FLAG_PROBE_PORT)
continue; continue;
key = ((u64)addr << 32) | cpu_to_be16(rover); __rds_create_bind_key(key, addr, cpu_to_be16(rover),
if (rhashtable_lookup_fast(&bind_hash_table, &key, ht_parms)) scope_id);
if (rhashtable_lookup_fast(&bind_hash_table, key, ht_parms))
continue; continue;
rs->rs_bound_key = key; memcpy(rs->rs_bound_key, key, sizeof(rs->rs_bound_key));
rs->rs_bound_addr = addr; rs->rs_bound_addr = *addr;
net_get_random_once(&rs->rs_hash_initval, net_get_random_once(&rs->rs_hash_initval,
sizeof(rs->rs_hash_initval)); sizeof(rs->rs_hash_initval));
rs->rs_bound_port = cpu_to_be16(rover); rs->rs_bound_port = cpu_to_be16(rover);
...@@ -109,12 +127,13 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) ...@@ -109,12 +127,13 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
if (!rhashtable_insert_fast(&bind_hash_table, if (!rhashtable_insert_fast(&bind_hash_table,
&rs->rs_bound_node, ht_parms)) { &rs->rs_bound_node, ht_parms)) {
*port = rs->rs_bound_port; *port = rs->rs_bound_port;
rs->rs_bound_scope_id = scope_id;
ret = 0; ret = 0;
rdsdebug("rs %p binding to %pI4:%d\n", rdsdebug("rs %p binding to %pI6c:%d\n",
rs, &addr, (int)ntohs(*port)); rs, addr, (int)ntohs(*port));
break; break;
} else { } else {
rs->rs_bound_addr = 0; rs->rs_bound_addr = in6addr_any;
rds_sock_put(rs); rds_sock_put(rs);
ret = -ENOMEM; ret = -ENOMEM;
break; break;
...@@ -127,44 +146,101 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) ...@@ -127,44 +146,101 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
void rds_remove_bound(struct rds_sock *rs) void rds_remove_bound(struct rds_sock *rs)
{ {
if (!rs->rs_bound_addr) if (ipv6_addr_any(&rs->rs_bound_addr))
return; return;
rdsdebug("rs %p unbinding from %pI4:%d\n", rdsdebug("rs %p unbinding from %pI6c:%d\n",
rs, &rs->rs_bound_addr, rs, &rs->rs_bound_addr,
ntohs(rs->rs_bound_port)); ntohs(rs->rs_bound_port));
rhashtable_remove_fast(&bind_hash_table, &rs->rs_bound_node, ht_parms); rhashtable_remove_fast(&bind_hash_table, &rs->rs_bound_node, ht_parms);
rds_sock_put(rs); rds_sock_put(rs);
rs->rs_bound_addr = 0; rs->rs_bound_addr = in6addr_any;
} }
int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
struct rds_sock *rs = rds_sk_to_rs(sk); struct rds_sock *rs = rds_sk_to_rs(sk);
struct in6_addr v6addr, *binding_addr;
struct rds_transport *trans; struct rds_transport *trans;
__u32 scope_id = 0;
int addr_type;
int ret = 0; int ret = 0;
__be16 port;
/* We allow an RDS socket to be bound to either IPv4 or IPv6
* address.
*/
if (uaddr->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
if (addr_len < sizeof(struct sockaddr_in) ||
sin->sin_addr.s_addr == htonl(INADDR_ANY) ||
sin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
return -EINVAL;
ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &v6addr);
binding_addr = &v6addr;
port = sin->sin_port;
} else if (uaddr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)uaddr;
if (addr_len < sizeof(struct sockaddr_in6))
return -EINVAL;
addr_type = ipv6_addr_type(&sin6->sin6_addr);
if (!(addr_type & IPV6_ADDR_UNICAST)) {
__be32 addr4;
if (!(addr_type & IPV6_ADDR_MAPPED))
return -EINVAL;
/* It is a mapped address. Need to do some sanity
* checks.
*/
addr4 = sin6->sin6_addr.s6_addr32[3];
if (addr4 == htonl(INADDR_ANY) ||
addr4 == htonl(INADDR_BROADCAST) ||
IN_MULTICAST(ntohl(addr4)))
return -EINVAL;
}
/* The scope ID must be specified for link local address. */
if (addr_type & IPV6_ADDR_LINKLOCAL) {
if (sin6->sin6_scope_id == 0)
return -EINVAL;
scope_id = sin6->sin6_scope_id;
}
binding_addr = &sin6->sin6_addr;
port = sin6->sin6_port;
} else {
return -EINVAL;
}
lock_sock(sk); lock_sock(sk);
if (addr_len != sizeof(struct sockaddr_in) || /* RDS socket does not allow re-binding. */
sin->sin_family != AF_INET || if (!ipv6_addr_any(&rs->rs_bound_addr)) {
rs->rs_bound_addr || ret = -EINVAL;
sin->sin_addr.s_addr == htonl(INADDR_ANY)) { goto out;
}
/* Socket is connected. The binding address should have the same
* scope ID as the connected address, except the case when one is
* non-link local address (scope_id is 0).
*/
if (!ipv6_addr_any(&rs->rs_conn_addr) && scope_id &&
rs->rs_bound_scope_id &&
scope_id != rs->rs_bound_scope_id) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
ret = rds_add_bound(rs, sin->sin_addr.s_addr, &sin->sin_port); ret = rds_add_bound(rs, binding_addr, &port, scope_id);
if (ret) if (ret)
goto out; goto out;
if (rs->rs_transport) { /* previously bound */ if (rs->rs_transport) { /* previously bound */
trans = rs->rs_transport; trans = rs->rs_transport;
if (trans->laddr_check(sock_net(sock->sk), if (trans->laddr_check(sock_net(sock->sk),
sin->sin_addr.s_addr) != 0) { binding_addr, scope_id) != 0) {
ret = -ENOPROTOOPT; ret = -ENOPROTOOPT;
rds_remove_bound(rs); rds_remove_bound(rs);
} else { } else {
...@@ -172,13 +248,13 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -172,13 +248,13 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
} }
goto out; goto out;
} }
trans = rds_trans_get_preferred(sock_net(sock->sk), trans = rds_trans_get_preferred(sock_net(sock->sk), binding_addr,
sin->sin_addr.s_addr); scope_id);
if (!trans) { if (!trans) {
ret = -EADDRNOTAVAIL; ret = -EADDRNOTAVAIL;
rds_remove_bound(rs); rds_remove_bound(rs);
pr_info_ratelimited("RDS: %s could not find a transport for %pI4, load rds_tcp or rds_rdma?\n", pr_info_ratelimited("RDS: %s could not find a transport for %pI6c, load rds_tcp or rds_rdma?\n",
__func__, &sin->sin_addr.s_addr); __func__, binding_addr);
goto out; goto out;
} }
......
/* /*
* Copyright (c) 2007 Oracle. All rights reserved. * Copyright (c) 2007, 2017 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -101,7 +101,7 @@ static DEFINE_RWLOCK(rds_cong_monitor_lock); ...@@ -101,7 +101,7 @@ static DEFINE_RWLOCK(rds_cong_monitor_lock);
static DEFINE_SPINLOCK(rds_cong_lock); static DEFINE_SPINLOCK(rds_cong_lock);
static struct rb_root rds_cong_tree = RB_ROOT; static struct rb_root rds_cong_tree = RB_ROOT;
static struct rds_cong_map *rds_cong_tree_walk(__be32 addr, static struct rds_cong_map *rds_cong_tree_walk(const struct in6_addr *addr,
struct rds_cong_map *insert) struct rds_cong_map *insert)
{ {
struct rb_node **p = &rds_cong_tree.rb_node; struct rb_node **p = &rds_cong_tree.rb_node;
...@@ -109,12 +109,15 @@ static struct rds_cong_map *rds_cong_tree_walk(__be32 addr, ...@@ -109,12 +109,15 @@ static struct rds_cong_map *rds_cong_tree_walk(__be32 addr,
struct rds_cong_map *map; struct rds_cong_map *map;
while (*p) { while (*p) {
int diff;
parent = *p; parent = *p;
map = rb_entry(parent, struct rds_cong_map, m_rb_node); map = rb_entry(parent, struct rds_cong_map, m_rb_node);
if (addr < map->m_addr) diff = rds_addr_cmp(addr, &map->m_addr);
if (diff < 0)
p = &(*p)->rb_left; p = &(*p)->rb_left;
else if (addr > map->m_addr) else if (diff > 0)
p = &(*p)->rb_right; p = &(*p)->rb_right;
else else
return map; return map;
...@@ -132,7 +135,7 @@ static struct rds_cong_map *rds_cong_tree_walk(__be32 addr, ...@@ -132,7 +135,7 @@ static struct rds_cong_map *rds_cong_tree_walk(__be32 addr,
* these bitmaps in the process getting pointers to them. The bitmaps are only * these bitmaps in the process getting pointers to them. The bitmaps are only
* ever freed as the module is removed after all connections have been freed. * ever freed as the module is removed after all connections have been freed.
*/ */
static struct rds_cong_map *rds_cong_from_addr(__be32 addr) static struct rds_cong_map *rds_cong_from_addr(const struct in6_addr *addr)
{ {
struct rds_cong_map *map; struct rds_cong_map *map;
struct rds_cong_map *ret = NULL; struct rds_cong_map *ret = NULL;
...@@ -144,7 +147,7 @@ static struct rds_cong_map *rds_cong_from_addr(__be32 addr) ...@@ -144,7 +147,7 @@ static struct rds_cong_map *rds_cong_from_addr(__be32 addr)
if (!map) if (!map)
return NULL; return NULL;
map->m_addr = addr; map->m_addr = *addr;
init_waitqueue_head(&map->m_waitq); init_waitqueue_head(&map->m_waitq);
INIT_LIST_HEAD(&map->m_conn_list); INIT_LIST_HEAD(&map->m_conn_list);
...@@ -171,7 +174,7 @@ static struct rds_cong_map *rds_cong_from_addr(__be32 addr) ...@@ -171,7 +174,7 @@ static struct rds_cong_map *rds_cong_from_addr(__be32 addr)
kfree(map); kfree(map);
} }
rdsdebug("map %p for addr %x\n", ret, be32_to_cpu(addr)); rdsdebug("map %p for addr %pI6c\n", ret, addr);
return ret; return ret;
} }
...@@ -202,8 +205,8 @@ void rds_cong_remove_conn(struct rds_connection *conn) ...@@ -202,8 +205,8 @@ void rds_cong_remove_conn(struct rds_connection *conn)
int rds_cong_get_maps(struct rds_connection *conn) int rds_cong_get_maps(struct rds_connection *conn)
{ {
conn->c_lcong = rds_cong_from_addr(conn->c_laddr); conn->c_lcong = rds_cong_from_addr(&conn->c_laddr);
conn->c_fcong = rds_cong_from_addr(conn->c_faddr); conn->c_fcong = rds_cong_from_addr(&conn->c_faddr);
if (!(conn->c_lcong && conn->c_fcong)) if (!(conn->c_lcong && conn->c_fcong))
return -ENOMEM; return -ENOMEM;
...@@ -353,7 +356,7 @@ void rds_cong_remove_socket(struct rds_sock *rs) ...@@ -353,7 +356,7 @@ void rds_cong_remove_socket(struct rds_sock *rs)
/* update congestion map for now-closed port */ /* update congestion map for now-closed port */
spin_lock_irqsave(&rds_cong_lock, flags); spin_lock_irqsave(&rds_cong_lock, flags);
map = rds_cong_tree_walk(rs->rs_bound_addr, NULL); map = rds_cong_tree_walk(&rs->rs_bound_addr, NULL);
spin_unlock_irqrestore(&rds_cong_lock, flags); spin_unlock_irqrestore(&rds_cong_lock, flags);
if (map && rds_cong_test_bit(map, rs->rs_bound_port)) { if (map && rds_cong_test_bit(map, rs->rs_bound_port)) {
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -34,7 +34,9 @@ ...@@ -34,7 +34,9 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/export.h> #include <linux/export.h>
#include <net/inet_hashtables.h> #include <net/ipv6.h>
#include <net/inet6_hashtables.h>
#include <net/addrconf.h>
#include "rds.h" #include "rds.h"
#include "loop.h" #include "loop.h"
...@@ -49,18 +51,21 @@ static unsigned long rds_conn_count; ...@@ -49,18 +51,21 @@ static unsigned long rds_conn_count;
static struct hlist_head rds_conn_hash[RDS_CONNECTION_HASH_ENTRIES]; static struct hlist_head rds_conn_hash[RDS_CONNECTION_HASH_ENTRIES];
static struct kmem_cache *rds_conn_slab; static struct kmem_cache *rds_conn_slab;
static struct hlist_head *rds_conn_bucket(__be32 laddr, __be32 faddr) static struct hlist_head *rds_conn_bucket(const struct in6_addr *laddr,
const struct in6_addr *faddr)
{ {
static u32 rds6_hash_secret __read_mostly;
static u32 rds_hash_secret __read_mostly; static u32 rds_hash_secret __read_mostly;
unsigned long hash; u32 lhash, fhash, hash;
net_get_random_once(&rds_hash_secret, sizeof(rds_hash_secret)); net_get_random_once(&rds_hash_secret, sizeof(rds_hash_secret));
net_get_random_once(&rds6_hash_secret, sizeof(rds6_hash_secret));
lhash = (__force u32)laddr->s6_addr32[3];
fhash = __ipv6_addr_jhash(faddr, rds6_hash_secret);
hash = __inet6_ehashfn(lhash, 0, fhash, 0, rds_hash_secret);
/* Pass NULL, don't need struct net for hash */
hash = __inet_ehashfn(be32_to_cpu(laddr), 0,
be32_to_cpu(faddr), 0,
rds_hash_secret);
return &rds_conn_hash[hash & RDS_CONNECTION_HASH_MASK]; return &rds_conn_hash[hash & RDS_CONNECTION_HASH_MASK];
} }
...@@ -72,20 +77,25 @@ static struct hlist_head *rds_conn_bucket(__be32 laddr, __be32 faddr) ...@@ -72,20 +77,25 @@ static struct hlist_head *rds_conn_bucket(__be32 laddr, __be32 faddr)
/* rcu read lock must be held or the connection spinlock */ /* rcu read lock must be held or the connection spinlock */
static struct rds_connection *rds_conn_lookup(struct net *net, static struct rds_connection *rds_conn_lookup(struct net *net,
struct hlist_head *head, struct hlist_head *head,
__be32 laddr, __be32 faddr, const struct in6_addr *laddr,
struct rds_transport *trans) const struct in6_addr *faddr,
struct rds_transport *trans,
int dev_if)
{ {
struct rds_connection *conn, *ret = NULL; struct rds_connection *conn, *ret = NULL;
hlist_for_each_entry_rcu(conn, head, c_hash_node) { hlist_for_each_entry_rcu(conn, head, c_hash_node) {
if (conn->c_faddr == faddr && conn->c_laddr == laddr && if (ipv6_addr_equal(&conn->c_faddr, faddr) &&
conn->c_trans == trans && net == rds_conn_net(conn)) { ipv6_addr_equal(&conn->c_laddr, laddr) &&
conn->c_trans == trans &&
net == rds_conn_net(conn) &&
conn->c_dev_if == dev_if) {
ret = conn; ret = conn;
break; break;
} }
} }
rdsdebug("returning conn %p for %pI4 -> %pI4\n", ret, rdsdebug("returning conn %p for %pI6c -> %pI6c\n", ret,
&laddr, &faddr); laddr, faddr);
return ret; return ret;
} }
...@@ -99,7 +109,7 @@ static void rds_conn_path_reset(struct rds_conn_path *cp) ...@@ -99,7 +109,7 @@ static void rds_conn_path_reset(struct rds_conn_path *cp)
{ {
struct rds_connection *conn = cp->cp_conn; struct rds_connection *conn = cp->cp_conn;
rdsdebug("connection %pI4 to %pI4 reset\n", rdsdebug("connection %pI6c to %pI6c reset\n",
&conn->c_laddr, &conn->c_faddr); &conn->c_laddr, &conn->c_faddr);
rds_stats_inc(s_conn_reset); rds_stats_inc(s_conn_reset);
...@@ -142,9 +152,12 @@ static void __rds_conn_path_init(struct rds_connection *conn, ...@@ -142,9 +152,12 @@ static void __rds_conn_path_init(struct rds_connection *conn,
* are torn down as the module is removed, if ever. * are torn down as the module is removed, if ever.
*/ */
static struct rds_connection *__rds_conn_create(struct net *net, static struct rds_connection *__rds_conn_create(struct net *net,
__be32 laddr, __be32 faddr, const struct in6_addr *laddr,
struct rds_transport *trans, gfp_t gfp, const struct in6_addr *faddr,
int is_outgoing) struct rds_transport *trans,
gfp_t gfp,
int is_outgoing,
int dev_if)
{ {
struct rds_connection *conn, *parent = NULL; struct rds_connection *conn, *parent = NULL;
struct hlist_head *head = rds_conn_bucket(laddr, faddr); struct hlist_head *head = rds_conn_bucket(laddr, faddr);
...@@ -154,9 +167,12 @@ static struct rds_connection *__rds_conn_create(struct net *net, ...@@ -154,9 +167,12 @@ static struct rds_connection *__rds_conn_create(struct net *net,
int npaths = (trans->t_mp_capable ? RDS_MPATH_WORKERS : 1); int npaths = (trans->t_mp_capable ? RDS_MPATH_WORKERS : 1);
rcu_read_lock(); rcu_read_lock();
conn = rds_conn_lookup(net, head, laddr, faddr, trans); conn = rds_conn_lookup(net, head, laddr, faddr, trans, dev_if);
if (conn && conn->c_loopback && conn->c_trans != &rds_loop_transport && if (conn &&
laddr == faddr && !is_outgoing) { conn->c_loopback &&
conn->c_trans != &rds_loop_transport &&
ipv6_addr_equal(laddr, faddr) &&
!is_outgoing) {
/* This is a looped back IB connection, and we're /* This is a looped back IB connection, and we're
* called by the code handling the incoming connect. * called by the code handling the incoming connect.
* We need a second connection object into which we * We need a second connection object into which we
...@@ -181,8 +197,19 @@ static struct rds_connection *__rds_conn_create(struct net *net, ...@@ -181,8 +197,19 @@ static struct rds_connection *__rds_conn_create(struct net *net,
} }
INIT_HLIST_NODE(&conn->c_hash_node); INIT_HLIST_NODE(&conn->c_hash_node);
conn->c_laddr = laddr; conn->c_laddr = *laddr;
conn->c_faddr = faddr; conn->c_isv6 = !ipv6_addr_v4mapped(laddr);
conn->c_faddr = *faddr;
conn->c_dev_if = dev_if;
/* If the local address is link local, set c_bound_if to be the
* index used for this connection. Otherwise, set it to 0 as
* the socket is not bound to an interface. c_bound_if is used
* to look up a socket when a packet is received
*/
if (ipv6_addr_type(laddr) & IPV6_ADDR_LINKLOCAL)
conn->c_bound_if = dev_if;
else
conn->c_bound_if = 0;
rds_conn_net_set(conn, net); rds_conn_net_set(conn, net);
...@@ -199,7 +226,7 @@ static struct rds_connection *__rds_conn_create(struct net *net, ...@@ -199,7 +226,7 @@ static struct rds_connection *__rds_conn_create(struct net *net,
* can bind to the destination address then we'd rather the messages * can bind to the destination address then we'd rather the messages
* flow through loopback rather than either transport. * flow through loopback rather than either transport.
*/ */
loop_trans = rds_trans_get_preferred(net, faddr); loop_trans = rds_trans_get_preferred(net, faddr, conn->c_dev_if);
if (loop_trans) { if (loop_trans) {
rds_trans_put(loop_trans); rds_trans_put(loop_trans);
conn->c_loopback = 1; conn->c_loopback = 1;
...@@ -233,10 +260,10 @@ static struct rds_connection *__rds_conn_create(struct net *net, ...@@ -233,10 +260,10 @@ static struct rds_connection *__rds_conn_create(struct net *net,
goto out; goto out;
} }
rdsdebug("allocated conn %p for %pI4 -> %pI4 over %s %s\n", rdsdebug("allocated conn %p for %pI6c -> %pI6c over %s %s\n",
conn, &laddr, &faddr, conn, laddr, faddr,
strnlen(trans->t_name, sizeof(trans->t_name)) ? trans->t_name : strnlen(trans->t_name, sizeof(trans->t_name)) ?
"[unknown]", is_outgoing ? "(outgoing)" : ""); trans->t_name : "[unknown]", is_outgoing ? "(outgoing)" : "");
/* /*
* Since we ran without holding the conn lock, someone could * Since we ran without holding the conn lock, someone could
...@@ -262,7 +289,8 @@ static struct rds_connection *__rds_conn_create(struct net *net, ...@@ -262,7 +289,8 @@ static struct rds_connection *__rds_conn_create(struct net *net,
/* Creating normal conn */ /* Creating normal conn */
struct rds_connection *found; struct rds_connection *found;
found = rds_conn_lookup(net, head, laddr, faddr, trans); found = rds_conn_lookup(net, head, laddr, faddr, trans,
dev_if);
if (found) { if (found) {
struct rds_conn_path *cp; struct rds_conn_path *cp;
int i; int i;
...@@ -295,18 +323,22 @@ static struct rds_connection *__rds_conn_create(struct net *net, ...@@ -295,18 +323,22 @@ static struct rds_connection *__rds_conn_create(struct net *net,
} }
struct rds_connection *rds_conn_create(struct net *net, struct rds_connection *rds_conn_create(struct net *net,
__be32 laddr, __be32 faddr, const struct in6_addr *laddr,
struct rds_transport *trans, gfp_t gfp) const struct in6_addr *faddr,
struct rds_transport *trans, gfp_t gfp,
int dev_if)
{ {
return __rds_conn_create(net, laddr, faddr, trans, gfp, 0); return __rds_conn_create(net, laddr, faddr, trans, gfp, 0, dev_if);
} }
EXPORT_SYMBOL_GPL(rds_conn_create); EXPORT_SYMBOL_GPL(rds_conn_create);
struct rds_connection *rds_conn_create_outgoing(struct net *net, struct rds_connection *rds_conn_create_outgoing(struct net *net,
__be32 laddr, __be32 faddr, const struct in6_addr *laddr,
struct rds_transport *trans, gfp_t gfp) const struct in6_addr *faddr,
struct rds_transport *trans,
gfp_t gfp, int dev_if)
{ {
return __rds_conn_create(net, laddr, faddr, trans, gfp, 1); return __rds_conn_create(net, laddr, faddr, trans, gfp, 1, dev_if);
} }
EXPORT_SYMBOL_GPL(rds_conn_create_outgoing); EXPORT_SYMBOL_GPL(rds_conn_create_outgoing);
...@@ -464,10 +496,21 @@ void rds_conn_destroy(struct rds_connection *conn) ...@@ -464,10 +496,21 @@ void rds_conn_destroy(struct rds_connection *conn)
} }
EXPORT_SYMBOL_GPL(rds_conn_destroy); EXPORT_SYMBOL_GPL(rds_conn_destroy);
static void rds_conn_message_info(struct socket *sock, unsigned int len, static void __rds_inc_msg_cp(struct rds_incoming *inc,
struct rds_info_iterator *iter,
void *saddr, void *daddr, int flip, bool isv6)
{
if (isv6)
rds6_inc_info_copy(inc, iter, saddr, daddr, flip);
else
rds_inc_info_copy(inc, iter, *(__be32 *)saddr,
*(__be32 *)daddr, flip);
}
static void rds_conn_message_info_cmn(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter, struct rds_info_iterator *iter,
struct rds_info_lengths *lens, struct rds_info_lengths *lens,
int want_send) int want_send, bool isv6)
{ {
struct hlist_head *head; struct hlist_head *head;
struct list_head *list; struct list_head *list;
...@@ -478,6 +521,9 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len, ...@@ -478,6 +521,9 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len,
size_t i; size_t i;
int j; int j;
if (isv6)
len /= sizeof(struct rds6_info_message);
else
len /= sizeof(struct rds_info_message); len /= sizeof(struct rds_info_message);
rcu_read_lock(); rcu_read_lock();
...@@ -488,6 +534,9 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len, ...@@ -488,6 +534,9 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len,
struct rds_conn_path *cp; struct rds_conn_path *cp;
int npaths; int npaths;
if (!isv6 && conn->c_isv6)
continue;
npaths = (conn->c_trans->t_mp_capable ? npaths = (conn->c_trans->t_mp_capable ?
RDS_MPATH_WORKERS : 1); RDS_MPATH_WORKERS : 1);
...@@ -504,11 +553,11 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len, ...@@ -504,11 +553,11 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len,
list_for_each_entry(rm, list, m_conn_item) { list_for_each_entry(rm, list, m_conn_item) {
total++; total++;
if (total <= len) if (total <= len)
rds_inc_info_copy(&rm->m_inc, __rds_inc_msg_cp(&rm->m_inc,
iter, iter,
conn->c_laddr, &conn->c_laddr,
conn->c_faddr, &conn->c_faddr,
0); 0, isv6);
} }
spin_unlock_irqrestore(&cp->cp_lock, flags); spin_unlock_irqrestore(&cp->cp_lock, flags);
...@@ -518,9 +567,28 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len, ...@@ -518,9 +567,28 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len,
rcu_read_unlock(); rcu_read_unlock();
lens->nr = total; lens->nr = total;
if (isv6)
lens->each = sizeof(struct rds6_info_message);
else
lens->each = sizeof(struct rds_info_message); lens->each = sizeof(struct rds_info_message);
} }
static void rds_conn_message_info(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter,
struct rds_info_lengths *lens,
int want_send)
{
rds_conn_message_info_cmn(sock, len, iter, lens, want_send, false);
}
static void rds6_conn_message_info(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter,
struct rds_info_lengths *lens,
int want_send)
{
rds_conn_message_info_cmn(sock, len, iter, lens, want_send, true);
}
static void rds_conn_message_info_send(struct socket *sock, unsigned int len, static void rds_conn_message_info_send(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter, struct rds_info_iterator *iter,
struct rds_info_lengths *lens) struct rds_info_lengths *lens)
...@@ -528,6 +596,13 @@ static void rds_conn_message_info_send(struct socket *sock, unsigned int len, ...@@ -528,6 +596,13 @@ static void rds_conn_message_info_send(struct socket *sock, unsigned int len,
rds_conn_message_info(sock, len, iter, lens, 1); rds_conn_message_info(sock, len, iter, lens, 1);
} }
static void rds6_conn_message_info_send(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter,
struct rds_info_lengths *lens)
{
rds6_conn_message_info(sock, len, iter, lens, 1);
}
static void rds_conn_message_info_retrans(struct socket *sock, static void rds_conn_message_info_retrans(struct socket *sock,
unsigned int len, unsigned int len,
struct rds_info_iterator *iter, struct rds_info_iterator *iter,
...@@ -536,6 +611,14 @@ static void rds_conn_message_info_retrans(struct socket *sock, ...@@ -536,6 +611,14 @@ static void rds_conn_message_info_retrans(struct socket *sock,
rds_conn_message_info(sock, len, iter, lens, 0); rds_conn_message_info(sock, len, iter, lens, 0);
} }
static void rds6_conn_message_info_retrans(struct socket *sock,
unsigned int len,
struct rds_info_iterator *iter,
struct rds_info_lengths *lens)
{
rds6_conn_message_info(sock, len, iter, lens, 0);
}
void rds_for_each_conn_info(struct socket *sock, unsigned int len, void rds_for_each_conn_info(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter, struct rds_info_iterator *iter,
struct rds_info_lengths *lens, struct rds_info_lengths *lens,
...@@ -584,7 +667,6 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len, ...@@ -584,7 +667,6 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len,
struct hlist_head *head; struct hlist_head *head;
struct rds_connection *conn; struct rds_connection *conn;
size_t i; size_t i;
int j;
rcu_read_lock(); rcu_read_lock();
...@@ -595,17 +677,20 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len, ...@@ -595,17 +677,20 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len,
i++, head++) { i++, head++) {
hlist_for_each_entry_rcu(conn, head, c_hash_node) { hlist_for_each_entry_rcu(conn, head, c_hash_node) {
struct rds_conn_path *cp; struct rds_conn_path *cp;
int npaths;
npaths = (conn->c_trans->t_mp_capable ? /* XXX We only copy the information from the first
RDS_MPATH_WORKERS : 1); * path for now. The problem is that if there are
for (j = 0; j < npaths; j++) { * more than one underlying paths, we cannot report
cp = &conn->c_path[j]; * information of all of them using the existing
* API. For example, there is only one next_tx_seq,
* which path's next_tx_seq should we report? It is
* a bug in the design of MPRDS.
*/
cp = conn->c_path;
/* XXX no cp_lock usage.. */ /* XXX no cp_lock usage.. */
if (!visitor(cp, buffer)) if (!visitor(cp, buffer))
continue; continue;
}
/* We copy as much as we can fit in the buffer, /* We copy as much as we can fit in the buffer,
* but we count all items so that the caller * but we count all items so that the caller
...@@ -624,12 +709,16 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len, ...@@ -624,12 +709,16 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len,
static int rds_conn_info_visitor(struct rds_conn_path *cp, void *buffer) static int rds_conn_info_visitor(struct rds_conn_path *cp, void *buffer)
{ {
struct rds_info_connection *cinfo = buffer; struct rds_info_connection *cinfo = buffer;
struct rds_connection *conn = cp->cp_conn;
if (conn->c_isv6)
return 0;
cinfo->next_tx_seq = cp->cp_next_tx_seq; cinfo->next_tx_seq = cp->cp_next_tx_seq;
cinfo->next_rx_seq = cp->cp_next_rx_seq; cinfo->next_rx_seq = cp->cp_next_rx_seq;
cinfo->laddr = cp->cp_conn->c_laddr; cinfo->laddr = conn->c_laddr.s6_addr32[3];
cinfo->faddr = cp->cp_conn->c_faddr; cinfo->faddr = conn->c_faddr.s6_addr32[3];
strncpy(cinfo->transport, cp->cp_conn->c_trans->t_name, strncpy(cinfo->transport, conn->c_trans->t_name,
sizeof(cinfo->transport)); sizeof(cinfo->transport));
cinfo->flags = 0; cinfo->flags = 0;
...@@ -645,6 +734,34 @@ static int rds_conn_info_visitor(struct rds_conn_path *cp, void *buffer) ...@@ -645,6 +734,34 @@ static int rds_conn_info_visitor(struct rds_conn_path *cp, void *buffer)
return 1; return 1;
} }
static int rds6_conn_info_visitor(struct rds_conn_path *cp, void *buffer)
{
struct rds6_info_connection *cinfo6 = buffer;
struct rds_connection *conn = cp->cp_conn;
cinfo6->next_tx_seq = cp->cp_next_tx_seq;
cinfo6->next_rx_seq = cp->cp_next_rx_seq;
cinfo6->laddr = conn->c_laddr;
cinfo6->faddr = conn->c_faddr;
strncpy(cinfo6->transport, conn->c_trans->t_name,
sizeof(cinfo6->transport));
cinfo6->flags = 0;
rds_conn_info_set(cinfo6->flags, test_bit(RDS_IN_XMIT, &cp->cp_flags),
SENDING);
/* XXX Future: return the state rather than these funky bits */
rds_conn_info_set(cinfo6->flags,
atomic_read(&cp->cp_state) == RDS_CONN_CONNECTING,
CONNECTING);
rds_conn_info_set(cinfo6->flags,
atomic_read(&cp->cp_state) == RDS_CONN_UP,
CONNECTED);
/* Just return 1 as there is no error case. This is a helper function
* for rds_walk_conn_path_info() and it wants a return value.
*/
return 1;
}
static void rds_conn_info(struct socket *sock, unsigned int len, static void rds_conn_info(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter, struct rds_info_iterator *iter,
struct rds_info_lengths *lens) struct rds_info_lengths *lens)
...@@ -657,6 +774,18 @@ static void rds_conn_info(struct socket *sock, unsigned int len, ...@@ -657,6 +774,18 @@ static void rds_conn_info(struct socket *sock, unsigned int len,
sizeof(struct rds_info_connection)); sizeof(struct rds_info_connection));
} }
static void rds6_conn_info(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter,
struct rds_info_lengths *lens)
{
u64 buffer[(sizeof(struct rds6_info_connection) + 7) / 8];
rds_walk_conn_path_info(sock, len, iter, lens,
rds6_conn_info_visitor,
buffer,
sizeof(struct rds6_info_connection));
}
int rds_conn_init(void) int rds_conn_init(void)
{ {
int ret; int ret;
...@@ -678,6 +807,11 @@ int rds_conn_init(void) ...@@ -678,6 +807,11 @@ int rds_conn_init(void)
rds_conn_message_info_send); rds_conn_message_info_send);
rds_info_register_func(RDS_INFO_RETRANS_MESSAGES, rds_info_register_func(RDS_INFO_RETRANS_MESSAGES,
rds_conn_message_info_retrans); rds_conn_message_info_retrans);
rds_info_register_func(RDS6_INFO_CONNECTIONS, rds6_conn_info);
rds_info_register_func(RDS6_INFO_SEND_MESSAGES,
rds6_conn_message_info_send);
rds_info_register_func(RDS6_INFO_RETRANS_MESSAGES,
rds6_conn_message_info_retrans);
return 0; return 0;
} }
...@@ -696,6 +830,11 @@ void rds_conn_exit(void) ...@@ -696,6 +830,11 @@ void rds_conn_exit(void)
rds_conn_message_info_send); rds_conn_message_info_send);
rds_info_deregister_func(RDS_INFO_RETRANS_MESSAGES, rds_info_deregister_func(RDS_INFO_RETRANS_MESSAGES,
rds_conn_message_info_retrans); rds_conn_message_info_retrans);
rds_info_deregister_func(RDS6_INFO_CONNECTIONS, rds6_conn_info);
rds_info_deregister_func(RDS6_INFO_SEND_MESSAGES,
rds6_conn_message_info_send);
rds_info_deregister_func(RDS6_INFO_RETRANS_MESSAGES,
rds6_conn_message_info_retrans);
} }
/* /*
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <net/addrconf.h>
#include "rds_single_path.h" #include "rds_single_path.h"
#include "rds.h" #include "rds.h"
...@@ -295,9 +296,11 @@ static int rds_ib_conn_info_visitor(struct rds_connection *conn, ...@@ -295,9 +296,11 @@ static int rds_ib_conn_info_visitor(struct rds_connection *conn,
/* We will only ever look at IB transports */ /* We will only ever look at IB transports */
if (conn->c_trans != &rds_ib_transport) if (conn->c_trans != &rds_ib_transport)
return 0; return 0;
if (conn->c_isv6)
return 0;
iinfo->src_addr = conn->c_laddr; iinfo->src_addr = conn->c_laddr.s6_addr32[3];
iinfo->dst_addr = conn->c_faddr; iinfo->dst_addr = conn->c_faddr.s6_addr32[3];
memset(&iinfo->src_gid, 0, sizeof(iinfo->src_gid)); memset(&iinfo->src_gid, 0, sizeof(iinfo->src_gid));
memset(&iinfo->dst_gid, 0, sizeof(iinfo->dst_gid)); memset(&iinfo->dst_gid, 0, sizeof(iinfo->dst_gid));
...@@ -318,6 +321,43 @@ static int rds_ib_conn_info_visitor(struct rds_connection *conn, ...@@ -318,6 +321,43 @@ static int rds_ib_conn_info_visitor(struct rds_connection *conn,
return 1; return 1;
} }
/* IPv6 version of rds_ib_conn_info_visitor(). */
static int rds6_ib_conn_info_visitor(struct rds_connection *conn,
void *buffer)
{
struct rds6_info_rdma_connection *iinfo6 = buffer;
struct rds_ib_connection *ic;
/* We will only ever look at IB transports */
if (conn->c_trans != &rds_ib_transport)
return 0;
iinfo6->src_addr = conn->c_laddr;
iinfo6->dst_addr = conn->c_faddr;
memset(&iinfo6->src_gid, 0, sizeof(iinfo6->src_gid));
memset(&iinfo6->dst_gid, 0, sizeof(iinfo6->dst_gid));
if (rds_conn_state(conn) == RDS_CONN_UP) {
struct rds_ib_device *rds_ibdev;
struct rdma_dev_addr *dev_addr;
ic = conn->c_transport_data;
dev_addr = &ic->i_cm_id->route.addr.dev_addr;
rdma_addr_get_sgid(dev_addr,
(union ib_gid *)&iinfo6->src_gid);
rdma_addr_get_dgid(dev_addr,
(union ib_gid *)&iinfo6->dst_gid);
rds_ibdev = ic->rds_ibdev;
iinfo6->max_send_wr = ic->i_send_ring.w_nr;
iinfo6->max_recv_wr = ic->i_recv_ring.w_nr;
iinfo6->max_send_sge = rds_ibdev->max_sge;
rds6_ib_get_mr_info(rds_ibdev, iinfo6);
}
return 1;
}
static void rds_ib_ic_info(struct socket *sock, unsigned int len, static void rds_ib_ic_info(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter, struct rds_info_iterator *iter,
struct rds_info_lengths *lens) struct rds_info_lengths *lens)
...@@ -330,6 +370,18 @@ static void rds_ib_ic_info(struct socket *sock, unsigned int len, ...@@ -330,6 +370,18 @@ static void rds_ib_ic_info(struct socket *sock, unsigned int len,
sizeof(struct rds_info_rdma_connection)); sizeof(struct rds_info_rdma_connection));
} }
/* IPv6 version of rds_ib_ic_info(). */
static void rds6_ib_ic_info(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter,
struct rds_info_lengths *lens)
{
u64 buffer[(sizeof(struct rds6_info_rdma_connection) + 7) / 8];
rds_for_each_conn_info(sock, len, iter, lens,
rds6_ib_conn_info_visitor,
buffer,
sizeof(struct rds6_info_rdma_connection));
}
/* /*
* Early RDS/IB was built to only bind to an address if there is an IPoIB * Early RDS/IB was built to only bind to an address if there is an IPoIB
...@@ -341,12 +393,17 @@ static void rds_ib_ic_info(struct socket *sock, unsigned int len, ...@@ -341,12 +393,17 @@ static void rds_ib_ic_info(struct socket *sock, unsigned int len,
* allowed to influence which paths have priority. We could call userspace * allowed to influence which paths have priority. We could call userspace
* asserting this policy "routing". * asserting this policy "routing".
*/ */
static int rds_ib_laddr_check(struct net *net, __be32 addr) static int rds_ib_laddr_check(struct net *net, const struct in6_addr *addr,
__u32 scope_id)
{ {
int ret; int ret;
struct rdma_cm_id *cm_id; struct rdma_cm_id *cm_id;
struct sockaddr_in6 sin6;
struct sockaddr_in sin; struct sockaddr_in sin;
struct sockaddr *sa;
bool isv4;
isv4 = ipv6_addr_v4mapped(addr);
/* Create a CMA ID and try to bind it. This catches both /* Create a CMA ID and try to bind it. This catches both
* IB and iWARP capable NICs. * IB and iWARP capable NICs.
*/ */
...@@ -355,20 +412,53 @@ static int rds_ib_laddr_check(struct net *net, __be32 addr) ...@@ -355,20 +412,53 @@ static int rds_ib_laddr_check(struct net *net, __be32 addr)
if (IS_ERR(cm_id)) if (IS_ERR(cm_id))
return PTR_ERR(cm_id); return PTR_ERR(cm_id);
if (isv4) {
memset(&sin, 0, sizeof(sin)); memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_addr.s_addr = addr; sin.sin_addr.s_addr = addr->s6_addr32[3];
sa = (struct sockaddr *)&sin;
} else {
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = *addr;
sin6.sin6_scope_id = scope_id;
sa = (struct sockaddr *)&sin6;
/* XXX Do a special IPv6 link local address check here. The
* reason is that rdma_bind_addr() always succeeds with IPv6
* link local address regardless it is indeed configured in a
* system.
*/
if (ipv6_addr_type(addr) & IPV6_ADDR_LINKLOCAL) {
struct net_device *dev;
if (scope_id == 0)
return -EADDRNOTAVAIL;
/* Use init_net for now as RDS is not network
* name space aware.
*/
dev = dev_get_by_index(&init_net, scope_id);
if (!dev)
return -EADDRNOTAVAIL;
if (!ipv6_chk_addr(&init_net, addr, dev, 1)) {
dev_put(dev);
return -EADDRNOTAVAIL;
}
dev_put(dev);
}
}
/* rdma_bind_addr will only succeed for IB & iWARP devices */ /* rdma_bind_addr will only succeed for IB & iWARP devices */
ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); ret = rdma_bind_addr(cm_id, sa);
/* due to this, we will claim to support iWARP devices unless we /* due to this, we will claim to support iWARP devices unless we
check node_type. */ check node_type. */
if (ret || !cm_id->device || if (ret || !cm_id->device ||
cm_id->device->node_type != RDMA_NODE_IB_CA) cm_id->device->node_type != RDMA_NODE_IB_CA)
ret = -EADDRNOTAVAIL; ret = -EADDRNOTAVAIL;
rdsdebug("addr %pI4 ret %d node type %d\n", rdsdebug("addr %pI6c%%%u ret %d node type %d\n",
&addr, ret, addr, scope_id, ret,
cm_id->device ? cm_id->device->node_type : -1); cm_id->device ? cm_id->device->node_type : -1);
rdma_destroy_id(cm_id); rdma_destroy_id(cm_id);
...@@ -401,6 +491,7 @@ void rds_ib_exit(void) ...@@ -401,6 +491,7 @@ void rds_ib_exit(void)
rds_ib_set_unloading(); rds_ib_set_unloading();
synchronize_rcu(); synchronize_rcu();
rds_info_deregister_func(RDS_INFO_IB_CONNECTIONS, rds_ib_ic_info); rds_info_deregister_func(RDS_INFO_IB_CONNECTIONS, rds_ib_ic_info);
rds_info_deregister_func(RDS6_INFO_IB_CONNECTIONS, rds6_ib_ic_info);
rds_ib_unregister_client(); rds_ib_unregister_client();
rds_ib_destroy_nodev_conns(); rds_ib_destroy_nodev_conns();
rds_ib_sysctl_exit(); rds_ib_sysctl_exit();
...@@ -462,6 +553,7 @@ int rds_ib_init(void) ...@@ -462,6 +553,7 @@ int rds_ib_init(void)
rds_trans_register(&rds_ib_transport); rds_trans_register(&rds_ib_transport);
rds_info_register_func(RDS_INFO_IB_CONNECTIONS, rds_ib_ic_info); rds_info_register_func(RDS_INFO_IB_CONNECTIONS, rds_ib_ic_info);
rds_info_register_func(RDS6_INFO_IB_CONNECTIONS, rds6_ib_ic_info);
goto out; goto out;
......
...@@ -57,16 +57,44 @@ struct rds_ib_refill_cache { ...@@ -57,16 +57,44 @@ struct rds_ib_refill_cache {
struct list_head *ready; struct list_head *ready;
}; };
/* This is the common structure for the IB private data exchange in setting up
* an RDS connection. The exchange is different for IPv4 and IPv6 connections.
* The reason is that the address size is different and the addresses
* exchanged are in the beginning of the structure. Hence it is not possible
* for interoperability if same structure is used.
*/
struct rds_ib_conn_priv_cmn {
u8 ricpc_protocol_major;
u8 ricpc_protocol_minor;
__be16 ricpc_protocol_minor_mask; /* bitmask */
__be32 ricpc_reserved1;
__be64 ricpc_ack_seq;
__be32 ricpc_credit; /* non-zero enables flow ctl */
};
struct rds_ib_connect_private { struct rds_ib_connect_private {
/* Add new fields at the end, and don't permute existing fields. */ /* Add new fields at the end, and don't permute existing fields. */
__be32 dp_saddr; __be32 dp_saddr;
__be32 dp_daddr; __be32 dp_daddr;
u8 dp_protocol_major; struct rds_ib_conn_priv_cmn dp_cmn;
u8 dp_protocol_minor; };
__be16 dp_protocol_minor_mask; /* bitmask */
__be32 dp_reserved1; struct rds6_ib_connect_private {
__be64 dp_ack_seq; /* Add new fields at the end, and don't permute existing fields. */
__be32 dp_credit; /* non-zero enables flow ctl */ struct in6_addr dp_saddr;
struct in6_addr dp_daddr;
struct rds_ib_conn_priv_cmn dp_cmn;
};
#define dp_protocol_major dp_cmn.ricpc_protocol_major
#define dp_protocol_minor dp_cmn.ricpc_protocol_minor
#define dp_protocol_minor_mask dp_cmn.ricpc_protocol_minor_mask
#define dp_ack_seq dp_cmn.ricpc_ack_seq
#define dp_credit dp_cmn.ricpc_credit
union rds_ib_conn_priv {
struct rds_ib_connect_private ricp_v4;
struct rds6_ib_connect_private ricp_v6;
}; };
struct rds_ib_send_work { struct rds_ib_send_work {
...@@ -351,8 +379,8 @@ void rds_ib_listen_stop(void); ...@@ -351,8 +379,8 @@ void rds_ib_listen_stop(void);
__printf(2, 3) __printf(2, 3)
void __rds_ib_conn_error(struct rds_connection *conn, const char *, ...); void __rds_ib_conn_error(struct rds_connection *conn, const char *, ...);
int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
struct rdma_cm_event *event); struct rdma_cm_event *event, bool isv6);
int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id); int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id, bool isv6);
void rds_ib_cm_connect_complete(struct rds_connection *conn, void rds_ib_cm_connect_complete(struct rds_connection *conn,
struct rdma_cm_event *event); struct rdma_cm_event *event);
...@@ -361,7 +389,8 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, ...@@ -361,7 +389,8 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn,
__rds_ib_conn_error(conn, KERN_WARNING "RDS/IB: " fmt) __rds_ib_conn_error(conn, KERN_WARNING "RDS/IB: " fmt)
/* ib_rdma.c */ /* ib_rdma.c */
int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr); int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev,
struct in6_addr *ipaddr);
void rds_ib_add_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn); void rds_ib_add_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn);
void rds_ib_remove_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn); void rds_ib_remove_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn);
void rds_ib_destroy_nodev_conns(void); void rds_ib_destroy_nodev_conns(void);
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <net/addrconf.h>
#include "rds_single_path.h" #include "rds_single_path.h"
#include "rds.h" #include "rds.h"
...@@ -95,25 +96,45 @@ rds_ib_tune_rnr(struct rds_ib_connection *ic, struct ib_qp_attr *attr) ...@@ -95,25 +96,45 @@ rds_ib_tune_rnr(struct rds_ib_connection *ic, struct ib_qp_attr *attr)
*/ */
void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_event *event) void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_event *event)
{ {
const struct rds_ib_connect_private *dp = NULL;
struct rds_ib_connection *ic = conn->c_transport_data; struct rds_ib_connection *ic = conn->c_transport_data;
const union rds_ib_conn_priv *dp = NULL;
struct ib_qp_attr qp_attr; struct ib_qp_attr qp_attr;
__be64 ack_seq = 0;
__be32 credit = 0;
u8 major = 0;
u8 minor = 0;
int err; int err;
if (event->param.conn.private_data_len >= sizeof(*dp)) {
dp = event->param.conn.private_data; dp = event->param.conn.private_data;
if (conn->c_isv6) {
if (event->param.conn.private_data_len >=
sizeof(struct rds6_ib_connect_private)) {
major = dp->ricp_v6.dp_protocol_major;
minor = dp->ricp_v6.dp_protocol_minor;
credit = dp->ricp_v6.dp_credit;
/* dp structure start is not guaranteed to be 8 bytes
* aligned. Since dp_ack_seq is 64-bit extended load
* operations can be used so go through get_unaligned
* to avoid unaligned errors.
*/
ack_seq = get_unaligned(&dp->ricp_v6.dp_ack_seq);
}
} else if (event->param.conn.private_data_len >=
sizeof(struct rds_ib_connect_private)) {
major = dp->ricp_v4.dp_protocol_major;
minor = dp->ricp_v4.dp_protocol_minor;
credit = dp->ricp_v4.dp_credit;
ack_seq = get_unaligned(&dp->ricp_v4.dp_ack_seq);
}
/* make sure it isn't empty data */ /* make sure it isn't empty data */
if (dp->dp_protocol_major) { if (major) {
rds_ib_set_protocol(conn, rds_ib_set_protocol(conn, RDS_PROTOCOL(major, minor));
RDS_PROTOCOL(dp->dp_protocol_major, rds_ib_set_flow_control(conn, be32_to_cpu(credit));
dp->dp_protocol_minor));
rds_ib_set_flow_control(conn, be32_to_cpu(dp->dp_credit));
}
} }
if (conn->c_version < RDS_PROTOCOL(3, 1)) { if (conn->c_version < RDS_PROTOCOL(3, 1)) {
pr_notice("RDS/IB: Connection <%pI4,%pI4> version %u.%u no longer supported\n", pr_notice("RDS/IB: Connection <%pI6c,%pI6c> version %u.%u no longer supported\n",
&conn->c_laddr, &conn->c_faddr, &conn->c_laddr, &conn->c_faddr,
RDS_PROTOCOL_MAJOR(conn->c_version), RDS_PROTOCOL_MAJOR(conn->c_version),
RDS_PROTOCOL_MINOR(conn->c_version)); RDS_PROTOCOL_MINOR(conn->c_version));
...@@ -121,7 +142,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even ...@@ -121,7 +142,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
rds_conn_destroy(conn); rds_conn_destroy(conn);
return; return;
} else { } else {
pr_notice("RDS/IB: %s conn connected <%pI4,%pI4> version %u.%u%s\n", pr_notice("RDS/IB: %s conn connected <%pI6c,%pI6c> version %u.%u%s\n",
ic->i_active_side ? "Active" : "Passive", ic->i_active_side ? "Active" : "Passive",
&conn->c_laddr, &conn->c_faddr, &conn->c_laddr, &conn->c_faddr,
RDS_PROTOCOL_MAJOR(conn->c_version), RDS_PROTOCOL_MAJOR(conn->c_version),
...@@ -150,7 +171,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even ...@@ -150,7 +171,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
printk(KERN_NOTICE "ib_modify_qp(IB_QP_STATE, RTS): err=%d\n", err); printk(KERN_NOTICE "ib_modify_qp(IB_QP_STATE, RTS): err=%d\n", err);
/* update ib_device with this local ipaddr */ /* update ib_device with this local ipaddr */
err = rds_ib_update_ipaddr(ic->rds_ibdev, conn->c_laddr); err = rds_ib_update_ipaddr(ic->rds_ibdev, &conn->c_laddr);
if (err) if (err)
printk(KERN_ERR "rds_ib_update_ipaddr failed (%d)\n", printk(KERN_ERR "rds_ib_update_ipaddr failed (%d)\n",
err); err);
...@@ -158,14 +179,8 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even ...@@ -158,14 +179,8 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
/* If the peer gave us the last packet it saw, process this as if /* If the peer gave us the last packet it saw, process this as if
* we had received a regular ACK. */ * we had received a regular ACK. */
if (dp) { if (dp) {
/* dp structure start is not guaranteed to be 8 bytes aligned. if (ack_seq)
* Since dp_ack_seq is 64-bit extended load operations can be rds_send_drop_acked(conn, be64_to_cpu(ack_seq),
* used so go through get_unaligned to avoid unaligned errors.
*/
__be64 dp_ack_seq = get_unaligned(&dp->dp_ack_seq);
if (dp_ack_seq)
rds_send_drop_acked(conn, be64_to_cpu(dp_ack_seq),
NULL); NULL);
} }
...@@ -174,10 +189,11 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even ...@@ -174,10 +189,11 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
static void rds_ib_cm_fill_conn_param(struct rds_connection *conn, static void rds_ib_cm_fill_conn_param(struct rds_connection *conn,
struct rdma_conn_param *conn_param, struct rdma_conn_param *conn_param,
struct rds_ib_connect_private *dp, union rds_ib_conn_priv *dp,
u32 protocol_version, u32 protocol_version,
u32 max_responder_resources, u32 max_responder_resources,
u32 max_initiator_depth) u32 max_initiator_depth,
bool isv6)
{ {
struct rds_ib_connection *ic = conn->c_transport_data; struct rds_ib_connection *ic = conn->c_transport_data;
struct rds_ib_device *rds_ibdev = ic->rds_ibdev; struct rds_ib_device *rds_ibdev = ic->rds_ibdev;
...@@ -193,24 +209,49 @@ static void rds_ib_cm_fill_conn_param(struct rds_connection *conn, ...@@ -193,24 +209,49 @@ static void rds_ib_cm_fill_conn_param(struct rds_connection *conn,
if (dp) { if (dp) {
memset(dp, 0, sizeof(*dp)); memset(dp, 0, sizeof(*dp));
dp->dp_saddr = conn->c_laddr; if (isv6) {
dp->dp_daddr = conn->c_faddr; dp->ricp_v6.dp_saddr = conn->c_laddr;
dp->dp_protocol_major = RDS_PROTOCOL_MAJOR(protocol_version); dp->ricp_v6.dp_daddr = conn->c_faddr;
dp->dp_protocol_minor = RDS_PROTOCOL_MINOR(protocol_version); dp->ricp_v6.dp_protocol_major =
dp->dp_protocol_minor_mask = cpu_to_be16(RDS_IB_SUPPORTED_PROTOCOLS); RDS_PROTOCOL_MAJOR(protocol_version);
dp->dp_ack_seq = cpu_to_be64(rds_ib_piggyb_ack(ic)); dp->ricp_v6.dp_protocol_minor =
RDS_PROTOCOL_MINOR(protocol_version);
dp->ricp_v6.dp_protocol_minor_mask =
cpu_to_be16(RDS_IB_SUPPORTED_PROTOCOLS);
dp->ricp_v6.dp_ack_seq =
cpu_to_be64(rds_ib_piggyb_ack(ic));
conn_param->private_data = &dp->ricp_v6;
conn_param->private_data_len = sizeof(dp->ricp_v6);
} else {
dp->ricp_v4.dp_saddr = conn->c_laddr.s6_addr32[3];
dp->ricp_v4.dp_daddr = conn->c_faddr.s6_addr32[3];
dp->ricp_v4.dp_protocol_major =
RDS_PROTOCOL_MAJOR(protocol_version);
dp->ricp_v4.dp_protocol_minor =
RDS_PROTOCOL_MINOR(protocol_version);
dp->ricp_v4.dp_protocol_minor_mask =
cpu_to_be16(RDS_IB_SUPPORTED_PROTOCOLS);
dp->ricp_v4.dp_ack_seq =
cpu_to_be64(rds_ib_piggyb_ack(ic));
conn_param->private_data = &dp->ricp_v4;
conn_param->private_data_len = sizeof(dp->ricp_v4);
}
/* Advertise flow control */ /* Advertise flow control */
if (ic->i_flowctl) { if (ic->i_flowctl) {
unsigned int credits; unsigned int credits;
credits = IB_GET_POST_CREDITS(atomic_read(&ic->i_credits)); credits = IB_GET_POST_CREDITS
dp->dp_credit = cpu_to_be32(credits); (atomic_read(&ic->i_credits));
atomic_sub(IB_SET_POST_CREDITS(credits), &ic->i_credits); if (isv6)
dp->ricp_v6.dp_credit = cpu_to_be32(credits);
else
dp->ricp_v4.dp_credit = cpu_to_be32(credits);
atomic_sub(IB_SET_POST_CREDITS(credits),
&ic->i_credits);
} }
conn_param->private_data = dp;
conn_param->private_data_len = sizeof(*dp);
} }
} }
...@@ -349,7 +390,7 @@ static void rds_ib_qp_event_handler(struct ib_event *event, void *data) ...@@ -349,7 +390,7 @@ static void rds_ib_qp_event_handler(struct ib_event *event, void *data)
break; break;
default: default:
rdsdebug("Fatal QP Event %u (%s) " rdsdebug("Fatal QP Event %u (%s) "
"- connection %pI4->%pI4, reconnecting\n", "- connection %pI6c->%pI6c, reconnecting\n",
event->event, ib_event_msg(event->event), event->event, ib_event_msg(event->event),
&conn->c_laddr, &conn->c_faddr); &conn->c_laddr, &conn->c_faddr);
rds_conn_drop(conn); rds_conn_drop(conn);
...@@ -580,11 +621,13 @@ static int rds_ib_setup_qp(struct rds_connection *conn) ...@@ -580,11 +621,13 @@ static int rds_ib_setup_qp(struct rds_connection *conn)
return ret; return ret;
} }
static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event) static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event, bool isv6)
{ {
const struct rds_ib_connect_private *dp = event->param.conn.private_data; const union rds_ib_conn_priv *dp = event->param.conn.private_data;
u16 common; u8 data_len, major, minor;
u32 version = 0; u32 version = 0;
__be16 mask;
u16 common;
/* /*
* rdma_cm private data is odd - when there is any private data in the * rdma_cm private data is odd - when there is any private data in the
...@@ -603,51 +646,133 @@ static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event) ...@@ -603,51 +646,133 @@ static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event)
return 0; return 0;
} }
if (isv6) {
data_len = sizeof(struct rds6_ib_connect_private);
major = dp->ricp_v6.dp_protocol_major;
minor = dp->ricp_v6.dp_protocol_minor;
mask = dp->ricp_v6.dp_protocol_minor_mask;
} else {
data_len = sizeof(struct rds_ib_connect_private);
major = dp->ricp_v4.dp_protocol_major;
minor = dp->ricp_v4.dp_protocol_minor;
mask = dp->ricp_v4.dp_protocol_minor_mask;
}
/* Even if len is crap *now* I still want to check it. -ASG */ /* Even if len is crap *now* I still want to check it. -ASG */
if (event->param.conn.private_data_len < sizeof (*dp) || if (event->param.conn.private_data_len < data_len || major == 0)
dp->dp_protocol_major == 0)
return RDS_PROTOCOL_3_0; return RDS_PROTOCOL_3_0;
common = be16_to_cpu(dp->dp_protocol_minor_mask) & RDS_IB_SUPPORTED_PROTOCOLS; common = be16_to_cpu(mask) & RDS_IB_SUPPORTED_PROTOCOLS;
if (dp->dp_protocol_major == 3 && common) { if (major == 3 && common) {
version = RDS_PROTOCOL_3_0; version = RDS_PROTOCOL_3_0;
while ((common >>= 1) != 0) while ((common >>= 1) != 0)
version++; version++;
} else } else {
if (isv6)
printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI6c using incompatible protocol version %u.%u\n",
&dp->ricp_v6.dp_saddr, major, minor);
else
printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI4 using incompatible protocol version %u.%u\n", printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI4 using incompatible protocol version %u.%u\n",
&dp->dp_saddr, &dp->ricp_v4.dp_saddr, major, minor);
dp->dp_protocol_major, }
dp->dp_protocol_minor);
return version; return version;
} }
/* Given an IPv6 address, find the net_device which hosts that address and
* return its index. This is used by the rds_ib_cm_handle_connect() code to
* find the interface index of where an incoming request comes from when
* the request is using a link local address.
*
* Note one problem in this search. It is possible that two interfaces have
* the same link local address. Unfortunately, this cannot be solved unless
* the underlying layer gives us the interface which an incoming RDMA connect
* request comes from.
*/
static u32 __rds_find_ifindex(struct net *net, const struct in6_addr *addr)
{
struct net_device *dev;
int idx = 0;
rcu_read_lock();
for_each_netdev_rcu(net, dev) {
if (ipv6_chk_addr(net, addr, dev, 1)) {
idx = dev->ifindex;
break;
}
}
rcu_read_unlock();
return idx;
}
int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
struct rdma_cm_event *event) struct rdma_cm_event *event, bool isv6)
{ {
__be64 lguid = cm_id->route.path_rec->sgid.global.interface_id; __be64 lguid = cm_id->route.path_rec->sgid.global.interface_id;
__be64 fguid = cm_id->route.path_rec->dgid.global.interface_id; __be64 fguid = cm_id->route.path_rec->dgid.global.interface_id;
const struct rds_ib_connect_private *dp = event->param.conn.private_data; const struct rds_ib_conn_priv_cmn *dp_cmn;
struct rds_ib_connect_private dp_rep;
struct rds_connection *conn = NULL; struct rds_connection *conn = NULL;
struct rds_ib_connection *ic = NULL; struct rds_ib_connection *ic = NULL;
struct rdma_conn_param conn_param; struct rdma_conn_param conn_param;
const union rds_ib_conn_priv *dp;
union rds_ib_conn_priv dp_rep;
struct in6_addr s_mapped_addr;
struct in6_addr d_mapped_addr;
const struct in6_addr *saddr6;
const struct in6_addr *daddr6;
int destroy = 1;
u32 ifindex = 0;
u32 version; u32 version;
int err = 1, destroy = 1; int err = 1;
/* Check whether the remote protocol version matches ours. */ /* Check whether the remote protocol version matches ours. */
version = rds_ib_protocol_compatible(event); version = rds_ib_protocol_compatible(event, isv6);
if (!version) if (!version)
goto out; goto out;
rdsdebug("saddr %pI4 daddr %pI4 RDSv%u.%u lguid 0x%llx fguid " dp = event->param.conn.private_data;
"0x%llx\n", &dp->dp_saddr, &dp->dp_daddr, if (isv6) {
dp_cmn = &dp->ricp_v6.dp_cmn;
saddr6 = &dp->ricp_v6.dp_saddr;
daddr6 = &dp->ricp_v6.dp_daddr;
/* If either address is link local, need to find the
* interface index in order to create a proper RDS
* connection.
*/
if (ipv6_addr_type(daddr6) & IPV6_ADDR_LINKLOCAL) {
/* Using init_net for now .. */
ifindex = __rds_find_ifindex(&init_net, daddr6);
/* No index found... Need to bail out. */
if (ifindex == 0) {
err = -EOPNOTSUPP;
goto out;
}
} else if (ipv6_addr_type(saddr6) & IPV6_ADDR_LINKLOCAL) {
/* Use our address to find the correct index. */
ifindex = __rds_find_ifindex(&init_net, daddr6);
/* No index found... Need to bail out. */
if (ifindex == 0) {
err = -EOPNOTSUPP;
goto out;
}
}
} else {
dp_cmn = &dp->ricp_v4.dp_cmn;
ipv6_addr_set_v4mapped(dp->ricp_v4.dp_saddr, &s_mapped_addr);
ipv6_addr_set_v4mapped(dp->ricp_v4.dp_daddr, &d_mapped_addr);
saddr6 = &s_mapped_addr;
daddr6 = &d_mapped_addr;
}
rdsdebug("saddr %pI6c daddr %pI6c RDSv%u.%u lguid 0x%llx fguid "
"0x%llx\n", saddr6, daddr6,
RDS_PROTOCOL_MAJOR(version), RDS_PROTOCOL_MINOR(version), RDS_PROTOCOL_MAJOR(version), RDS_PROTOCOL_MINOR(version),
(unsigned long long)be64_to_cpu(lguid), (unsigned long long)be64_to_cpu(lguid),
(unsigned long long)be64_to_cpu(fguid)); (unsigned long long)be64_to_cpu(fguid));
/* RDS/IB is not currently netns aware, thus init_net */ /* RDS/IB is not currently netns aware, thus init_net */
conn = rds_conn_create(&init_net, dp->dp_daddr, dp->dp_saddr, conn = rds_conn_create(&init_net, daddr6, saddr6,
&rds_ib_transport, GFP_KERNEL); &rds_ib_transport, GFP_KERNEL, ifindex);
if (IS_ERR(conn)) { if (IS_ERR(conn)) {
rdsdebug("rds_conn_create failed (%ld)\n", PTR_ERR(conn)); rdsdebug("rds_conn_create failed (%ld)\n", PTR_ERR(conn));
conn = NULL; conn = NULL;
...@@ -678,12 +803,13 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, ...@@ -678,12 +803,13 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
ic = conn->c_transport_data; ic = conn->c_transport_data;
rds_ib_set_protocol(conn, version); rds_ib_set_protocol(conn, version);
rds_ib_set_flow_control(conn, be32_to_cpu(dp->dp_credit)); rds_ib_set_flow_control(conn, be32_to_cpu(dp_cmn->ricpc_credit));
/* If the peer gave us the last packet it saw, process this as if /* If the peer gave us the last packet it saw, process this as if
* we had received a regular ACK. */ * we had received a regular ACK. */
if (dp->dp_ack_seq) if (dp_cmn->ricpc_ack_seq)
rds_send_drop_acked(conn, be64_to_cpu(dp->dp_ack_seq), NULL); rds_send_drop_acked(conn, be64_to_cpu(dp_cmn->ricpc_ack_seq),
NULL);
BUG_ON(cm_id->context); BUG_ON(cm_id->context);
BUG_ON(ic->i_cm_id); BUG_ON(ic->i_cm_id);
...@@ -703,7 +829,7 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, ...@@ -703,7 +829,7 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
rds_ib_cm_fill_conn_param(conn, &conn_param, &dp_rep, version, rds_ib_cm_fill_conn_param(conn, &conn_param, &dp_rep, version,
event->param.conn.responder_resources, event->param.conn.responder_resources,
event->param.conn.initiator_depth); event->param.conn.initiator_depth, isv6);
/* rdma_accept() calls rdma_reject() internally if it fails */ /* rdma_accept() calls rdma_reject() internally if it fails */
if (rdma_accept(cm_id, &conn_param)) if (rdma_accept(cm_id, &conn_param))
...@@ -718,12 +844,12 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, ...@@ -718,12 +844,12 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
} }
int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id) int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id, bool isv6)
{ {
struct rds_connection *conn = cm_id->context; struct rds_connection *conn = cm_id->context;
struct rds_ib_connection *ic = conn->c_transport_data; struct rds_ib_connection *ic = conn->c_transport_data;
struct rdma_conn_param conn_param; struct rdma_conn_param conn_param;
struct rds_ib_connect_private dp; union rds_ib_conn_priv dp;
int ret; int ret;
/* If the peer doesn't do protocol negotiation, we must /* If the peer doesn't do protocol negotiation, we must
...@@ -738,7 +864,7 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id) ...@@ -738,7 +864,7 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id)
} }
rds_ib_cm_fill_conn_param(conn, &conn_param, &dp, RDS_PROTOCOL_VERSION, rds_ib_cm_fill_conn_param(conn, &conn_param, &dp, RDS_PROTOCOL_VERSION,
UINT_MAX, UINT_MAX); UINT_MAX, UINT_MAX, isv6);
ret = rdma_connect(cm_id, &conn_param); ret = rdma_connect(cm_id, &conn_param);
if (ret) if (ret)
rds_ib_conn_error(conn, "rdma_connect failed (%d)\n", ret); rds_ib_conn_error(conn, "rdma_connect failed (%d)\n", ret);
...@@ -758,13 +884,20 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id) ...@@ -758,13 +884,20 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id)
int rds_ib_conn_path_connect(struct rds_conn_path *cp) int rds_ib_conn_path_connect(struct rds_conn_path *cp)
{ {
struct rds_connection *conn = cp->cp_conn; struct rds_connection *conn = cp->cp_conn;
struct rds_ib_connection *ic = conn->c_transport_data; struct sockaddr_storage src, dest;
struct sockaddr_in src, dest; rdma_cm_event_handler handler;
struct rds_ib_connection *ic;
int ret; int ret;
ic = conn->c_transport_data;
/* XXX I wonder what affect the port space has */ /* XXX I wonder what affect the port space has */
/* delegate cm event handler to rdma_transport */ /* delegate cm event handler to rdma_transport */
ic->i_cm_id = rdma_create_id(&init_net, rds_rdma_cm_event_handler, conn, if (conn->c_isv6)
handler = rds6_rdma_cm_event_handler;
else
handler = rds_rdma_cm_event_handler;
ic->i_cm_id = rdma_create_id(&init_net, handler, conn,
RDMA_PS_TCP, IB_QPT_RC); RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(ic->i_cm_id)) { if (IS_ERR(ic->i_cm_id)) {
ret = PTR_ERR(ic->i_cm_id); ret = PTR_ERR(ic->i_cm_id);
...@@ -775,13 +908,33 @@ int rds_ib_conn_path_connect(struct rds_conn_path *cp) ...@@ -775,13 +908,33 @@ int rds_ib_conn_path_connect(struct rds_conn_path *cp)
rdsdebug("created cm id %p for conn %p\n", ic->i_cm_id, conn); rdsdebug("created cm id %p for conn %p\n", ic->i_cm_id, conn);
src.sin_family = AF_INET; if (ipv6_addr_v4mapped(&conn->c_faddr)) {
src.sin_addr.s_addr = (__force u32)conn->c_laddr; struct sockaddr_in *sin;
src.sin_port = (__force u16)htons(0);
dest.sin_family = AF_INET; sin = (struct sockaddr_in *)&src;
dest.sin_addr.s_addr = (__force u32)conn->c_faddr; sin->sin_family = AF_INET;
dest.sin_port = (__force u16)htons(RDS_PORT); sin->sin_addr.s_addr = conn->c_laddr.s6_addr32[3];
sin->sin_port = 0;
sin = (struct sockaddr_in *)&dest;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = conn->c_faddr.s6_addr32[3];
sin->sin_port = htons(RDS_PORT);
} else {
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *)&src;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = conn->c_laddr;
sin6->sin6_port = 0;
sin6->sin6_scope_id = conn->c_dev_if;
sin6 = (struct sockaddr_in6 *)&dest;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = conn->c_faddr;
sin6->sin6_port = htons(RDS_CM_PORT);
sin6->sin6_scope_id = conn->c_dev_if;
}
ret = rdma_resolve_addr(ic->i_cm_id, (struct sockaddr *)&src, ret = rdma_resolve_addr(ic->i_cm_id, (struct sockaddr *)&src,
(struct sockaddr *)&dest, (struct sockaddr *)&dest,
......
...@@ -113,6 +113,8 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_dev, ...@@ -113,6 +113,8 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_dev,
int npages); int npages);
void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev, void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev,
struct rds_info_rdma_connection *iinfo); struct rds_info_rdma_connection *iinfo);
void rds6_ib_get_mr_info(struct rds_ib_device *rds_ibdev,
struct rds6_info_rdma_connection *iinfo6);
void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *); void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *);
void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents, void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
struct rds_sock *rs, u32 *key_ret); struct rds_sock *rs, u32 *key_ret);
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -100,18 +100,19 @@ static void rds_ib_remove_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr) ...@@ -100,18 +100,19 @@ static void rds_ib_remove_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr)
kfree_rcu(to_free, rcu); kfree_rcu(to_free, rcu);
} }
int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr) int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev,
struct in6_addr *ipaddr)
{ {
struct rds_ib_device *rds_ibdev_old; struct rds_ib_device *rds_ibdev_old;
rds_ibdev_old = rds_ib_get_device(ipaddr); rds_ibdev_old = rds_ib_get_device(ipaddr->s6_addr32[3]);
if (!rds_ibdev_old) if (!rds_ibdev_old)
return rds_ib_add_ipaddr(rds_ibdev, ipaddr); return rds_ib_add_ipaddr(rds_ibdev, ipaddr->s6_addr32[3]);
if (rds_ibdev_old != rds_ibdev) { if (rds_ibdev_old != rds_ibdev) {
rds_ib_remove_ipaddr(rds_ibdev_old, ipaddr); rds_ib_remove_ipaddr(rds_ibdev_old, ipaddr->s6_addr32[3]);
rds_ib_dev_put(rds_ibdev_old); rds_ib_dev_put(rds_ibdev_old);
return rds_ib_add_ipaddr(rds_ibdev, ipaddr); return rds_ib_add_ipaddr(rds_ibdev, ipaddr->s6_addr32[3]);
} }
rds_ib_dev_put(rds_ibdev_old); rds_ib_dev_put(rds_ibdev_old);
...@@ -179,6 +180,15 @@ void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev, struct rds_info_rdma_co ...@@ -179,6 +180,15 @@ void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev, struct rds_info_rdma_co
iinfo->rdma_mr_size = pool_1m->fmr_attr.max_pages; iinfo->rdma_mr_size = pool_1m->fmr_attr.max_pages;
} }
void rds6_ib_get_mr_info(struct rds_ib_device *rds_ibdev,
struct rds6_info_rdma_connection *iinfo6)
{
struct rds_ib_mr_pool *pool_1m = rds_ibdev->mr_1m_pool;
iinfo6->rdma_mr_max = pool_1m->max_items;
iinfo6->rdma_mr_size = pool_1m->fmr_attr.max_pages;
}
struct rds_ib_mr *rds_ib_reuse_mr(struct rds_ib_mr_pool *pool) struct rds_ib_mr *rds_ib_reuse_mr(struct rds_ib_mr_pool *pool)
{ {
struct rds_ib_mr *ibmr = NULL; struct rds_ib_mr *ibmr = NULL;
...@@ -544,7 +554,7 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents, ...@@ -544,7 +554,7 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
struct rds_ib_connection *ic = rs->rs_conn->c_transport_data; struct rds_ib_connection *ic = rs->rs_conn->c_transport_data;
int ret; int ret;
rds_ibdev = rds_ib_get_device(rs->rs_bound_addr); rds_ibdev = rds_ib_get_device(rs->rs_bound_addr.s6_addr32[3]);
if (!rds_ibdev) { if (!rds_ibdev) {
ret = -ENODEV; ret = -ENODEV;
goto out; goto out;
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -266,7 +266,7 @@ static struct rds_ib_incoming *rds_ib_refill_one_inc(struct rds_ib_connection *i ...@@ -266,7 +266,7 @@ static struct rds_ib_incoming *rds_ib_refill_one_inc(struct rds_ib_connection *i
rds_ib_stats_inc(s_ib_rx_total_incs); rds_ib_stats_inc(s_ib_rx_total_incs);
} }
INIT_LIST_HEAD(&ibinc->ii_frags); INIT_LIST_HEAD(&ibinc->ii_frags);
rds_inc_init(&ibinc->ii_inc, ic->conn, ic->conn->c_faddr); rds_inc_init(&ibinc->ii_inc, ic->conn, &ic->conn->c_faddr);
return ibinc; return ibinc;
} }
...@@ -418,7 +418,7 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp) ...@@ -418,7 +418,7 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp)
ret = ib_post_recv(ic->i_cm_id->qp, &recv->r_wr, &failed_wr); ret = ib_post_recv(ic->i_cm_id->qp, &recv->r_wr, &failed_wr);
if (ret) { if (ret) {
rds_ib_conn_error(conn, "recv post on " rds_ib_conn_error(conn, "recv post on "
"%pI4 returned %d, disconnecting and " "%pI6c returned %d, disconnecting and "
"reconnecting\n", &conn->c_faddr, "reconnecting\n", &conn->c_faddr,
ret); ret);
break; break;
...@@ -848,7 +848,7 @@ static void rds_ib_process_recv(struct rds_connection *conn, ...@@ -848,7 +848,7 @@ static void rds_ib_process_recv(struct rds_connection *conn,
if (data_len < sizeof(struct rds_header)) { if (data_len < sizeof(struct rds_header)) {
rds_ib_conn_error(conn, "incoming message " rds_ib_conn_error(conn, "incoming message "
"from %pI4 didn't include a " "from %pI6c didn't include a "
"header, disconnecting and " "header, disconnecting and "
"reconnecting\n", "reconnecting\n",
&conn->c_faddr); &conn->c_faddr);
...@@ -861,7 +861,7 @@ static void rds_ib_process_recv(struct rds_connection *conn, ...@@ -861,7 +861,7 @@ static void rds_ib_process_recv(struct rds_connection *conn,
/* Validate the checksum. */ /* Validate the checksum. */
if (!rds_message_verify_checksum(ihdr)) { if (!rds_message_verify_checksum(ihdr)) {
rds_ib_conn_error(conn, "incoming message " rds_ib_conn_error(conn, "incoming message "
"from %pI4 has corrupted header - " "from %pI6c has corrupted header - "
"forcing a reconnect\n", "forcing a reconnect\n",
&conn->c_faddr); &conn->c_faddr);
rds_stats_inc(s_recv_drop_bad_checksum); rds_stats_inc(s_recv_drop_bad_checksum);
...@@ -941,10 +941,10 @@ static void rds_ib_process_recv(struct rds_connection *conn, ...@@ -941,10 +941,10 @@ static void rds_ib_process_recv(struct rds_connection *conn,
ic->i_recv_data_rem = 0; ic->i_recv_data_rem = 0;
ic->i_ibinc = NULL; ic->i_ibinc = NULL;
if (ibinc->ii_inc.i_hdr.h_flags == RDS_FLAG_CONG_BITMAP) if (ibinc->ii_inc.i_hdr.h_flags == RDS_FLAG_CONG_BITMAP) {
rds_ib_cong_recv(conn, ibinc); rds_ib_cong_recv(conn, ibinc);
else { } else {
rds_recv_incoming(conn, conn->c_faddr, conn->c_laddr, rds_recv_incoming(conn, &conn->c_faddr, &conn->c_laddr,
&ibinc->ii_inc, GFP_ATOMIC); &ibinc->ii_inc, GFP_ATOMIC);
state->ack_next = be64_to_cpu(hdr->h_sequence); state->ack_next = be64_to_cpu(hdr->h_sequence);
state->ack_next_valid = 1; state->ack_next_valid = 1;
...@@ -988,7 +988,7 @@ void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic, ...@@ -988,7 +988,7 @@ void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic,
} else { } else {
/* We expect errors as the qp is drained during shutdown */ /* We expect errors as the qp is drained during shutdown */
if (rds_conn_up(conn) || rds_conn_connecting(conn)) if (rds_conn_up(conn) || rds_conn_connecting(conn))
rds_ib_conn_error(conn, "recv completion on <%pI4,%pI4> had status %u (%s), disconnecting and reconnecting\n", rds_ib_conn_error(conn, "recv completion on <%pI6c,%pI6c> had status %u (%s), disconnecting and reconnecting\n",
&conn->c_laddr, &conn->c_faddr, &conn->c_laddr, &conn->c_faddr,
wc->status, wc->status,
ib_wc_status_msg(wc->status)); ib_wc_status_msg(wc->status));
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -305,7 +305,7 @@ void rds_ib_send_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc) ...@@ -305,7 +305,7 @@ void rds_ib_send_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc)
/* We expect errors as the qp is drained during shutdown */ /* We expect errors as the qp is drained during shutdown */
if (wc->status != IB_WC_SUCCESS && rds_conn_up(conn)) { if (wc->status != IB_WC_SUCCESS && rds_conn_up(conn)) {
rds_ib_conn_error(conn, "send completion on <%pI4,%pI4> had status %u (%s), disconnecting and reconnecting\n", rds_ib_conn_error(conn, "send completion on <%pI6c,%pI6c> had status %u (%s), disconnecting and reconnecting\n",
&conn->c_laddr, &conn->c_faddr, wc->status, &conn->c_laddr, &conn->c_faddr, wc->status,
ib_wc_status_msg(wc->status)); ib_wc_status_msg(wc->status));
} }
...@@ -730,7 +730,7 @@ int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm, ...@@ -730,7 +730,7 @@ int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm,
first, &first->s_wr, ret, failed_wr); first, &first->s_wr, ret, failed_wr);
BUG_ON(failed_wr != &first->s_wr); BUG_ON(failed_wr != &first->s_wr);
if (ret) { if (ret) {
printk(KERN_WARNING "RDS/IB: ib_post_send to %pI4 " printk(KERN_WARNING "RDS/IB: ib_post_send to %pI6c "
"returned %d\n", &conn->c_faddr, ret); "returned %d\n", &conn->c_faddr, ret);
rds_ib_ring_unalloc(&ic->i_send_ring, work_alloc); rds_ib_ring_unalloc(&ic->i_send_ring, work_alloc);
rds_ib_sub_signaled(ic, nr_sig); rds_ib_sub_signaled(ic, nr_sig);
...@@ -827,7 +827,7 @@ int rds_ib_xmit_atomic(struct rds_connection *conn, struct rm_atomic_op *op) ...@@ -827,7 +827,7 @@ int rds_ib_xmit_atomic(struct rds_connection *conn, struct rm_atomic_op *op)
send, &send->s_atomic_wr, ret, failed_wr); send, &send->s_atomic_wr, ret, failed_wr);
BUG_ON(failed_wr != &send->s_atomic_wr.wr); BUG_ON(failed_wr != &send->s_atomic_wr.wr);
if (ret) { if (ret) {
printk(KERN_WARNING "RDS/IB: atomic ib_post_send to %pI4 " printk(KERN_WARNING "RDS/IB: atomic ib_post_send to %pI6c "
"returned %d\n", &conn->c_faddr, ret); "returned %d\n", &conn->c_faddr, ret);
rds_ib_ring_unalloc(&ic->i_send_ring, work_alloc); rds_ib_ring_unalloc(&ic->i_send_ring, work_alloc);
rds_ib_sub_signaled(ic, nr_sig); rds_ib_sub_signaled(ic, nr_sig);
...@@ -967,7 +967,7 @@ int rds_ib_xmit_rdma(struct rds_connection *conn, struct rm_rdma_op *op) ...@@ -967,7 +967,7 @@ int rds_ib_xmit_rdma(struct rds_connection *conn, struct rm_rdma_op *op)
first, &first->s_rdma_wr.wr, ret, failed_wr); first, &first->s_rdma_wr.wr, ret, failed_wr);
BUG_ON(failed_wr != &first->s_rdma_wr.wr); BUG_ON(failed_wr != &first->s_rdma_wr.wr);
if (ret) { if (ret) {
printk(KERN_WARNING "RDS/IB: rdma ib_post_send to %pI4 " printk(KERN_WARNING "RDS/IB: rdma ib_post_send to %pI6c "
"returned %d\n", &conn->c_faddr, ret); "returned %d\n", &conn->c_faddr, ret);
rds_ib_ring_unalloc(&ic->i_send_ring, work_alloc); rds_ib_ring_unalloc(&ic->i_send_ring, work_alloc);
rds_ib_sub_signaled(ic, nr_sig); rds_ib_sub_signaled(ic, nr_sig);
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/in.h> #include <linux/in.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>
#include <linux/ipv6.h>
#include "rds_single_path.h" #include "rds_single_path.h"
#include "rds.h" #include "rds.h"
...@@ -88,11 +89,11 @@ static int rds_loop_xmit(struct rds_connection *conn, struct rds_message *rm, ...@@ -88,11 +89,11 @@ static int rds_loop_xmit(struct rds_connection *conn, struct rds_message *rm,
BUG_ON(hdr_off || sg || off); BUG_ON(hdr_off || sg || off);
rds_inc_init(&rm->m_inc, conn, conn->c_laddr); rds_inc_init(&rm->m_inc, conn, &conn->c_laddr);
/* For the embedded inc. Matching put is in loop_inc_free() */ /* For the embedded inc. Matching put is in loop_inc_free() */
rds_message_addref(rm); rds_message_addref(rm);
rds_recv_incoming(conn, conn->c_laddr, conn->c_faddr, &rm->m_inc, rds_recv_incoming(conn, &conn->c_laddr, &conn->c_faddr, &rm->m_inc,
GFP_KERNEL); GFP_KERNEL);
rds_send_drop_acked(conn, be64_to_cpu(rm->m_inc.i_hdr.h_sequence), rds_send_drop_acked(conn, be64_to_cpu(rm->m_inc.i_hdr.h_sequence),
......
/* /*
* Copyright (c) 2007 Oracle. All rights reserved. * Copyright (c) 2007, 2017 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -183,7 +183,7 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, ...@@ -183,7 +183,7 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args,
long i; long i;
int ret; int ret;
if (rs->rs_bound_addr == 0 || !rs->rs_transport) { if (ipv6_addr_any(&rs->rs_bound_addr) || !rs->rs_transport) {
ret = -ENOTCONN; /* XXX not a great errno */ ret = -ENOTCONN; /* XXX not a great errno */
goto out; goto out;
} }
...@@ -574,7 +574,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, ...@@ -574,7 +574,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
args = CMSG_DATA(cmsg); args = CMSG_DATA(cmsg);
if (rs->rs_bound_addr == 0) { if (ipv6_addr_any(&rs->rs_bound_addr)) {
ret = -ENOTCONN; /* XXX not a great errno */ ret = -ENOTCONN; /* XXX not a great errno */
goto out_ret; goto out_ret;
} }
......
/* /*
* Copyright (c) 2009 Oracle. All rights reserved. * Copyright (c) 2009, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -37,10 +37,13 @@ ...@@ -37,10 +37,13 @@
#include "rdma_transport.h" #include "rdma_transport.h"
#include "ib.h" #include "ib.h"
/* Global IPv4 and IPv6 RDS RDMA listener cm_id */
static struct rdma_cm_id *rds_rdma_listen_id; static struct rdma_cm_id *rds_rdma_listen_id;
static struct rdma_cm_id *rds6_rdma_listen_id;
int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id, static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id,
struct rdma_cm_event *event) struct rdma_cm_event *event,
bool isv6)
{ {
/* this can be null in the listening path */ /* this can be null in the listening path */
struct rds_connection *conn = cm_id->context; struct rds_connection *conn = cm_id->context;
...@@ -72,7 +75,7 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id, ...@@ -72,7 +75,7 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
switch (event->event) { switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST: case RDMA_CM_EVENT_CONNECT_REQUEST:
ret = trans->cm_handle_connect(cm_id, event); ret = trans->cm_handle_connect(cm_id, event, isv6);
break; break;
case RDMA_CM_EVENT_ADDR_RESOLVED: case RDMA_CM_EVENT_ADDR_RESOLVED:
...@@ -90,7 +93,7 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id, ...@@ -90,7 +93,7 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
ibic = conn->c_transport_data; ibic = conn->c_transport_data;
if (ibic && ibic->i_cm_id == cm_id) if (ibic && ibic->i_cm_id == cm_id)
ret = trans->cm_initiate_connect(cm_id); ret = trans->cm_initiate_connect(cm_id, isv6);
else else
rds_conn_drop(conn); rds_conn_drop(conn);
} }
...@@ -116,14 +119,14 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id, ...@@ -116,14 +119,14 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
case RDMA_CM_EVENT_DISCONNECTED: case RDMA_CM_EVENT_DISCONNECTED:
rdsdebug("DISCONNECT event - dropping connection " rdsdebug("DISCONNECT event - dropping connection "
"%pI4->%pI4\n", &conn->c_laddr, "%pI6c->%pI6c\n", &conn->c_laddr,
&conn->c_faddr); &conn->c_faddr);
rds_conn_drop(conn); rds_conn_drop(conn);
break; break;
case RDMA_CM_EVENT_TIMEWAIT_EXIT: case RDMA_CM_EVENT_TIMEWAIT_EXIT:
if (conn) { if (conn) {
pr_info("RDS: RDMA_CM_EVENT_TIMEWAIT_EXIT event: dropping connection %pI4->%pI4\n", pr_info("RDS: RDMA_CM_EVENT_TIMEWAIT_EXIT event: dropping connection %pI6c->%pI6c\n",
&conn->c_laddr, &conn->c_faddr); &conn->c_laddr, &conn->c_faddr);
rds_conn_drop(conn); rds_conn_drop(conn);
} }
...@@ -146,13 +149,26 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id, ...@@ -146,13 +149,26 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
return ret; return ret;
} }
static int rds_rdma_listen_init(void) int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
struct rdma_cm_event *event)
{
return rds_rdma_cm_event_handler_cmn(cm_id, event, false);
}
int rds6_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
struct rdma_cm_event *event)
{
return rds_rdma_cm_event_handler_cmn(cm_id, event, true);
}
static int rds_rdma_listen_init_common(rdma_cm_event_handler handler,
struct sockaddr *sa,
struct rdma_cm_id **ret_cm_id)
{ {
struct sockaddr_in sin;
struct rdma_cm_id *cm_id; struct rdma_cm_id *cm_id;
int ret; int ret;
cm_id = rdma_create_id(&init_net, rds_rdma_cm_event_handler, NULL, cm_id = rdma_create_id(&init_net, handler, NULL,
RDMA_PS_TCP, IB_QPT_RC); RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(cm_id)) { if (IS_ERR(cm_id)) {
ret = PTR_ERR(cm_id); ret = PTR_ERR(cm_id);
...@@ -161,15 +177,11 @@ static int rds_rdma_listen_init(void) ...@@ -161,15 +177,11 @@ static int rds_rdma_listen_init(void)
return ret; return ret;
} }
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY);
sin.sin_port = (__force u16)htons(RDS_PORT);
/* /*
* XXX I bet this binds the cm_id to a device. If we want to support * XXX I bet this binds the cm_id to a device. If we want to support
* fail-over we'll have to take this into consideration. * fail-over we'll have to take this into consideration.
*/ */
ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); ret = rdma_bind_addr(cm_id, sa);
if (ret) { if (ret) {
printk(KERN_ERR "RDS/RDMA: failed to setup listener, " printk(KERN_ERR "RDS/RDMA: failed to setup listener, "
"rdma_bind_addr() returned %d\n", ret); "rdma_bind_addr() returned %d\n", ret);
...@@ -185,7 +197,7 @@ static int rds_rdma_listen_init(void) ...@@ -185,7 +197,7 @@ static int rds_rdma_listen_init(void)
rdsdebug("cm %p listening on port %u\n", cm_id, RDS_PORT); rdsdebug("cm %p listening on port %u\n", cm_id, RDS_PORT);
rds_rdma_listen_id = cm_id; *ret_cm_id = cm_id;
cm_id = NULL; cm_id = NULL;
out: out:
if (cm_id) if (cm_id)
...@@ -193,6 +205,41 @@ static int rds_rdma_listen_init(void) ...@@ -193,6 +205,41 @@ static int rds_rdma_listen_init(void)
return ret; return ret;
} }
/* Initialize the RDS RDMA listeners. We create two listeners for
* compatibility reason. The one on RDS_PORT is used for IPv4
* requests only. The one on RDS_CM_PORT is used for IPv6 requests
* only. So only IPv6 enabled RDS module will communicate using this
* port.
*/
static int rds_rdma_listen_init(void)
{
int ret;
struct sockaddr_in6 sin6;
struct sockaddr_in sin;
sin.sin_family = PF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(RDS_PORT);
ret = rds_rdma_listen_init_common(rds_rdma_cm_event_handler,
(struct sockaddr *)&sin,
&rds_rdma_listen_id);
if (ret != 0)
return ret;
sin6.sin6_family = PF_INET6;
sin6.sin6_addr = in6addr_any;
sin6.sin6_port = htons(RDS_CM_PORT);
sin6.sin6_scope_id = 0;
sin6.sin6_flowinfo = 0;
ret = rds_rdma_listen_init_common(rds6_rdma_cm_event_handler,
(struct sockaddr *)&sin6,
&rds6_rdma_listen_id);
/* Keep going even when IPv6 is not enabled in the system. */
if (ret != 0)
rdsdebug("Cannot set up IPv6 RDMA listener\n");
return 0;
}
static void rds_rdma_listen_stop(void) static void rds_rdma_listen_stop(void)
{ {
if (rds_rdma_listen_id) { if (rds_rdma_listen_id) {
...@@ -200,6 +247,11 @@ static void rds_rdma_listen_stop(void) ...@@ -200,6 +247,11 @@ static void rds_rdma_listen_stop(void)
rdma_destroy_id(rds_rdma_listen_id); rdma_destroy_id(rds_rdma_listen_id);
rds_rdma_listen_id = NULL; rds_rdma_listen_id = NULL;
} }
if (rds6_rdma_listen_id) {
rdsdebug("cm %p\n", rds6_rdma_listen_id);
rdma_destroy_id(rds6_rdma_listen_id);
rds6_rdma_listen_id = NULL;
}
} }
static int rds_rdma_init(void) static int rds_rdma_init(void)
......
...@@ -6,11 +6,16 @@ ...@@ -6,11 +6,16 @@
#include <rdma/rdma_cm.h> #include <rdma/rdma_cm.h>
#include "rds.h" #include "rds.h"
/* RDMA_CM also uses 16385 as the listener port. */
#define RDS_CM_PORT 16385
#define RDS_RDMA_RESOLVE_TIMEOUT_MS 5000 #define RDS_RDMA_RESOLVE_TIMEOUT_MS 5000
int rds_rdma_conn_connect(struct rds_connection *conn); int rds_rdma_conn_connect(struct rds_connection *conn);
int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id, int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
struct rdma_cm_event *event); struct rdma_cm_event *event);
int rds6_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
struct rdma_cm_event *event);
/* from ib.c */ /* from ib.c */
extern struct rds_transport rds_ib_transport; extern struct rds_transport rds_ib_transport;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/rds.h> #include <linux/rds.h>
#include <linux/rhashtable.h> #include <linux/rhashtable.h>
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/in6.h>
#include "info.h" #include "info.h"
...@@ -23,11 +24,13 @@ ...@@ -23,11 +24,13 @@
#define RDS_PROTOCOL_MINOR(v) ((v) & 255) #define RDS_PROTOCOL_MINOR(v) ((v) & 255)
#define RDS_PROTOCOL(maj, min) (((maj) << 8) | min) #define RDS_PROTOCOL(maj, min) (((maj) << 8) | min)
/* /* The following ports, 16385, 18634, 18635, are registered with IANA as
* XXX randomly chosen, but at least seems to be unused: * the ports to be used for RDS over TCP and UDP. Currently, only RDS over
* # 18464-18768 Unassigned * TCP and RDS over IB/RDMA are implemented. 18634 is the historical value
* We should do better. We want a reserved port to discourage unpriv'ed * used for the RDMA_CM listener port. RDS/TCP uses port 16385. After
* userspace from listening. * IPv6 work, RDMA_CM also uses 16385 as the listener port. 18634 is kept
* to ensure compatibility with older RDS modules. Those ports are defined
* in each transport's header file.
*/ */
#define RDS_PORT 18634 #define RDS_PORT 18634
...@@ -61,7 +64,7 @@ void rdsdebug(char *fmt, ...) ...@@ -61,7 +64,7 @@ void rdsdebug(char *fmt, ...)
struct rds_cong_map { struct rds_cong_map {
struct rb_node m_rb_node; struct rb_node m_rb_node;
__be32 m_addr; struct in6_addr m_addr;
wait_queue_head_t m_waitq; wait_queue_head_t m_waitq;
struct list_head m_conn_list; struct list_head m_conn_list;
unsigned long m_page_addrs[RDS_CONG_MAP_PAGES]; unsigned long m_page_addrs[RDS_CONG_MAP_PAGES];
...@@ -136,11 +139,14 @@ struct rds_conn_path { ...@@ -136,11 +139,14 @@ struct rds_conn_path {
/* One rds_connection per RDS address pair */ /* One rds_connection per RDS address pair */
struct rds_connection { struct rds_connection {
struct hlist_node c_hash_node; struct hlist_node c_hash_node;
__be32 c_laddr; struct in6_addr c_laddr;
__be32 c_faddr; struct in6_addr c_faddr;
int c_dev_if; /* ifindex used for this conn */
int c_bound_if; /* ifindex of c_laddr */
unsigned int c_loopback:1, unsigned int c_loopback:1,
c_isv6:1,
c_ping_triggered:1, c_ping_triggered:1,
c_pad_to_32:30; c_pad_to_32:29;
int c_npaths; int c_npaths;
struct rds_connection *c_passive; struct rds_connection *c_passive;
struct rds_transport *c_trans; struct rds_transport *c_trans;
...@@ -269,7 +275,7 @@ struct rds_incoming { ...@@ -269,7 +275,7 @@ struct rds_incoming {
struct rds_conn_path *i_conn_path; struct rds_conn_path *i_conn_path;
struct rds_header i_hdr; struct rds_header i_hdr;
unsigned long i_rx_jiffies; unsigned long i_rx_jiffies;
__be32 i_saddr; struct in6_addr i_saddr;
rds_rdma_cookie_t i_rdma_cookie; rds_rdma_cookie_t i_rdma_cookie;
struct timeval i_rx_tstamp; struct timeval i_rx_tstamp;
...@@ -386,7 +392,7 @@ struct rds_message { ...@@ -386,7 +392,7 @@ struct rds_message {
struct list_head m_conn_item; struct list_head m_conn_item;
struct rds_incoming m_inc; struct rds_incoming m_inc;
u64 m_ack_seq; u64 m_ack_seq;
__be32 m_daddr; struct in6_addr m_daddr;
unsigned long m_flags; unsigned long m_flags;
/* Never access m_rs without holding m_rs_lock. /* Never access m_rs without holding m_rs_lock.
...@@ -519,7 +525,8 @@ struct rds_transport { ...@@ -519,7 +525,8 @@ struct rds_transport {
t_mp_capable:1; t_mp_capable:1;
unsigned int t_type; unsigned int t_type;
int (*laddr_check)(struct net *net, __be32 addr); int (*laddr_check)(struct net *net, const struct in6_addr *addr,
__u32 scope_id);
int (*conn_alloc)(struct rds_connection *conn, gfp_t gfp); int (*conn_alloc)(struct rds_connection *conn, gfp_t gfp);
void (*conn_free)(void *data); void (*conn_free)(void *data);
int (*conn_path_connect)(struct rds_conn_path *cp); int (*conn_path_connect)(struct rds_conn_path *cp);
...@@ -535,8 +542,8 @@ struct rds_transport { ...@@ -535,8 +542,8 @@ struct rds_transport {
void (*inc_free)(struct rds_incoming *inc); void (*inc_free)(struct rds_incoming *inc);
int (*cm_handle_connect)(struct rdma_cm_id *cm_id, int (*cm_handle_connect)(struct rdma_cm_id *cm_id,
struct rdma_cm_event *event); struct rdma_cm_event *event, bool isv6);
int (*cm_initiate_connect)(struct rdma_cm_id *cm_id); int (*cm_initiate_connect)(struct rdma_cm_id *cm_id, bool isv6);
void (*cm_connect_complete)(struct rds_connection *conn, void (*cm_connect_complete)(struct rds_connection *conn,
struct rdma_cm_event *event); struct rdma_cm_event *event);
...@@ -551,6 +558,12 @@ struct rds_transport { ...@@ -551,6 +558,12 @@ struct rds_transport {
bool (*t_unloading)(struct rds_connection *conn); bool (*t_unloading)(struct rds_connection *conn);
}; };
/* Bind hash table key length. It is the sum of the size of a struct
* in6_addr, a scope_id and a port.
*/
#define RDS_BOUND_KEY_LEN \
(sizeof(struct in6_addr) + sizeof(__u32) + sizeof(__be16))
struct rds_sock { struct rds_sock {
struct sock rs_sk; struct sock rs_sk;
...@@ -562,10 +575,14 @@ struct rds_sock { ...@@ -562,10 +575,14 @@ struct rds_sock {
* support. * support.
*/ */
struct rhash_head rs_bound_node; struct rhash_head rs_bound_node;
u64 rs_bound_key; u8 rs_bound_key[RDS_BOUND_KEY_LEN];
__be32 rs_bound_addr; struct sockaddr_in6 rs_bound_sin6;
__be32 rs_conn_addr; #define rs_bound_addr rs_bound_sin6.sin6_addr
__be16 rs_bound_port; #define rs_bound_addr_v4 rs_bound_sin6.sin6_addr.s6_addr32[3]
#define rs_bound_port rs_bound_sin6.sin6_port
#define rs_bound_scope_id rs_bound_sin6.sin6_scope_id
struct in6_addr rs_conn_addr;
#define rs_conn_addr_v4 rs_conn_addr.s6_addr32[3]
__be16 rs_conn_port; __be16 rs_conn_port;
struct rds_transport *rs_transport; struct rds_transport *rs_transport;
...@@ -701,7 +718,8 @@ extern wait_queue_head_t rds_poll_waitq; ...@@ -701,7 +718,8 @@ extern wait_queue_head_t rds_poll_waitq;
/* bind.c */ /* bind.c */
int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len);
void rds_remove_bound(struct rds_sock *rs); void rds_remove_bound(struct rds_sock *rs);
struct rds_sock *rds_find_bound(__be32 addr, __be16 port); struct rds_sock *rds_find_bound(const struct in6_addr *addr, __be16 port,
__u32 scope_id);
int rds_bind_lock_init(void); int rds_bind_lock_init(void);
void rds_bind_lock_destroy(void); void rds_bind_lock_destroy(void);
...@@ -720,16 +738,20 @@ void rds_cong_remove_socket(struct rds_sock *); ...@@ -720,16 +738,20 @@ void rds_cong_remove_socket(struct rds_sock *);
void rds_cong_exit(void); void rds_cong_exit(void);
struct rds_message *rds_cong_update_alloc(struct rds_connection *conn); struct rds_message *rds_cong_update_alloc(struct rds_connection *conn);
/* conn.c */ /* connection.c */
extern u32 rds_gen_num; extern u32 rds_gen_num;
int rds_conn_init(void); int rds_conn_init(void);
void rds_conn_exit(void); void rds_conn_exit(void);
struct rds_connection *rds_conn_create(struct net *net, struct rds_connection *rds_conn_create(struct net *net,
__be32 laddr, __be32 faddr, const struct in6_addr *laddr,
struct rds_transport *trans, gfp_t gfp); const struct in6_addr *faddr,
struct rds_transport *trans, gfp_t gfp,
int dev_if);
struct rds_connection *rds_conn_create_outgoing(struct net *net, struct rds_connection *rds_conn_create_outgoing(struct net *net,
__be32 laddr, __be32 faddr, const struct in6_addr *laddr,
struct rds_transport *trans, gfp_t gfp); const struct in6_addr *faddr,
struct rds_transport *trans,
gfp_t gfp, int dev_if);
void rds_conn_shutdown(struct rds_conn_path *cpath); void rds_conn_shutdown(struct rds_conn_path *cpath);
void rds_conn_destroy(struct rds_connection *conn); void rds_conn_destroy(struct rds_connection *conn);
void rds_conn_drop(struct rds_connection *conn); void rds_conn_drop(struct rds_connection *conn);
...@@ -840,11 +862,12 @@ void rds_page_exit(void); ...@@ -840,11 +862,12 @@ void rds_page_exit(void);
/* recv.c */ /* recv.c */
void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn, void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn,
__be32 saddr); struct in6_addr *saddr);
void rds_inc_path_init(struct rds_incoming *inc, struct rds_conn_path *conn, void rds_inc_path_init(struct rds_incoming *inc, struct rds_conn_path *conn,
__be32 saddr); struct in6_addr *saddr);
void rds_inc_put(struct rds_incoming *inc); void rds_inc_put(struct rds_incoming *inc);
void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr, void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr,
struct in6_addr *daddr,
struct rds_incoming *inc, gfp_t gfp); struct rds_incoming *inc, gfp_t gfp);
int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
int msg_flags); int msg_flags);
...@@ -853,13 +876,17 @@ int rds_notify_queue_get(struct rds_sock *rs, struct msghdr *msg); ...@@ -853,13 +876,17 @@ int rds_notify_queue_get(struct rds_sock *rs, struct msghdr *msg);
void rds_inc_info_copy(struct rds_incoming *inc, void rds_inc_info_copy(struct rds_incoming *inc,
struct rds_info_iterator *iter, struct rds_info_iterator *iter,
__be32 saddr, __be32 daddr, int flip); __be32 saddr, __be32 daddr, int flip);
void rds6_inc_info_copy(struct rds_incoming *inc,
struct rds_info_iterator *iter,
struct in6_addr *saddr, struct in6_addr *daddr,
int flip);
/* send.c */ /* send.c */
int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len); int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len);
void rds_send_path_reset(struct rds_conn_path *conn); void rds_send_path_reset(struct rds_conn_path *conn);
int rds_send_xmit(struct rds_conn_path *cp); int rds_send_xmit(struct rds_conn_path *cp);
struct sockaddr_in; struct sockaddr_in;
void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest); void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in6 *dest);
typedef int (*is_acked_func)(struct rds_message *rm, uint64_t ack); typedef int (*is_acked_func)(struct rds_message *rm, uint64_t ack);
void rds_send_drop_acked(struct rds_connection *conn, u64 ack, void rds_send_drop_acked(struct rds_connection *conn, u64 ack,
is_acked_func is_acked); is_acked_func is_acked);
...@@ -946,11 +973,14 @@ void rds_send_worker(struct work_struct *); ...@@ -946,11 +973,14 @@ void rds_send_worker(struct work_struct *);
void rds_recv_worker(struct work_struct *); void rds_recv_worker(struct work_struct *);
void rds_connect_path_complete(struct rds_conn_path *conn, int curr); void rds_connect_path_complete(struct rds_conn_path *conn, int curr);
void rds_connect_complete(struct rds_connection *conn); void rds_connect_complete(struct rds_connection *conn);
int rds_addr_cmp(const struct in6_addr *a1, const struct in6_addr *a2);
/* transport.c */ /* transport.c */
void rds_trans_register(struct rds_transport *trans); void rds_trans_register(struct rds_transport *trans);
void rds_trans_unregister(struct rds_transport *trans); void rds_trans_unregister(struct rds_transport *trans);
struct rds_transport *rds_trans_get_preferred(struct net *net, __be32 addr); struct rds_transport *rds_trans_get_preferred(struct net *net,
const struct in6_addr *addr,
__u32 scope_id);
void rds_trans_put(struct rds_transport *trans); void rds_trans_put(struct rds_transport *trans);
unsigned int rds_trans_stats_info_copy(struct rds_info_iterator *iter, unsigned int rds_trans_stats_info_copy(struct rds_info_iterator *iter,
unsigned int avail); unsigned int avail);
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -41,14 +41,14 @@ ...@@ -41,14 +41,14 @@
#include "rds.h" #include "rds.h"
void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn, void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn,
__be32 saddr) struct in6_addr *saddr)
{ {
int i; int i;
refcount_set(&inc->i_refcount, 1); refcount_set(&inc->i_refcount, 1);
INIT_LIST_HEAD(&inc->i_item); INIT_LIST_HEAD(&inc->i_item);
inc->i_conn = conn; inc->i_conn = conn;
inc->i_saddr = saddr; inc->i_saddr = *saddr;
inc->i_rdma_cookie = 0; inc->i_rdma_cookie = 0;
inc->i_rx_tstamp.tv_sec = 0; inc->i_rx_tstamp.tv_sec = 0;
inc->i_rx_tstamp.tv_usec = 0; inc->i_rx_tstamp.tv_usec = 0;
...@@ -59,13 +59,13 @@ void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn, ...@@ -59,13 +59,13 @@ void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn,
EXPORT_SYMBOL_GPL(rds_inc_init); EXPORT_SYMBOL_GPL(rds_inc_init);
void rds_inc_path_init(struct rds_incoming *inc, struct rds_conn_path *cp, void rds_inc_path_init(struct rds_incoming *inc, struct rds_conn_path *cp,
__be32 saddr) struct in6_addr *saddr)
{ {
refcount_set(&inc->i_refcount, 1); refcount_set(&inc->i_refcount, 1);
INIT_LIST_HEAD(&inc->i_item); INIT_LIST_HEAD(&inc->i_item);
inc->i_conn = cp->cp_conn; inc->i_conn = cp->cp_conn;
inc->i_conn_path = cp; inc->i_conn_path = cp;
inc->i_saddr = saddr; inc->i_saddr = *saddr;
inc->i_rdma_cookie = 0; inc->i_rdma_cookie = 0;
inc->i_rx_tstamp.tv_sec = 0; inc->i_rx_tstamp.tv_sec = 0;
inc->i_rx_tstamp.tv_usec = 0; inc->i_rx_tstamp.tv_usec = 0;
...@@ -110,7 +110,7 @@ static void rds_recv_rcvbuf_delta(struct rds_sock *rs, struct sock *sk, ...@@ -110,7 +110,7 @@ static void rds_recv_rcvbuf_delta(struct rds_sock *rs, struct sock *sk,
now_congested = rs->rs_rcv_bytes > rds_sk_rcvbuf(rs); now_congested = rs->rs_rcv_bytes > rds_sk_rcvbuf(rs);
rdsdebug("rs %p (%pI4:%u) recv bytes %d buf %d " rdsdebug("rs %p (%pI6c:%u) recv bytes %d buf %d "
"now_cong %d delta %d\n", "now_cong %d delta %d\n",
rs, &rs->rs_bound_addr, rs, &rs->rs_bound_addr,
ntohs(rs->rs_bound_port), rs->rs_rcv_bytes, ntohs(rs->rs_bound_port), rs->rs_rcv_bytes,
...@@ -260,7 +260,7 @@ static void rds_start_mprds(struct rds_connection *conn) ...@@ -260,7 +260,7 @@ static void rds_start_mprds(struct rds_connection *conn)
struct rds_conn_path *cp; struct rds_conn_path *cp;
if (conn->c_npaths > 1 && if (conn->c_npaths > 1 &&
IS_CANONICAL(conn->c_laddr, conn->c_faddr)) { rds_addr_cmp(&conn->c_laddr, &conn->c_faddr) < 0) {
for (i = 0; i < conn->c_npaths; i++) { for (i = 0; i < conn->c_npaths; i++) {
cp = &conn->c_path[i]; cp = &conn->c_path[i];
rds_conn_path_connect_if_down(cp); rds_conn_path_connect_if_down(cp);
...@@ -284,7 +284,8 @@ static void rds_start_mprds(struct rds_connection *conn) ...@@ -284,7 +284,8 @@ static void rds_start_mprds(struct rds_connection *conn)
* conn. This lets loopback, who only has one conn for both directions, * conn. This lets loopback, who only has one conn for both directions,
* tell us which roles the addrs in the conn are playing for this message. * tell us which roles the addrs in the conn are playing for this message.
*/ */
void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr, void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr,
struct in6_addr *daddr,
struct rds_incoming *inc, gfp_t gfp) struct rds_incoming *inc, gfp_t gfp)
{ {
struct rds_sock *rs = NULL; struct rds_sock *rs = NULL;
...@@ -339,7 +340,8 @@ void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr, ...@@ -339,7 +340,8 @@ void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr,
if (rds_sysctl_ping_enable && inc->i_hdr.h_dport == 0) { if (rds_sysctl_ping_enable && inc->i_hdr.h_dport == 0) {
if (inc->i_hdr.h_sport == 0) { if (inc->i_hdr.h_sport == 0) {
rdsdebug("ignore ping with 0 sport from 0x%x\n", saddr); rdsdebug("ignore ping with 0 sport from %pI6c\n",
saddr);
goto out; goto out;
} }
rds_stats_inc(s_recv_ping); rds_stats_inc(s_recv_ping);
...@@ -362,7 +364,7 @@ void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr, ...@@ -362,7 +364,7 @@ void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr,
goto out; goto out;
} }
rs = rds_find_bound(daddr, inc->i_hdr.h_dport); rs = rds_find_bound(daddr, inc->i_hdr.h_dport, conn->c_bound_if);
if (!rs) { if (!rs) {
rds_stats_inc(s_recv_drop_no_sock); rds_stats_inc(s_recv_drop_no_sock);
goto out; goto out;
...@@ -625,6 +627,7 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, ...@@ -625,6 +627,7 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
struct rds_sock *rs = rds_sk_to_rs(sk); struct rds_sock *rs = rds_sk_to_rs(sk);
long timeo; long timeo;
int ret = 0, nonblock = msg_flags & MSG_DONTWAIT; int ret = 0, nonblock = msg_flags & MSG_DONTWAIT;
DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
struct rds_incoming *inc = NULL; struct rds_incoming *inc = NULL;
...@@ -673,7 +676,7 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, ...@@ -673,7 +676,7 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
break; break;
} }
rdsdebug("copying inc %p from %pI4:%u to user\n", inc, rdsdebug("copying inc %p from %pI6c:%u to user\n", inc,
&inc->i_conn->c_faddr, &inc->i_conn->c_faddr,
ntohs(inc->i_hdr.h_sport)); ntohs(inc->i_hdr.h_sport));
ret = inc->i_conn->c_trans->inc_copy_to_user(inc, &msg->msg_iter); ret = inc->i_conn->c_trans->inc_copy_to_user(inc, &msg->msg_iter);
...@@ -707,12 +710,26 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, ...@@ -707,12 +710,26 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
rds_stats_inc(s_recv_delivered); rds_stats_inc(s_recv_delivered);
if (sin) { if (msg->msg_name) {
if (ipv6_addr_v4mapped(&inc->i_saddr)) {
sin = (struct sockaddr_in *)msg->msg_name;
sin->sin_family = AF_INET; sin->sin_family = AF_INET;
sin->sin_port = inc->i_hdr.h_sport; sin->sin_port = inc->i_hdr.h_sport;
sin->sin_addr.s_addr = inc->i_saddr; sin->sin_addr.s_addr =
inc->i_saddr.s6_addr32[3];
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
msg->msg_namelen = sizeof(*sin); msg->msg_namelen = sizeof(*sin);
} else {
sin6 = (struct sockaddr_in6 *)msg->msg_name;
sin6->sin6_family = AF_INET6;
sin6->sin6_port = inc->i_hdr.h_sport;
sin6->sin6_addr = inc->i_saddr;
sin6->sin6_flowinfo = 0;
sin6->sin6_scope_id = rs->rs_bound_scope_id;
msg->msg_namelen = sizeof(*sin6);
}
} }
break; break;
} }
...@@ -775,3 +792,28 @@ void rds_inc_info_copy(struct rds_incoming *inc, ...@@ -775,3 +792,28 @@ void rds_inc_info_copy(struct rds_incoming *inc,
rds_info_copy(iter, &minfo, sizeof(minfo)); rds_info_copy(iter, &minfo, sizeof(minfo));
} }
void rds6_inc_info_copy(struct rds_incoming *inc,
struct rds_info_iterator *iter,
struct in6_addr *saddr, struct in6_addr *daddr,
int flip)
{
struct rds6_info_message minfo6;
minfo6.seq = be64_to_cpu(inc->i_hdr.h_sequence);
minfo6.len = be32_to_cpu(inc->i_hdr.h_len);
if (flip) {
minfo6.laddr = *daddr;
minfo6.faddr = *saddr;
minfo6.lport = inc->i_hdr.h_dport;
minfo6.fport = inc->i_hdr.h_sport;
} else {
minfo6.laddr = *saddr;
minfo6.faddr = *daddr;
minfo6.lport = inc->i_hdr.h_sport;
minfo6.fport = inc->i_hdr.h_dport;
}
rds_info_copy(iter, &minfo6, sizeof(minfo6));
}
...@@ -709,7 +709,7 @@ void rds_send_drop_acked(struct rds_connection *conn, u64 ack, ...@@ -709,7 +709,7 @@ void rds_send_drop_acked(struct rds_connection *conn, u64 ack,
} }
EXPORT_SYMBOL_GPL(rds_send_drop_acked); EXPORT_SYMBOL_GPL(rds_send_drop_acked);
void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest) void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in6 *dest)
{ {
struct rds_message *rm, *tmp; struct rds_message *rm, *tmp;
struct rds_connection *conn; struct rds_connection *conn;
...@@ -721,8 +721,9 @@ void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest) ...@@ -721,8 +721,9 @@ void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest)
spin_lock_irqsave(&rs->rs_lock, flags); spin_lock_irqsave(&rs->rs_lock, flags);
list_for_each_entry_safe(rm, tmp, &rs->rs_send_queue, m_sock_item) { list_for_each_entry_safe(rm, tmp, &rs->rs_send_queue, m_sock_item) {
if (dest && (dest->sin_addr.s_addr != rm->m_daddr || if (dest &&
dest->sin_port != rm->m_inc.i_hdr.h_dport)) (!ipv6_addr_equal(&dest->sin6_addr, &rm->m_daddr) ||
dest->sin6_port != rm->m_inc.i_hdr.h_dport))
continue; continue;
list_move(&rm->m_sock_item, &list); list_move(&rm->m_sock_item, &list);
...@@ -1059,8 +1060,8 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) ...@@ -1059,8 +1060,8 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct rds_sock *rs = rds_sk_to_rs(sk); struct rds_sock *rs = rds_sk_to_rs(sk);
DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name); DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name);
__be32 daddr;
__be16 dport; __be16 dport;
struct rds_message *rm = NULL; struct rds_message *rm = NULL;
struct rds_connection *conn; struct rds_connection *conn;
...@@ -1069,10 +1070,13 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) ...@@ -1069,10 +1070,13 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
int nonblock = msg->msg_flags & MSG_DONTWAIT; int nonblock = msg->msg_flags & MSG_DONTWAIT;
long timeo = sock_sndtimeo(sk, nonblock); long timeo = sock_sndtimeo(sk, nonblock);
struct rds_conn_path *cpath; struct rds_conn_path *cpath;
struct in6_addr daddr;
__u32 scope_id = 0;
size_t total_payload_len = payload_len, rdma_payload_len = 0; size_t total_payload_len = payload_len, rdma_payload_len = 0;
bool zcopy = ((msg->msg_flags & MSG_ZEROCOPY) && bool zcopy = ((msg->msg_flags & MSG_ZEROCOPY) &&
sock_flag(rds_rs_to_sk(rs), SOCK_ZEROCOPY)); sock_flag(rds_rs_to_sk(rs), SOCK_ZEROCOPY));
int num_sgs = ceil(payload_len, PAGE_SIZE); int num_sgs = ceil(payload_len, PAGE_SIZE);
int namelen;
/* Mirror Linux UDP mirror of BSD error message compatibility */ /* Mirror Linux UDP mirror of BSD error message compatibility */
/* XXX: Perhaps MSG_MORE someday */ /* XXX: Perhaps MSG_MORE someday */
...@@ -1081,27 +1085,106 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) ...@@ -1081,27 +1085,106 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
goto out; goto out;
} }
if (msg->msg_namelen) { namelen = msg->msg_namelen;
/* XXX fail non-unicast destination IPs? */ if (namelen != 0) {
if (msg->msg_namelen < sizeof(*usin) || usin->sin_family != AF_INET) { if (namelen < sizeof(*usin)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
daddr = usin->sin_addr.s_addr; switch (usin->sin_family) {
case AF_INET:
if (usin->sin_addr.s_addr == htonl(INADDR_ANY) ||
usin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
IN_MULTICAST(ntohl(usin->sin_addr.s_addr))) {
ret = -EINVAL;
goto out;
}
ipv6_addr_set_v4mapped(usin->sin_addr.s_addr, &daddr);
dport = usin->sin_port; dport = usin->sin_port;
break;
case AF_INET6: {
int addr_type;
if (namelen < sizeof(*sin6)) {
ret = -EINVAL;
goto out;
}
addr_type = ipv6_addr_type(&sin6->sin6_addr);
if (!(addr_type & IPV6_ADDR_UNICAST)) {
__be32 addr4;
if (!(addr_type & IPV6_ADDR_MAPPED)) {
ret = -EINVAL;
goto out;
}
/* It is a mapped address. Need to do some
* sanity checks.
*/
addr4 = sin6->sin6_addr.s6_addr32[3];
if (addr4 == htonl(INADDR_ANY) ||
addr4 == htonl(INADDR_BROADCAST) ||
IN_MULTICAST(ntohl(addr4))) {
return -EINVAL;
goto out;
}
}
if (addr_type & IPV6_ADDR_LINKLOCAL) {
if (sin6->sin6_scope_id == 0) {
ret = -EINVAL;
goto out;
}
scope_id = sin6->sin6_scope_id;
}
daddr = sin6->sin6_addr;
dport = sin6->sin6_port;
break;
}
default:
ret = -EINVAL;
goto out;
}
} else { } else {
/* We only care about consistency with ->connect() */ /* We only care about consistency with ->connect() */
lock_sock(sk); lock_sock(sk);
daddr = rs->rs_conn_addr; daddr = rs->rs_conn_addr;
dport = rs->rs_conn_port; dport = rs->rs_conn_port;
scope_id = rs->rs_bound_scope_id;
release_sock(sk); release_sock(sk);
} }
lock_sock(sk); lock_sock(sk);
if (daddr == 0 || rs->rs_bound_addr == 0) { if (ipv6_addr_any(&rs->rs_bound_addr) || ipv6_addr_any(&daddr)) {
release_sock(sk); release_sock(sk);
ret = -ENOTCONN; /* XXX not a great errno */ ret = -ENOTCONN;
goto out; goto out;
} else if (namelen != 0) {
/* Cannot send to an IPv4 address using an IPv6 source
* address and cannot send to an IPv6 address using an
* IPv4 source address.
*/
if (ipv6_addr_v4mapped(&daddr) ^
ipv6_addr_v4mapped(&rs->rs_bound_addr)) {
release_sock(sk);
ret = -EOPNOTSUPP;
goto out;
}
/* If the socket is already bound to a link local address,
* it can only send to peers on the same link. But allow
* communicating beween link local and non-link local address.
*/
if (scope_id != rs->rs_bound_scope_id) {
if (!scope_id) {
scope_id = rs->rs_bound_scope_id;
} else if (rs->rs_bound_scope_id) {
release_sock(sk);
ret = -EINVAL;
goto out;
}
}
} }
release_sock(sk); release_sock(sk);
...@@ -1155,13 +1238,14 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) ...@@ -1155,13 +1238,14 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
/* rds_conn_create has a spinlock that runs with IRQ off. /* rds_conn_create has a spinlock that runs with IRQ off.
* Caching the conn in the socket helps a lot. */ * Caching the conn in the socket helps a lot. */
if (rs->rs_conn && rs->rs_conn->c_faddr == daddr) if (rs->rs_conn && ipv6_addr_equal(&rs->rs_conn->c_faddr, &daddr))
conn = rs->rs_conn; conn = rs->rs_conn;
else { else {
conn = rds_conn_create_outgoing(sock_net(sock->sk), conn = rds_conn_create_outgoing(sock_net(sock->sk),
rs->rs_bound_addr, daddr, &rs->rs_bound_addr, &daddr,
rs->rs_transport, rs->rs_transport,
sock->sk->sk_allocation); sock->sk->sk_allocation,
scope_id);
if (IS_ERR(conn)) { if (IS_ERR(conn)) {
ret = PTR_ERR(conn); ret = PTR_ERR(conn);
goto out; goto out;
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
#include <net/tcp.h> #include <net/tcp.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>
#include <net/tcp.h>
#include <net/addrconf.h>
#include "rds.h" #include "rds.h"
#include "tcp.h" #include "tcp.h"
...@@ -44,7 +46,12 @@ ...@@ -44,7 +46,12 @@
/* only for info exporting */ /* only for info exporting */
static DEFINE_SPINLOCK(rds_tcp_tc_list_lock); static DEFINE_SPINLOCK(rds_tcp_tc_list_lock);
static LIST_HEAD(rds_tcp_tc_list); static LIST_HEAD(rds_tcp_tc_list);
/* rds_tcp_tc_count counts only IPv4 connections.
* rds6_tcp_tc_count counts both IPv4 and IPv6 connections.
*/
static unsigned int rds_tcp_tc_count; static unsigned int rds_tcp_tc_count;
static unsigned int rds6_tcp_tc_count;
/* Track rds_tcp_connection structs so they can be cleaned up */ /* Track rds_tcp_connection structs so they can be cleaned up */
static DEFINE_SPINLOCK(rds_tcp_conn_lock); static DEFINE_SPINLOCK(rds_tcp_conn_lock);
...@@ -111,6 +118,8 @@ void rds_tcp_restore_callbacks(struct socket *sock, ...@@ -111,6 +118,8 @@ void rds_tcp_restore_callbacks(struct socket *sock,
/* done under the callback_lock to serialize with write_space */ /* done under the callback_lock to serialize with write_space */
spin_lock(&rds_tcp_tc_list_lock); spin_lock(&rds_tcp_tc_list_lock);
list_del_init(&tc->t_list_item); list_del_init(&tc->t_list_item);
rds6_tcp_tc_count--;
if (!tc->t_cpath->cp_conn->c_isv6)
rds_tcp_tc_count--; rds_tcp_tc_count--;
spin_unlock(&rds_tcp_tc_list_lock); spin_unlock(&rds_tcp_tc_list_lock);
...@@ -198,6 +207,8 @@ void rds_tcp_set_callbacks(struct socket *sock, struct rds_conn_path *cp) ...@@ -198,6 +207,8 @@ void rds_tcp_set_callbacks(struct socket *sock, struct rds_conn_path *cp)
/* done under the callback_lock to serialize with write_space */ /* done under the callback_lock to serialize with write_space */
spin_lock(&rds_tcp_tc_list_lock); spin_lock(&rds_tcp_tc_list_lock);
list_add_tail(&tc->t_list_item, &rds_tcp_tc_list); list_add_tail(&tc->t_list_item, &rds_tcp_tc_list);
rds6_tcp_tc_count++;
if (!tc->t_cpath->cp_conn->c_isv6)
rds_tcp_tc_count++; rds_tcp_tc_count++;
spin_unlock(&rds_tcp_tc_list_lock); spin_unlock(&rds_tcp_tc_list_lock);
...@@ -219,6 +230,9 @@ void rds_tcp_set_callbacks(struct socket *sock, struct rds_conn_path *cp) ...@@ -219,6 +230,9 @@ void rds_tcp_set_callbacks(struct socket *sock, struct rds_conn_path *cp)
write_unlock_bh(&sock->sk->sk_callback_lock); write_unlock_bh(&sock->sk->sk_callback_lock);
} }
/* Handle RDS_INFO_TCP_SOCKETS socket option. It only returns IPv4
* connections for backward compatibility.
*/
static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len, static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
struct rds_info_iterator *iter, struct rds_info_iterator *iter,
struct rds_info_lengths *lens) struct rds_info_lengths *lens)
...@@ -226,8 +240,6 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len, ...@@ -226,8 +240,6 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
struct rds_info_tcp_socket tsinfo; struct rds_info_tcp_socket tsinfo;
struct rds_tcp_connection *tc; struct rds_tcp_connection *tc;
unsigned long flags; unsigned long flags;
struct sockaddr_in sin;
struct socket *sock;
spin_lock_irqsave(&rds_tcp_tc_list_lock, flags); spin_lock_irqsave(&rds_tcp_tc_list_lock, flags);
...@@ -235,16 +247,15 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len, ...@@ -235,16 +247,15 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
goto out; goto out;
list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) { list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) {
struct inet_sock *inet = inet_sk(tc->t_sock->sk);
sock = tc->t_sock; if (tc->t_cpath->cp_conn->c_isv6)
if (sock) { continue;
sock->ops->getname(sock, (struct sockaddr *)&sin, 0);
tsinfo.local_addr = sin.sin_addr.s_addr; tsinfo.local_addr = inet->inet_saddr;
tsinfo.local_port = sin.sin_port; tsinfo.local_port = inet->inet_sport;
sock->ops->getname(sock, (struct sockaddr *)&sin, 1); tsinfo.peer_addr = inet->inet_daddr;
tsinfo.peer_addr = sin.sin_addr.s_addr; tsinfo.peer_port = inet->inet_dport;
tsinfo.peer_port = sin.sin_port;
}
tsinfo.hdr_rem = tc->t_tinc_hdr_rem; tsinfo.hdr_rem = tc->t_tinc_hdr_rem;
tsinfo.data_rem = tc->t_tinc_data_rem; tsinfo.data_rem = tc->t_tinc_data_rem;
...@@ -262,9 +273,75 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len, ...@@ -262,9 +273,75 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
spin_unlock_irqrestore(&rds_tcp_tc_list_lock, flags); spin_unlock_irqrestore(&rds_tcp_tc_list_lock, flags);
} }
static int rds_tcp_laddr_check(struct net *net, __be32 addr) /* Handle RDS6_INFO_TCP_SOCKETS socket option. It returns both IPv4 and
* IPv6 connections. IPv4 connection address is returned in an IPv4 mapped
* address.
*/
static void rds6_tcp_tc_info(struct socket *sock, unsigned int len,
struct rds_info_iterator *iter,
struct rds_info_lengths *lens)
{ {
if (inet_addr_type(net, addr) == RTN_LOCAL) struct rds6_info_tcp_socket tsinfo6;
struct rds_tcp_connection *tc;
unsigned long flags;
spin_lock_irqsave(&rds_tcp_tc_list_lock, flags);
if (len / sizeof(tsinfo6) < rds6_tcp_tc_count)
goto out;
list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) {
struct sock *sk = tc->t_sock->sk;
struct inet_sock *inet = inet_sk(sk);
tsinfo6.local_addr = sk->sk_v6_rcv_saddr;
tsinfo6.local_port = inet->inet_sport;
tsinfo6.peer_addr = sk->sk_v6_daddr;
tsinfo6.peer_port = inet->inet_dport;
tsinfo6.hdr_rem = tc->t_tinc_hdr_rem;
tsinfo6.data_rem = tc->t_tinc_data_rem;
tsinfo6.last_sent_nxt = tc->t_last_sent_nxt;
tsinfo6.last_expected_una = tc->t_last_expected_una;
tsinfo6.last_seen_una = tc->t_last_seen_una;
rds_info_copy(iter, &tsinfo6, sizeof(tsinfo6));
}
out:
lens->nr = rds6_tcp_tc_count;
lens->each = sizeof(tsinfo6);
spin_unlock_irqrestore(&rds_tcp_tc_list_lock, flags);
}
static int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
__u32 scope_id)
{
struct net_device *dev = NULL;
int ret;
if (ipv6_addr_v4mapped(addr)) {
if (inet_addr_type(net, addr->s6_addr32[3]) == RTN_LOCAL)
return 0;
return -EADDRNOTAVAIL;
}
/* If the scope_id is specified, check only those addresses
* hosted on the specified interface.
*/
if (scope_id != 0) {
rcu_read_lock();
dev = dev_get_by_index_rcu(net, scope_id);
/* scope_id is not valid... */
if (!dev) {
rcu_read_unlock();
return -EADDRNOTAVAIL;
}
rcu_read_unlock();
}
ret = ipv6_chk_addr(net, addr, dev, 0);
if (ret)
return 0; return 0;
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
} }
...@@ -468,14 +545,19 @@ static __net_init int rds_tcp_init_net(struct net *net) ...@@ -468,14 +545,19 @@ static __net_init int rds_tcp_init_net(struct net *net)
err = -ENOMEM; err = -ENOMEM;
goto fail; goto fail;
} }
rtn->rds_tcp_listen_sock = rds_tcp_listen_init(net); rtn->rds_tcp_listen_sock = rds_tcp_listen_init(net, true);
if (!rtn->rds_tcp_listen_sock) {
pr_warn("could not set up IPv6 listen sock\n");
/* Try IPv4 as some systems disable IPv6 */
rtn->rds_tcp_listen_sock = rds_tcp_listen_init(net, false);
if (!rtn->rds_tcp_listen_sock) { if (!rtn->rds_tcp_listen_sock) {
pr_warn("could not set up listen sock\n");
unregister_net_sysctl_table(rtn->rds_tcp_sysctl); unregister_net_sysctl_table(rtn->rds_tcp_sysctl);
rtn->rds_tcp_sysctl = NULL; rtn->rds_tcp_sysctl = NULL;
err = -EAFNOSUPPORT; err = -EAFNOSUPPORT;
goto fail; goto fail;
} }
}
INIT_WORK(&rtn->rds_tcp_accept_w, rds_tcp_accept_worker); INIT_WORK(&rtn->rds_tcp_accept_w, rds_tcp_accept_worker);
return 0; return 0;
...@@ -588,6 +670,7 @@ static void rds_tcp_exit(void) ...@@ -588,6 +670,7 @@ static void rds_tcp_exit(void)
rds_tcp_set_unloading(); rds_tcp_set_unloading();
synchronize_rcu(); synchronize_rcu();
rds_info_deregister_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info); rds_info_deregister_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info);
rds_info_deregister_func(RDS6_INFO_TCP_SOCKETS, rds6_tcp_tc_info);
unregister_pernet_device(&rds_tcp_net_ops); unregister_pernet_device(&rds_tcp_net_ops);
rds_tcp_destroy_conns(); rds_tcp_destroy_conns();
rds_trans_unregister(&rds_tcp_transport); rds_trans_unregister(&rds_tcp_transport);
...@@ -619,6 +702,7 @@ static int rds_tcp_init(void) ...@@ -619,6 +702,7 @@ static int rds_tcp_init(void)
rds_trans_register(&rds_tcp_transport); rds_trans_register(&rds_tcp_transport);
rds_info_register_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info); rds_info_register_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info);
rds_info_register_func(RDS6_INFO_TCP_SOCKETS, rds6_tcp_tc_info);
goto out; goto out;
out_recv: out_recv:
......
...@@ -67,7 +67,7 @@ void rds_tcp_conn_path_shutdown(struct rds_conn_path *conn); ...@@ -67,7 +67,7 @@ void rds_tcp_conn_path_shutdown(struct rds_conn_path *conn);
void rds_tcp_state_change(struct sock *sk); void rds_tcp_state_change(struct sock *sk);
/* tcp_listen.c */ /* tcp_listen.c */
struct socket *rds_tcp_listen_init(struct net *); struct socket *rds_tcp_listen_init(struct net *net, bool isv6);
void rds_tcp_listen_stop(struct socket *sock, struct work_struct *acceptor); void rds_tcp_listen_stop(struct socket *sock, struct work_struct *acceptor);
void rds_tcp_listen_data_ready(struct sock *sk); void rds_tcp_listen_data_ready(struct sock *sk);
int rds_tcp_accept_one(struct socket *sock); int rds_tcp_accept_one(struct socket *sock);
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -66,7 +66,8 @@ void rds_tcp_state_change(struct sock *sk) ...@@ -66,7 +66,8 @@ void rds_tcp_state_change(struct sock *sk)
* RDS connection as RDS_CONN_UP until the reconnect, * RDS connection as RDS_CONN_UP until the reconnect,
* to avoid RDS datagram loss. * to avoid RDS datagram loss.
*/ */
if (!IS_CANONICAL(cp->cp_conn->c_laddr, cp->cp_conn->c_faddr) && if (rds_addr_cmp(&cp->cp_conn->c_laddr,
&cp->cp_conn->c_faddr) >= 0 &&
rds_conn_path_transition(cp, RDS_CONN_CONNECTING, rds_conn_path_transition(cp, RDS_CONN_CONNECTING,
RDS_CONN_ERROR)) { RDS_CONN_ERROR)) {
rds_conn_path_drop(cp, false); rds_conn_path_drop(cp, false);
...@@ -88,7 +89,11 @@ void rds_tcp_state_change(struct sock *sk) ...@@ -88,7 +89,11 @@ void rds_tcp_state_change(struct sock *sk)
int rds_tcp_conn_path_connect(struct rds_conn_path *cp) int rds_tcp_conn_path_connect(struct rds_conn_path *cp)
{ {
struct socket *sock = NULL; struct socket *sock = NULL;
struct sockaddr_in src, dest; struct sockaddr_in6 sin6;
struct sockaddr_in sin;
struct sockaddr *addr;
int addrlen;
bool isv6;
int ret; int ret;
struct rds_connection *conn = cp->cp_conn; struct rds_connection *conn = cp->cp_conn;
struct rds_tcp_connection *tc = cp->cp_transport_data; struct rds_tcp_connection *tc = cp->cp_transport_data;
...@@ -105,37 +110,68 @@ int rds_tcp_conn_path_connect(struct rds_conn_path *cp) ...@@ -105,37 +110,68 @@ int rds_tcp_conn_path_connect(struct rds_conn_path *cp)
mutex_unlock(&tc->t_conn_path_lock); mutex_unlock(&tc->t_conn_path_lock);
return 0; return 0;
} }
if (ipv6_addr_v4mapped(&conn->c_laddr)) {
ret = sock_create_kern(rds_conn_net(conn), PF_INET, ret = sock_create_kern(rds_conn_net(conn), PF_INET,
SOCK_STREAM, IPPROTO_TCP, &sock); SOCK_STREAM, IPPROTO_TCP, &sock);
isv6 = false;
} else {
ret = sock_create_kern(rds_conn_net(conn), PF_INET6,
SOCK_STREAM, IPPROTO_TCP, &sock);
isv6 = true;
}
if (ret < 0) if (ret < 0)
goto out; goto out;
rds_tcp_tune(sock); rds_tcp_tune(sock);
src.sin_family = AF_INET; if (isv6) {
src.sin_addr.s_addr = (__force u32)conn->c_laddr; sin6.sin6_family = AF_INET6;
src.sin_port = (__force u16)htons(0); sin6.sin6_addr = conn->c_laddr;
sin6.sin6_port = 0;
sin6.sin6_flowinfo = 0;
sin6.sin6_scope_id = conn->c_dev_if;
addr = (struct sockaddr *)&sin6;
addrlen = sizeof(sin6);
} else {
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = conn->c_laddr.s6_addr32[3];
sin.sin_port = 0;
addr = (struct sockaddr *)&sin;
addrlen = sizeof(sin);
}
ret = sock->ops->bind(sock, (struct sockaddr *)&src, sizeof(src)); ret = sock->ops->bind(sock, addr, addrlen);
if (ret) { if (ret) {
rdsdebug("bind failed with %d at address %pI4\n", rdsdebug("bind failed with %d at address %pI6c\n",
ret, &conn->c_laddr); ret, &conn->c_laddr);
goto out; goto out;
} }
dest.sin_family = AF_INET; if (isv6) {
dest.sin_addr.s_addr = (__force u32)conn->c_faddr; sin6.sin6_family = AF_INET6;
dest.sin_port = (__force u16)htons(RDS_TCP_PORT); sin6.sin6_addr = conn->c_faddr;
sin6.sin6_port = htons(RDS_TCP_PORT);
sin6.sin6_flowinfo = 0;
sin6.sin6_scope_id = conn->c_dev_if;
addr = (struct sockaddr *)&sin6;
addrlen = sizeof(sin6);
} else {
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = conn->c_faddr.s6_addr32[3];
sin.sin_port = htons(RDS_TCP_PORT);
addr = (struct sockaddr *)&sin;
addrlen = sizeof(sin);
}
/* /*
* once we call connect() we can start getting callbacks and they * once we call connect() we can start getting callbacks and they
* own the socket * own the socket
*/ */
rds_tcp_set_callbacks(sock, cp); rds_tcp_set_callbacks(sock, cp);
ret = sock->ops->connect(sock, (struct sockaddr *)&dest, sizeof(dest), ret = sock->ops->connect(sock, addr, addrlen, O_NONBLOCK);
O_NONBLOCK);
rdsdebug("connect to address %pI4 returned %d\n", &conn->c_faddr, ret); rdsdebug("connect to address %pI6c returned %d\n", &conn->c_faddr, ret);
if (ret == -EINPROGRESS) if (ret == -EINPROGRESS)
ret = 0; ret = 0;
if (ret == 0) { if (ret == 0) {
......
/* /*
* Copyright (c) 2006, 2018 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -83,13 +83,12 @@ static ...@@ -83,13 +83,12 @@ static
struct rds_tcp_connection *rds_tcp_accept_one_path(struct rds_connection *conn) struct rds_tcp_connection *rds_tcp_accept_one_path(struct rds_connection *conn)
{ {
int i; int i;
bool peer_is_smaller = IS_CANONICAL(conn->c_faddr, conn->c_laddr);
int npaths = max_t(int, 1, conn->c_npaths); int npaths = max_t(int, 1, conn->c_npaths);
/* for mprds, all paths MUST be initiated by the peer /* for mprds, all paths MUST be initiated by the peer
* with the smaller address. * with the smaller address.
*/ */
if (!peer_is_smaller) { if (rds_addr_cmp(&conn->c_faddr, &conn->c_laddr) >= 0) {
/* Make sure we initiate at least one path if this /* Make sure we initiate at least one path if this
* has not already been done; rds_start_mprds() will * has not already been done; rds_start_mprds() will
* take care of additional paths, if necessary. * take care of additional paths, if necessary.
...@@ -132,6 +131,8 @@ int rds_tcp_accept_one(struct socket *sock) ...@@ -132,6 +131,8 @@ int rds_tcp_accept_one(struct socket *sock)
struct rds_tcp_connection *rs_tcp = NULL; struct rds_tcp_connection *rs_tcp = NULL;
int conn_state; int conn_state;
struct rds_conn_path *cp; struct rds_conn_path *cp;
struct in6_addr *my_addr, *peer_addr;
int dev_if;
if (!sock) /* module unload or netns delete in progress */ if (!sock) /* module unload or netns delete in progress */
return -ENETUNREACH; return -ENETUNREACH;
...@@ -164,13 +165,30 @@ int rds_tcp_accept_one(struct socket *sock) ...@@ -164,13 +165,30 @@ int rds_tcp_accept_one(struct socket *sock)
inet = inet_sk(new_sock->sk); inet = inet_sk(new_sock->sk);
rdsdebug("accepted tcp %pI4:%u -> %pI4:%u\n", my_addr = &new_sock->sk->sk_v6_rcv_saddr;
&inet->inet_saddr, ntohs(inet->inet_sport), peer_addr = &new_sock->sk->sk_v6_daddr;
&inet->inet_daddr, ntohs(inet->inet_dport)); rdsdebug("accepted tcp %pI6c:%u -> %pI6c:%u\n",
my_addr, ntohs(inet->inet_sport),
peer_addr, ntohs(inet->inet_dport));
/* sk_bound_dev_if is not set if the peer address is not link local
* address. In this case, it happens that mcast_oif is set. So
* just use it.
*/
if ((ipv6_addr_type(my_addr) & IPV6_ADDR_LINKLOCAL) &&
!(ipv6_addr_type(peer_addr) & IPV6_ADDR_LINKLOCAL)) {
struct ipv6_pinfo *inet6;
inet6 = inet6_sk(new_sock->sk);
dev_if = inet6->mcast_oif;
} else {
dev_if = new_sock->sk->sk_bound_dev_if;
}
conn = rds_conn_create(sock_net(sock->sk), conn = rds_conn_create(sock_net(sock->sk),
inet->inet_saddr, inet->inet_daddr, &new_sock->sk->sk_v6_rcv_saddr,
&rds_tcp_transport, GFP_KERNEL); &new_sock->sk->sk_v6_daddr,
&rds_tcp_transport, GFP_KERNEL, dev_if);
if (IS_ERR(conn)) { if (IS_ERR(conn)) {
ret = PTR_ERR(conn); ret = PTR_ERR(conn);
goto out; goto out;
...@@ -254,15 +272,22 @@ void rds_tcp_listen_data_ready(struct sock *sk) ...@@ -254,15 +272,22 @@ void rds_tcp_listen_data_ready(struct sock *sk)
ready(sk); ready(sk);
} }
struct socket *rds_tcp_listen_init(struct net *net) struct socket *rds_tcp_listen_init(struct net *net, bool isv6)
{ {
struct sockaddr_in sin;
struct socket *sock = NULL; struct socket *sock = NULL;
struct sockaddr_storage ss;
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin;
int addr_len;
int ret; int ret;
ret = sock_create_kern(net, PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); ret = sock_create_kern(net, isv6 ? PF_INET6 : PF_INET, SOCK_STREAM,
if (ret < 0) IPPROTO_TCP, &sock);
if (ret < 0) {
rdsdebug("could not create %s listener socket: %d\n",
isv6 ? "IPv6" : "IPv4", ret);
goto out; goto out;
}
sock->sk->sk_reuse = SK_CAN_REUSE; sock->sk->sk_reuse = SK_CAN_REUSE;
rds_tcp_nonagle(sock); rds_tcp_nonagle(sock);
...@@ -272,13 +297,28 @@ struct socket *rds_tcp_listen_init(struct net *net) ...@@ -272,13 +297,28 @@ struct socket *rds_tcp_listen_init(struct net *net)
sock->sk->sk_data_ready = rds_tcp_listen_data_ready; sock->sk->sk_data_ready = rds_tcp_listen_data_ready;
write_unlock_bh(&sock->sk->sk_callback_lock); write_unlock_bh(&sock->sk->sk_callback_lock);
sin.sin_family = PF_INET; if (isv6) {
sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY); sin6 = (struct sockaddr_in6 *)&ss;
sin.sin_port = (__force u16)htons(RDS_TCP_PORT); sin6->sin6_family = PF_INET6;
sin6->sin6_addr = in6addr_any;
sin6->sin6_port = (__force u16)htons(RDS_TCP_PORT);
sin6->sin6_scope_id = 0;
sin6->sin6_flowinfo = 0;
addr_len = sizeof(*sin6);
} else {
sin = (struct sockaddr_in *)&ss;
sin->sin_family = PF_INET;
sin->sin_addr.s_addr = INADDR_ANY;
sin->sin_port = (__force u16)htons(RDS_TCP_PORT);
addr_len = sizeof(*sin);
}
ret = sock->ops->bind(sock, (struct sockaddr *)&sin, sizeof(sin)); ret = sock->ops->bind(sock, (struct sockaddr *)&ss, addr_len);
if (ret < 0) if (ret < 0) {
rdsdebug("could not bind %s listener socket: %d\n",
isv6 ? "IPv6" : "IPv4", ret);
goto out; goto out;
}
ret = sock->ops->listen(sock, 64); ret = sock->ops->listen(sock, 64);
if (ret < 0) if (ret < 0)
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -179,7 +179,7 @@ static int rds_tcp_data_recv(read_descriptor_t *desc, struct sk_buff *skb, ...@@ -179,7 +179,7 @@ static int rds_tcp_data_recv(read_descriptor_t *desc, struct sk_buff *skb,
tc->t_tinc = tinc; tc->t_tinc = tinc;
rdsdebug("alloced tinc %p\n", tinc); rdsdebug("alloced tinc %p\n", tinc);
rds_inc_path_init(&tinc->ti_inc, cp, rds_inc_path_init(&tinc->ti_inc, cp,
cp->cp_conn->c_faddr); &cp->cp_conn->c_faddr);
tinc->ti_inc.i_rx_lat_trace[RDS_MSG_RX_HDR] = tinc->ti_inc.i_rx_lat_trace[RDS_MSG_RX_HDR] =
local_clock(); local_clock();
...@@ -239,8 +239,9 @@ static int rds_tcp_data_recv(read_descriptor_t *desc, struct sk_buff *skb, ...@@ -239,8 +239,9 @@ static int rds_tcp_data_recv(read_descriptor_t *desc, struct sk_buff *skb,
if (tinc->ti_inc.i_hdr.h_flags == RDS_FLAG_CONG_BITMAP) if (tinc->ti_inc.i_hdr.h_flags == RDS_FLAG_CONG_BITMAP)
rds_tcp_cong_recv(conn, tinc); rds_tcp_cong_recv(conn, tinc);
else else
rds_recv_incoming(conn, conn->c_faddr, rds_recv_incoming(conn, &conn->c_faddr,
conn->c_laddr, &tinc->ti_inc, &conn->c_laddr,
&tinc->ti_inc,
arg->gfp); arg->gfp);
tc->t_tinc_hdr_rem = sizeof(struct rds_header); tc->t_tinc_hdr_rem = sizeof(struct rds_header);
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -153,7 +153,7 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm, ...@@ -153,7 +153,7 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm,
* an incoming RST. * an incoming RST.
*/ */
if (rds_conn_path_up(cp)) { if (rds_conn_path_up(cp)) {
pr_warn("RDS/tcp: send to %pI4 on cp [%d]" pr_warn("RDS/tcp: send to %pI6c on cp [%d]"
"returned %d, " "returned %d, "
"disconnecting and reconnecting\n", "disconnecting and reconnecting\n",
&conn->c_faddr, cp->cp_index, ret); &conn->c_faddr, cp->cp_index, ret);
......
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -82,7 +82,7 @@ void rds_connect_path_complete(struct rds_conn_path *cp, int curr) ...@@ -82,7 +82,7 @@ void rds_connect_path_complete(struct rds_conn_path *cp, int curr)
return; return;
} }
rdsdebug("conn %p for %pI4 to %pI4 complete\n", rdsdebug("conn %p for %pI6c to %pI6c complete\n",
cp->cp_conn, &cp->cp_conn->c_laddr, &cp->cp_conn->c_faddr); cp->cp_conn, &cp->cp_conn->c_laddr, &cp->cp_conn->c_faddr);
cp->cp_reconnect_jiffies = 0; cp->cp_reconnect_jiffies = 0;
...@@ -125,13 +125,13 @@ void rds_queue_reconnect(struct rds_conn_path *cp) ...@@ -125,13 +125,13 @@ void rds_queue_reconnect(struct rds_conn_path *cp)
unsigned long rand; unsigned long rand;
struct rds_connection *conn = cp->cp_conn; struct rds_connection *conn = cp->cp_conn;
rdsdebug("conn %p for %pI4 to %pI4 reconnect jiffies %lu\n", rdsdebug("conn %p for %pI6c to %pI6c reconnect jiffies %lu\n",
conn, &conn->c_laddr, &conn->c_faddr, conn, &conn->c_laddr, &conn->c_faddr,
cp->cp_reconnect_jiffies); cp->cp_reconnect_jiffies);
/* let peer with smaller addr initiate reconnect, to avoid duels */ /* let peer with smaller addr initiate reconnect, to avoid duels */
if (conn->c_trans->t_type == RDS_TRANS_TCP && if (conn->c_trans->t_type == RDS_TRANS_TCP &&
!IS_CANONICAL(conn->c_laddr, conn->c_faddr)) rds_addr_cmp(&conn->c_laddr, &conn->c_faddr) >= 0)
return; return;
set_bit(RDS_RECONNECT_PENDING, &cp->cp_flags); set_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
...@@ -145,7 +145,7 @@ void rds_queue_reconnect(struct rds_conn_path *cp) ...@@ -145,7 +145,7 @@ void rds_queue_reconnect(struct rds_conn_path *cp)
} }
get_random_bytes(&rand, sizeof(rand)); get_random_bytes(&rand, sizeof(rand));
rdsdebug("%lu delay %lu ceil conn %p for %pI4 -> %pI4\n", rdsdebug("%lu delay %lu ceil conn %p for %pI6c -> %pI6c\n",
rand % cp->cp_reconnect_jiffies, cp->cp_reconnect_jiffies, rand % cp->cp_reconnect_jiffies, cp->cp_reconnect_jiffies,
conn, &conn->c_laddr, &conn->c_faddr); conn, &conn->c_laddr, &conn->c_faddr);
rcu_read_lock(); rcu_read_lock();
...@@ -167,13 +167,13 @@ void rds_connect_worker(struct work_struct *work) ...@@ -167,13 +167,13 @@ void rds_connect_worker(struct work_struct *work)
int ret; int ret;
if (cp->cp_index > 0 && if (cp->cp_index > 0 &&
!IS_CANONICAL(cp->cp_conn->c_laddr, cp->cp_conn->c_faddr)) rds_addr_cmp(&cp->cp_conn->c_laddr, &cp->cp_conn->c_faddr) >= 0)
return; return;
clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags); clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
ret = rds_conn_path_transition(cp, RDS_CONN_DOWN, RDS_CONN_CONNECTING); ret = rds_conn_path_transition(cp, RDS_CONN_DOWN, RDS_CONN_CONNECTING);
if (ret) { if (ret) {
ret = conn->c_trans->conn_path_connect(cp); ret = conn->c_trans->conn_path_connect(cp);
rdsdebug("conn %p for %pI4 to %pI4 dispatched, ret %d\n", rdsdebug("conn %p for %pI6c to %pI6c dispatched, ret %d\n",
conn, &conn->c_laddr, &conn->c_faddr, ret); conn, &conn->c_laddr, &conn->c_faddr, ret);
if (ret) { if (ret) {
...@@ -259,3 +259,50 @@ int rds_threads_init(void) ...@@ -259,3 +259,50 @@ int rds_threads_init(void)
return 0; return 0;
} }
/* Compare two IPv6 addresses. Return 0 if the two addresses are equal.
* Return 1 if the first is greater. Return -1 if the second is greater.
*/
int rds_addr_cmp(const struct in6_addr *addr1,
const struct in6_addr *addr2)
{
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
const __be64 *a1, *a2;
u64 x, y;
a1 = (__be64 *)addr1;
a2 = (__be64 *)addr2;
if (*a1 != *a2) {
if (be64_to_cpu(*a1) < be64_to_cpu(*a2))
return -1;
else
return 1;
} else {
x = be64_to_cpu(*++a1);
y = be64_to_cpu(*++a2);
if (x < y)
return -1;
else if (x > y)
return 1;
else
return 0;
}
#else
u32 a, b;
int i;
for (i = 0; i < 4; i++) {
if (addr1->s6_addr32[i] != addr2->s6_addr32[i]) {
a = ntohl(addr1->s6_addr32[i]);
b = ntohl(addr2->s6_addr32[i]);
if (a < b)
return -1;
else if (a > b)
return 1;
}
}
return 0;
#endif
}
EXPORT_SYMBOL_GPL(rds_addr_cmp);
/* /*
* Copyright (c) 2006 Oracle. All rights reserved. * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU * licenses. You may choose to be licensed under the terms of the GNU
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/ipv6.h>
#include "rds.h" #include "rds.h"
#include "loop.h" #include "loop.h"
...@@ -75,20 +76,26 @@ void rds_trans_put(struct rds_transport *trans) ...@@ -75,20 +76,26 @@ void rds_trans_put(struct rds_transport *trans)
module_put(trans->t_owner); module_put(trans->t_owner);
} }
struct rds_transport *rds_trans_get_preferred(struct net *net, __be32 addr) struct rds_transport *rds_trans_get_preferred(struct net *net,
const struct in6_addr *addr,
__u32 scope_id)
{ {
struct rds_transport *ret = NULL; struct rds_transport *ret = NULL;
struct rds_transport *trans; struct rds_transport *trans;
unsigned int i; unsigned int i;
if (IN_LOOPBACK(ntohl(addr))) if (ipv6_addr_v4mapped(addr)) {
if (*(u_int8_t *)&addr->s6_addr32[3] == IN_LOOPBACKNET)
return &rds_loop_transport; return &rds_loop_transport;
} else if (ipv6_addr_loopback(addr)) {
return &rds_loop_transport;
}
down_read(&rds_trans_sem); down_read(&rds_trans_sem);
for (i = 0; i < RDS_TRANS_COUNT; i++) { for (i = 0; i < RDS_TRANS_COUNT; i++) {
trans = transports[i]; trans = transports[i];
if (trans && (trans->laddr_check(net, addr) == 0) && if (trans && (trans->laddr_check(net, addr, scope_id) == 0) &&
(!trans->t_owner || try_module_get(trans->t_owner))) { (!trans->t_owner || try_module_get(trans->t_owner))) {
ret = trans; ret = trans;
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment