Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,27 @@
# SPDX-License-Identifier: GPL-2.0-or-later
config INFINIBAND_RTRS
tristate
depends on INFINIBAND_ADDR_TRANS
config INFINIBAND_RTRS_CLIENT
tristate "RTRS client module"
depends on INFINIBAND_ADDR_TRANS
select INFINIBAND_RTRS
help
RDMA transport client module.
RDMA Transport (RTRS) client implements a reliable transport layer
and also multipathing functionality and that it is intended to be
the base layer for a block storage initiator over RDMA.
config INFINIBAND_RTRS_SERVER
tristate "RTRS server module"
depends on INFINIBAND_ADDR_TRANS
select INFINIBAND_RTRS
help
RDMA transport server module.
RDMA Transport (RTRS) server module processing connection and IO
requests received from the RTRS client module, it will pass the
IO requests to its user eg. RNBD_server.
@@ -0,0 +1,21 @@
# SPDX-License-Identifier: GPL-2.0-or-later
CFLAGS_rtrs-clt-trace.o = -I$(src)
rtrs-client-y := rtrs-clt.o \
rtrs-clt-stats.o \
rtrs-clt-sysfs.o \
rtrs-clt-trace.o
CFLAGS_rtrs-srv-trace.o = -I$(src)
rtrs-server-y := rtrs-srv.o \
rtrs-srv-stats.o \
rtrs-srv-sysfs.o \
rtrs-srv-trace.o
rtrs-core-y := rtrs.o
obj-$(CONFIG_INFINIBAND_RTRS) += rtrs-core.o
obj-$(CONFIG_INFINIBAND_RTRS_CLIENT) += rtrs-client.o
obj-$(CONFIG_INFINIBAND_RTRS_SERVER) += rtrs-server.o
@@ -0,0 +1,213 @@
****************************
RDMA Transport (RTRS)
****************************
RTRS (RDMA Transport) is a reliable high speed transport library
which provides support to establish optimal number of connections
between client and server machines using RDMA (InfiniBand, RoCE, iWarp)
transport. It is optimized to transfer (read/write) IO blocks.
In its core interface it follows the BIO semantics of providing the
possibility to either write data from an sg list to the remote side
or to request ("read") data transfer from the remote side into a given
sg list.
RTRS provides I/O fail-over and load-balancing capabilities by using
multipath I/O (see "add_path" and "mp_policy" configuration entries in
Documentation/ABI/testing/sysfs-class-rtrs-client).
RTRS is used by the RNBD (RDMA Network Block Device) modules.
==================
Transport protocol
==================
Overview
--------
An established connection between a client and a server is called rtrs
session. A session is associated with a set of memory chunks reserved on the
server side for a given client for rdma transfer. A session
consists of multiple paths, each representing a separate physical link
between client and server. Those are used for load balancing and failover.
Each path consists of as many connections (QPs) as there are cpus on
the client.
When processing an incoming write or read request, rtrs client uses memory
chunks reserved for him on the server side. Their number, size and addresses
need to be exchanged between client and server during the connection
establishment phase. Apart from the memory related information client needs to
inform the server about the session name and identify each path and connection
individually.
On an established session client sends to server write or read messages.
Server uses immediate field to tell the client which request is being
acknowledged and for errno. Client uses immediate field to tell the server
which of the memory chunks has been accessed and at which offset the message
can be found.
Module parameter always_invalidate is introduced for the security problem
discussed in LPC RDMA MC 2019. When always_invalidate=Y, on the server side we
invalidate each rdma buffer before we hand it over to RNBD server and
then pass it to the block layer. A new rkey is generated and registered for the
buffer after it returns back from the block layer and RNBD server.
The new rkey is sent back to the client along with the IO result.
The procedure is the default behaviour of the driver. This invalidation and
registration on each IO causes performance drop of up to 20%. A user of the
driver may choose to load the modules with this mechanism switched off
(always_invalidate=N), if he understands and can take the risk of a malicious
client being able to corrupt memory of a server it is connected to. This might
be a reasonable option in a scenario where all the clients and all the servers
are located within a secure datacenter.
Connection establishment
------------------------
1. Client starts establishing connections belonging to a path of a session one
by one via attaching RTRS_MSG_CON_REQ messages to the rdma_connect requests.
Those include uuid of the session and uuid of the path to be
established. They are used by the server to find a persisting session/path or
to create a new one when necessary. The message also contains the protocol
version and magic for compatibility, total number of connections per session
(as many as cpus on the client), the id of the current connection and
the reconnect counter, which is used to resolve the situations where
client is trying to reconnect a path, while server is still destroying the old
one.
2. Server accepts the connection requests one by one and attaches
RTRS_MSG_CONN_RSP messages to the rdma_accept. Apart from magic and
protocol version, the messages include error code, queue depth supported by
the server (number of memory chunks which are going to be allocated for that
session) and the maximum size of one io, RTRS_MSG_NEW_RKEY_F flags is set
when always_invalidate=Y.
3. After all connections of a path are established client sends to server the
RTRS_MSG_INFO_REQ message, containing the name of the session. This message
requests the address information from the server.
4. Server replies to the session info request message with RTRS_MSG_INFO_RSP,
which contains the addresses and keys of the RDMA buffers allocated for that
session.
5. Session becomes connected after all paths to be established are connected
(i.e. steps 1-4 finished for all paths requested for a session)
6. Server and client exchange periodically heartbeat messages (empty rdma
messages with an immediate field) which are used to detect a crash on remote
side or network outage in an absence of IO.
7. On any RDMA related error or in the case of a heartbeat timeout, the
corresponding path is disconnected, all the inflight IO are failed over to a
healthy path, if any, and the reconnect mechanism is triggered.
CLT SRV
*for each connection belonging to a path and for each path:
RTRS_MSG_CON_REQ ------------------->
<------------------- RTRS_MSG_CON_RSP
...
*after all connections are established:
RTRS_MSG_INFO_REQ ------------------->
<------------------- RTRS_MSG_INFO_RSP
*heartbeat is started from both sides:
-------------------> [RTRS_HB_MSG_IMM]
[RTRS_HB_MSG_ACK] <-------------------
[RTRS_HB_MSG_IMM] <-------------------
-------------------> [RTRS_HB_MSG_ACK]
IO path
-------
* Write (always_invalidate=N) *
1. When processing a write request client selects one of the memory chunks
on the server side and rdma writes there the user data, user header and the
RTRS_MSG_RDMA_WRITE message. Apart from the type (write), the message only
contains size of the user header. The client tells the server which chunk has
been accessed and at what offset the RTRS_MSG_RDMA_WRITE can be found by
using the IMM field.
2. When confirming a write request server sends an "empty" rdma message with
an immediate field. The 32 bit field is used to specify the outstanding
inflight IO and for the error code.
CLT SRV
usr_data + usr_hdr + rtrs_msg_rdma_write -----------------> [RTRS_IO_REQ_IMM]
[RTRS_IO_RSP_IMM] <----------------- (id + errno)
* Write (always_invalidate=Y) *
1. When processing a write request client selects one of the memory chunks
on the server side and rdma writes there the user data, user header and the
RTRS_MSG_RDMA_WRITE message. Apart from the type (write), the message only
contains size of the user header. The client tells the server which chunk has
been accessed and at what offset the RTRS_MSG_RDMA_WRITE can be found by
using the IMM field, Server invalidate rkey associated to the memory chunks
first, when it finishes, pass the IO to RNBD server module.
2. When confirming a write request server sends an "empty" rdma message with
an immediate field. The 32 bit field is used to specify the outstanding
inflight IO and for the error code. The new rkey is sent back using
SEND_WITH_IMM WR, client When it recived new rkey message, it validates
the message and finished IO after update rkey for the rbuffer, then post
back the recv buffer for later use.
CLT SRV
usr_data + usr_hdr + rtrs_msg_rdma_write -----------------> [RTRS_IO_REQ_IMM]
[RTRS_MSG_RKEY_RSP] <----------------- (RTRS_MSG_RKEY_RSP)
[RTRS_IO_RSP_IMM] <----------------- (id + errno)
* Read (always_invalidate=N)*
1. When processing a read request client selects one of the memory chunks
on the server side and rdma writes there the user header and the
RTRS_MSG_RDMA_READ message. This message contains the type (read), size of
the user header, flags (specifying if memory invalidation is necessary) and the
list of addresses along with keys for the data to be read into.
2. When confirming a read request server transfers the requested data first,
attaches an invalidation message if requested and finally an "empty" rdma
message with an immediate field. The 32 bit field is used to specify the
outstanding inflight IO and the error code.
CLT SRV
usr_hdr + rtrs_msg_rdma_read --------------> [RTRS_IO_REQ_IMM]
[RTRS_IO_RSP_IMM] <-------------- usr_data + (id + errno)
or in case client requested invalidation:
[RTRS_IO_RSP_IMM_W_INV] <-------------- usr_data + (INV) + (id + errno)
* Read (always_invalidate=Y)*
1. When processing a read request client selects one of the memory chunks
on the server side and rdma writes there the user header and the
RTRS_MSG_RDMA_READ message. This message contains the type (read), size of
the user header, flags (specifying if memory invalidation is necessary) and the
list of addresses along with keys for the data to be read into.
Server invalidate rkey associated to the memory chunks first, when it finishes,
passes the IO to RNBD server module.
2. When confirming a read request server transfers the requested data first,
attaches an invalidation message if requested and finally an "empty" rdma
message with an immediate field. The 32 bit field is used to specify the
outstanding inflight IO and the error code. The new rkey is sent back using
SEND_WITH_IMM WR, client When it recived new rkey message, it validates
the message and finished IO after update rkey for the rbuffer, then post
back the recv buffer for later use.
CLT SRV
usr_hdr + rtrs_msg_rdma_read --------------> [RTRS_IO_REQ_IMM]
[RTRS_IO_RSP_IMM] <-------------- usr_data + (id + errno)
[RTRS_MSG_RKEY_RSP] <----------------- (RTRS_MSG_RKEY_RSP)
or in case client requested invalidation:
[RTRS_IO_RSP_IMM_W_INV] <-------------- usr_data + (INV) + (id + errno)
=========================================
Contributors List(in alphabetical order)
=========================================
Danil Kipnis <danil.kipnis@profitbricks.com>
Fabian Holler <mail@fholler.de>
Guoqing Jiang <guoqing.jiang@cloud.ionos.com>
Jack Wang <jinpu.wang@profitbricks.com>
Kleber Souza <kleber.souza@profitbricks.com>
Lutz Pogrell <lutz.pogrell@cloud.ionos.com>
Milind Dumbare <Milind.dumbare@gmail.com>
Roman Penyaev <roman.penyaev@profitbricks.com>
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
#include "rtrs-clt.h"
void rtrs_clt_update_wc_stats(struct rtrs_clt_con *con)
{
struct rtrs_clt_path *clt_path = to_clt_path(con->c.path);
struct rtrs_clt_stats *stats = clt_path->stats;
struct rtrs_clt_stats_pcpu *s;
int cpu;
cpu = raw_smp_processor_id();
s = get_cpu_ptr(stats->pcpu_stats);
if (con->cpu != cpu) {
s->cpu_migr.to++;
/* Careful here, override s pointer */
s = per_cpu_ptr(stats->pcpu_stats, con->cpu);
atomic_inc(&s->cpu_migr.from);
}
put_cpu_ptr(stats->pcpu_stats);
}
void rtrs_clt_inc_failover_cnt(struct rtrs_clt_stats *stats)
{
this_cpu_inc(stats->pcpu_stats->rdma.failover_cnt);
}
int rtrs_clt_stats_migration_from_cnt_to_str(struct rtrs_clt_stats *stats, char *buf)
{
struct rtrs_clt_stats_pcpu *s;
size_t used;
int cpu;
used = 0;
for_each_possible_cpu(cpu) {
s = per_cpu_ptr(stats->pcpu_stats, cpu);
used += sysfs_emit_at(buf, used, "%d ",
atomic_read(&s->cpu_migr.from));
}
used += sysfs_emit_at(buf, used, "\n");
return used;
}
int rtrs_clt_stats_migration_to_cnt_to_str(struct rtrs_clt_stats *stats, char *buf)
{
struct rtrs_clt_stats_pcpu *s;
size_t used;
int cpu;
used = 0;
for_each_possible_cpu(cpu) {
s = per_cpu_ptr(stats->pcpu_stats, cpu);
used += sysfs_emit_at(buf, used, "%d ", s->cpu_migr.to);
}
used += sysfs_emit_at(buf, used, "\n");
return used;
}
int rtrs_clt_stats_reconnects_to_str(struct rtrs_clt_stats *stats, char *buf)
{
return sysfs_emit(buf, "%d %d\n", stats->reconnects.successful_cnt,
stats->reconnects.fail_cnt);
}
ssize_t rtrs_clt_stats_rdma_to_str(struct rtrs_clt_stats *stats, char *page)
{
struct rtrs_clt_stats_rdma sum;
struct rtrs_clt_stats_rdma *r;
int cpu;
memset(&sum, 0, sizeof(sum));
for_each_possible_cpu(cpu) {
r = &per_cpu_ptr(stats->pcpu_stats, cpu)->rdma;
sum.dir[READ].cnt += r->dir[READ].cnt;
sum.dir[READ].size_total += r->dir[READ].size_total;
sum.dir[WRITE].cnt += r->dir[WRITE].cnt;
sum.dir[WRITE].size_total += r->dir[WRITE].size_total;
sum.failover_cnt += r->failover_cnt;
}
return sysfs_emit(page, "%llu %llu %llu %llu %u %llu\n",
sum.dir[READ].cnt, sum.dir[READ].size_total,
sum.dir[WRITE].cnt, sum.dir[WRITE].size_total,
atomic_read(&stats->inflight), sum.failover_cnt);
}
ssize_t rtrs_clt_reset_all_help(struct rtrs_clt_stats *s, char *page)
{
return sysfs_emit(page, "echo 1 to reset all statistics\n");
}
int rtrs_clt_reset_rdma_stats(struct rtrs_clt_stats *stats, bool enable)
{
struct rtrs_clt_stats_pcpu *s;
int cpu;
if (!enable)
return -EINVAL;
for_each_possible_cpu(cpu) {
s = per_cpu_ptr(stats->pcpu_stats, cpu);
memset(&s->rdma, 0, sizeof(s->rdma));
}
return 0;
}
int rtrs_clt_reset_cpu_migr_stats(struct rtrs_clt_stats *stats, bool enable)
{
struct rtrs_clt_stats_pcpu *s;
int cpu;
if (!enable)
return -EINVAL;
for_each_possible_cpu(cpu) {
s = per_cpu_ptr(stats->pcpu_stats, cpu);
memset(&s->cpu_migr, 0, sizeof(s->cpu_migr));
}
return 0;
}
int rtrs_clt_reset_reconnects_stat(struct rtrs_clt_stats *stats, bool enable)
{
if (!enable)
return -EINVAL;
memset(&stats->reconnects, 0, sizeof(stats->reconnects));
return 0;
}
int rtrs_clt_reset_all_stats(struct rtrs_clt_stats *s, bool enable)
{
if (enable) {
rtrs_clt_reset_rdma_stats(s, enable);
rtrs_clt_reset_cpu_migr_stats(s, enable);
rtrs_clt_reset_reconnects_stat(s, enable);
atomic_set(&s->inflight, 0);
return 0;
}
return -EINVAL;
}
static inline void rtrs_clt_update_rdma_stats(struct rtrs_clt_stats *stats,
size_t size, int d)
{
this_cpu_inc(stats->pcpu_stats->rdma.dir[d].cnt);
this_cpu_add(stats->pcpu_stats->rdma.dir[d].size_total, size);
}
void rtrs_clt_update_all_stats(struct rtrs_clt_io_req *req, int dir)
{
struct rtrs_clt_con *con = req->con;
struct rtrs_clt_path *clt_path = to_clt_path(con->c.path);
struct rtrs_clt_stats *stats = clt_path->stats;
unsigned int len;
len = req->usr_len + req->data_len;
rtrs_clt_update_rdma_stats(stats, len, dir);
if (req->mp_policy == MP_POLICY_MIN_INFLIGHT)
atomic_inc(&stats->inflight);
}
int rtrs_clt_init_stats(struct rtrs_clt_stats *stats)
{
stats->pcpu_stats = alloc_percpu(typeof(*stats->pcpu_stats));
if (!stats->pcpu_stats)
return -ENOMEM;
/*
* successful_cnt will be set to 0 after session
* is established for the first time
*/
stats->reconnects.successful_cnt = -1;
return 0;
}
@@ -0,0 +1,512 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
#include "rtrs-pri.h"
#include "rtrs-clt.h"
#include "rtrs-log.h"
#define MIN_MAX_RECONN_ATT -1
#define MAX_MAX_RECONN_ATT 9999
static void rtrs_clt_path_release(struct kobject *kobj)
{
struct rtrs_clt_path *clt_path;
clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
free_path(clt_path);
}
static struct kobj_type ktype_sess = {
.sysfs_ops = &kobj_sysfs_ops,
.release = rtrs_clt_path_release
};
static void rtrs_clt_path_stats_release(struct kobject *kobj)
{
struct rtrs_clt_stats *stats;
stats = container_of(kobj, struct rtrs_clt_stats, kobj_stats);
free_percpu(stats->pcpu_stats);
}
static struct kobj_type ktype_stats = {
.sysfs_ops = &kobj_sysfs_ops,
.release = rtrs_clt_path_stats_release,
};
static ssize_t max_reconnect_attempts_show(struct device *dev,
struct device_attribute *attr,
char *page)
{
struct rtrs_clt_sess *clt = container_of(dev, struct rtrs_clt_sess,
dev);
return sysfs_emit(page, "%d\n",
rtrs_clt_get_max_reconnect_attempts(clt));
}
static ssize_t max_reconnect_attempts_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
int value;
int ret;
struct rtrs_clt_sess *clt = container_of(dev, struct rtrs_clt_sess,
dev);
ret = kstrtoint(buf, 10, &value);
if (ret) {
rtrs_err(clt, "%s: failed to convert string '%s' to int\n",
attr->attr.name, buf);
return ret;
}
if (value > MAX_MAX_RECONN_ATT ||
value < MIN_MAX_RECONN_ATT) {
rtrs_err(clt,
"%s: invalid range (provided: '%s', accepted: min: %d, max: %d)\n",
attr->attr.name, buf, MIN_MAX_RECONN_ATT,
MAX_MAX_RECONN_ATT);
return -EINVAL;
}
rtrs_clt_set_max_reconnect_attempts(clt, value);
return count;
}
static DEVICE_ATTR_RW(max_reconnect_attempts);
static ssize_t mpath_policy_show(struct device *dev,
struct device_attribute *attr,
char *page)
{
struct rtrs_clt_sess *clt;
clt = container_of(dev, struct rtrs_clt_sess, dev);
switch (clt->mp_policy) {
case MP_POLICY_RR:
return sysfs_emit(page, "round-robin (RR: %d)\n",
clt->mp_policy);
case MP_POLICY_MIN_INFLIGHT:
return sysfs_emit(page, "min-inflight (MI: %d)\n",
clt->mp_policy);
case MP_POLICY_MIN_LATENCY:
return sysfs_emit(page, "min-latency (ML: %d)\n",
clt->mp_policy);
default:
return sysfs_emit(page, "Unknown (%d)\n", clt->mp_policy);
}
}
static ssize_t mpath_policy_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct rtrs_clt_sess *clt;
int value;
int ret;
size_t len = 0;
clt = container_of(dev, struct rtrs_clt_sess, dev);
ret = kstrtoint(buf, 10, &value);
if (!ret && (value == MP_POLICY_RR ||
value == MP_POLICY_MIN_INFLIGHT ||
value == MP_POLICY_MIN_LATENCY)) {
clt->mp_policy = value;
return count;
}
/* distinguish "mi" and "min-latency" with length */
len = strnlen(buf, NAME_MAX);
if (len && buf[len - 1] == '\n')
len--;
if (!strncasecmp(buf, "round-robin", 11) ||
(len == 2 && !strncasecmp(buf, "rr", 2)))
clt->mp_policy = MP_POLICY_RR;
else if (!strncasecmp(buf, "min-inflight", 12) ||
(len == 2 && !strncasecmp(buf, "mi", 2)))
clt->mp_policy = MP_POLICY_MIN_INFLIGHT;
else if (!strncasecmp(buf, "min-latency", 11) ||
(len == 2 && !strncasecmp(buf, "ml", 2)))
clt->mp_policy = MP_POLICY_MIN_LATENCY;
else
return -EINVAL;
return count;
}
static DEVICE_ATTR_RW(mpath_policy);
static ssize_t add_path_show(struct device *dev,
struct device_attribute *attr, char *page)
{
return sysfs_emit(page,
"Usage: echo [<source addr>@]<destination addr> > %s\n\n*addr ::= [ ip:<ipv4|ipv6> | gid:<gid> ]\n",
attr->attr.name);
}
static ssize_t add_path_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct sockaddr_storage srcaddr, dstaddr;
struct rtrs_addr addr = {
.src = &srcaddr,
.dst = &dstaddr
};
struct rtrs_clt_sess *clt;
const char *nl;
size_t len;
int err;
clt = container_of(dev, struct rtrs_clt_sess, dev);
nl = strchr(buf, '\n');
if (nl)
len = nl - buf;
else
len = count;
err = rtrs_addr_to_sockaddr(buf, len, clt->port, &addr);
if (err)
return -EINVAL;
err = rtrs_clt_create_path_from_sysfs(clt, &addr);
if (err)
return err;
return count;
}
static DEVICE_ATTR_RW(add_path);
static ssize_t rtrs_clt_state_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
struct rtrs_clt_path *clt_path;
clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
if (clt_path->state == RTRS_CLT_CONNECTED)
return sysfs_emit(page, "connected\n");
return sysfs_emit(page, "disconnected\n");
}
static struct kobj_attribute rtrs_clt_state_attr =
__ATTR(state, 0444, rtrs_clt_state_show, NULL);
static ssize_t rtrs_clt_reconnect_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name);
}
static ssize_t rtrs_clt_reconnect_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct rtrs_clt_path *clt_path;
int ret;
clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
if (!sysfs_streq(buf, "1")) {
rtrs_err(clt_path->clt, "%s: unknown value: '%s'\n",
attr->attr.name, buf);
return -EINVAL;
}
ret = rtrs_clt_reconnect_from_sysfs(clt_path);
if (ret)
return ret;
return count;
}
static struct kobj_attribute rtrs_clt_reconnect_attr =
__ATTR(reconnect, 0644, rtrs_clt_reconnect_show,
rtrs_clt_reconnect_store);
static ssize_t rtrs_clt_disconnect_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name);
}
static ssize_t rtrs_clt_disconnect_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct rtrs_clt_path *clt_path;
clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
if (!sysfs_streq(buf, "1")) {
rtrs_err(clt_path->clt, "%s: unknown value: '%s'\n",
attr->attr.name, buf);
return -EINVAL;
}
rtrs_clt_close_conns(clt_path, true);
return count;
}
static struct kobj_attribute rtrs_clt_disconnect_attr =
__ATTR(disconnect, 0644, rtrs_clt_disconnect_show,
rtrs_clt_disconnect_store);
static ssize_t rtrs_clt_remove_path_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name);
}
static ssize_t rtrs_clt_remove_path_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct rtrs_clt_path *clt_path;
int ret;
clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
if (!sysfs_streq(buf, "1")) {
rtrs_err(clt_path->clt, "%s: unknown value: '%s'\n",
attr->attr.name, buf);
return -EINVAL;
}
ret = rtrs_clt_remove_path_from_sysfs(clt_path, &attr->attr);
if (ret)
return ret;
return count;
}
static struct kobj_attribute rtrs_clt_remove_path_attr =
__ATTR(remove_path, 0644, rtrs_clt_remove_path_show,
rtrs_clt_remove_path_store);
STAT_ATTR(struct rtrs_clt_stats, cpu_migration_from,
rtrs_clt_stats_migration_from_cnt_to_str,
rtrs_clt_reset_cpu_migr_stats);
STAT_ATTR(struct rtrs_clt_stats, cpu_migration_to,
rtrs_clt_stats_migration_to_cnt_to_str,
rtrs_clt_reset_cpu_migr_stats);
STAT_ATTR(struct rtrs_clt_stats, reconnects,
rtrs_clt_stats_reconnects_to_str,
rtrs_clt_reset_reconnects_stat);
STAT_ATTR(struct rtrs_clt_stats, rdma,
rtrs_clt_stats_rdma_to_str,
rtrs_clt_reset_rdma_stats);
STAT_ATTR(struct rtrs_clt_stats, reset_all,
rtrs_clt_reset_all_help,
rtrs_clt_reset_all_stats);
static struct attribute *rtrs_clt_stats_attrs[] = {
&cpu_migration_from_attr.attr,
&cpu_migration_to_attr.attr,
&reconnects_attr.attr,
&rdma_attr.attr,
&reset_all_attr.attr,
NULL
};
static const struct attribute_group rtrs_clt_stats_attr_group = {
.attrs = rtrs_clt_stats_attrs,
};
static ssize_t rtrs_clt_hca_port_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *page)
{
struct rtrs_clt_path *clt_path;
clt_path = container_of(kobj, typeof(*clt_path), kobj);
return sysfs_emit(page, "%u\n", clt_path->hca_port);
}
static struct kobj_attribute rtrs_clt_hca_port_attr =
__ATTR(hca_port, 0444, rtrs_clt_hca_port_show, NULL);
static ssize_t rtrs_clt_hca_name_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *page)
{
struct rtrs_clt_path *clt_path;
clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
return sysfs_emit(page, "%s\n", clt_path->hca_name);
}
static struct kobj_attribute rtrs_clt_hca_name_attr =
__ATTR(hca_name, 0444, rtrs_clt_hca_name_show, NULL);
static ssize_t rtrs_clt_cur_latency_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *page)
{
struct rtrs_clt_path *clt_path;
clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
return sysfs_emit(page, "%lld ns\n",
ktime_to_ns(clt_path->s.hb_cur_latency));
}
static struct kobj_attribute rtrs_clt_cur_latency_attr =
__ATTR(cur_latency, 0444, rtrs_clt_cur_latency_show, NULL);
static ssize_t rtrs_clt_src_addr_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *page)
{
struct rtrs_clt_path *clt_path;
int len;
clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
len = sockaddr_to_str((struct sockaddr *)&clt_path->s.src_addr, page,
PAGE_SIZE);
len += sysfs_emit_at(page, len, "\n");
return len;
}
static struct kobj_attribute rtrs_clt_src_addr_attr =
__ATTR(src_addr, 0444, rtrs_clt_src_addr_show, NULL);
static ssize_t rtrs_clt_dst_addr_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *page)
{
struct rtrs_clt_path *clt_path;
int len;
clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
len = sockaddr_to_str((struct sockaddr *)&clt_path->s.dst_addr, page,
PAGE_SIZE);
len += sysfs_emit_at(page, len, "\n");
return len;
}
static struct kobj_attribute rtrs_clt_dst_addr_attr =
__ATTR(dst_addr, 0444, rtrs_clt_dst_addr_show, NULL);
static struct attribute *rtrs_clt_path_attrs[] = {
&rtrs_clt_hca_name_attr.attr,
&rtrs_clt_hca_port_attr.attr,
&rtrs_clt_src_addr_attr.attr,
&rtrs_clt_dst_addr_attr.attr,
&rtrs_clt_state_attr.attr,
&rtrs_clt_reconnect_attr.attr,
&rtrs_clt_disconnect_attr.attr,
&rtrs_clt_remove_path_attr.attr,
&rtrs_clt_cur_latency_attr.attr,
NULL,
};
static const struct attribute_group rtrs_clt_path_attr_group = {
.attrs = rtrs_clt_path_attrs,
};
int rtrs_clt_create_path_files(struct rtrs_clt_path *clt_path)
{
struct rtrs_clt_sess *clt = clt_path->clt;
char str[NAME_MAX];
int err;
struct rtrs_addr path = {
.src = &clt_path->s.src_addr,
.dst = &clt_path->s.dst_addr,
};
rtrs_addr_to_str(&path, str, sizeof(str));
err = kobject_init_and_add(&clt_path->kobj, &ktype_sess,
clt->kobj_paths,
"%s", str);
if (err) {
pr_err("kobject_init_and_add: %pe\n", ERR_PTR(err));
kobject_put(&clt_path->kobj);
return err;
}
err = sysfs_create_group(&clt_path->kobj, &rtrs_clt_path_attr_group);
if (err) {
pr_err("sysfs_create_group(): %pe\n", ERR_PTR(err));
goto put_kobj;
}
err = kobject_init_and_add(&clt_path->stats->kobj_stats, &ktype_stats,
&clt_path->kobj, "stats");
if (err) {
pr_err("kobject_init_and_add: %pe\n", ERR_PTR(err));
kobject_put(&clt_path->stats->kobj_stats);
goto remove_group;
}
err = sysfs_create_group(&clt_path->stats->kobj_stats,
&rtrs_clt_stats_attr_group);
if (err) {
pr_err("failed to create stats sysfs group, err: %pe\n", ERR_PTR(err));
goto put_kobj_stats;
}
return 0;
put_kobj_stats:
kobject_del(&clt_path->stats->kobj_stats);
kobject_put(&clt_path->stats->kobj_stats);
remove_group:
sysfs_remove_group(&clt_path->kobj, &rtrs_clt_path_attr_group);
put_kobj:
kobject_del(&clt_path->kobj);
kobject_put(&clt_path->kobj);
return err;
}
void rtrs_clt_destroy_path_files(struct rtrs_clt_path *clt_path,
const struct attribute *sysfs_self)
{
kobject_del(&clt_path->stats->kobj_stats);
kobject_put(&clt_path->stats->kobj_stats);
if (sysfs_self)
sysfs_remove_file_self(&clt_path->kobj, sysfs_self);
kobject_del(&clt_path->kobj);
}
static struct attribute *rtrs_clt_attrs[] = {
&dev_attr_max_reconnect_attempts.attr,
&dev_attr_mpath_policy.attr,
&dev_attr_add_path.attr,
NULL,
};
static const struct attribute_group rtrs_clt_attr_group = {
.attrs = rtrs_clt_attrs,
};
int rtrs_clt_create_sysfs_root_files(struct rtrs_clt_sess *clt)
{
return sysfs_create_group(&clt->dev.kobj, &rtrs_clt_attr_group);
}
void rtrs_clt_destroy_sysfs_root(struct rtrs_clt_sess *clt)
{
sysfs_remove_group(&clt->dev.kobj, &rtrs_clt_attr_group);
if (clt->kobj_paths) {
kobject_del(clt->kobj_paths);
kobject_put(clt->kobj_paths);
}
}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* RDMA Network Block Driver
*
* Copyright (c) 2022 1&1 IONOS SE. All rights reserved.
*/
#include "rtrs.h"
#include "rtrs-clt.h"
/*
* We include this last to have the helpers above available for the trace
* event implementations.
*/
#define CREATE_TRACE_POINTS
#include "rtrs-clt-trace.h"
@@ -0,0 +1,86 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* RDMA Network Block Driver
*
* Copyright (c) 2022 1&1 IONOS SE. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM rtrs_clt
#if !defined(_TRACE_RTRS_CLT_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_RTRS_CLT_H
#include <linux/tracepoint.h>
struct rtrs_clt_path;
struct rtrs_clt_sess;
TRACE_DEFINE_ENUM(RTRS_CLT_CONNECTING);
TRACE_DEFINE_ENUM(RTRS_CLT_CONNECTING_ERR);
TRACE_DEFINE_ENUM(RTRS_CLT_RECONNECTING);
TRACE_DEFINE_ENUM(RTRS_CLT_CONNECTED);
TRACE_DEFINE_ENUM(RTRS_CLT_CLOSING);
TRACE_DEFINE_ENUM(RTRS_CLT_CLOSED);
TRACE_DEFINE_ENUM(RTRS_CLT_DEAD);
#define show_rtrs_clt_state(x) \
__print_symbolic(x, \
{ RTRS_CLT_CONNECTING, "CONNECTING" }, \
{ RTRS_CLT_CONNECTING_ERR, "CONNECTING_ERR" }, \
{ RTRS_CLT_RECONNECTING, "RECONNECTING" }, \
{ RTRS_CLT_CONNECTED, "CONNECTED" }, \
{ RTRS_CLT_CLOSING, "CLOSING" }, \
{ RTRS_CLT_CLOSED, "CLOSED" }, \
{ RTRS_CLT_DEAD, "DEAD" })
DECLARE_EVENT_CLASS(rtrs_clt_conn_class,
TP_PROTO(struct rtrs_clt_path *clt_path),
TP_ARGS(clt_path),
TP_STRUCT__entry(
__field(int, state)
__field(int, reconnect_attempts)
__field(int, max_reconnect_attempts)
__field(int, fail_cnt)
__field(int, success_cnt)
__array(char, sessname, NAME_MAX)
),
TP_fast_assign(
struct rtrs_clt_sess *clt = clt_path->clt;
__entry->state = clt_path->state;
__entry->reconnect_attempts = clt_path->reconnect_attempts;
__entry->max_reconnect_attempts = clt->max_reconnect_attempts;
__entry->fail_cnt = clt_path->stats->reconnects.fail_cnt;
__entry->success_cnt = clt_path->stats->reconnects.successful_cnt;
memcpy(__entry->sessname, kobject_name(&clt_path->kobj), NAME_MAX);
),
TP_printk("RTRS-CLT: sess='%s' state=%s attempts='%d' max-attempts='%d' fail='%d' success='%d'",
__entry->sessname,
show_rtrs_clt_state(__entry->state),
__entry->reconnect_attempts,
__entry->max_reconnect_attempts,
__entry->fail_cnt,
__entry->success_cnt
)
);
#define DEFINE_CLT_CONN_EVENT(name) \
DEFINE_EVENT(rtrs_clt_conn_class, rtrs_##name, \
TP_PROTO(struct rtrs_clt_path *clt_path), \
TP_ARGS(clt_path))
DEFINE_CLT_CONN_EVENT(clt_reconnect_work);
DEFINE_CLT_CONN_EVENT(clt_close_conns);
DEFINE_CLT_CONN_EVENT(rdma_error_recovery);
#endif /* _TRACE_RTRS_CLT_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE rtrs-clt-trace
#include <trace/define_trace.h>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,249 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#ifndef RTRS_CLT_H
#define RTRS_CLT_H
#include <linux/device.h>
#include "rtrs-pri.h"
/**
* enum rtrs_clt_state - Client states.
*/
enum rtrs_clt_state {
RTRS_CLT_CONNECTING,
RTRS_CLT_CONNECTING_ERR,
RTRS_CLT_RECONNECTING,
RTRS_CLT_CONNECTED,
RTRS_CLT_CLOSING,
RTRS_CLT_CLOSED,
RTRS_CLT_DEAD,
};
enum rtrs_mp_policy {
MP_POLICY_RR,
MP_POLICY_MIN_INFLIGHT,
MP_POLICY_MIN_LATENCY,
};
/* see Documentation/ABI/testing/sysfs-class-rtrs-client for details */
struct rtrs_clt_stats_reconnects {
int successful_cnt;
int fail_cnt;
};
/* see Documentation/ABI/testing/sysfs-class-rtrs-client for details */
struct rtrs_clt_stats_cpu_migr {
atomic_t from;
int to;
};
/* stats for Read and write operation.
* see Documentation/ABI/testing/sysfs-class-rtrs-client for details
*/
struct rtrs_clt_stats_rdma {
struct {
u64 cnt;
u64 size_total;
} dir[2];
u64 failover_cnt;
};
struct rtrs_clt_stats_pcpu {
struct rtrs_clt_stats_cpu_migr cpu_migr;
struct rtrs_clt_stats_rdma rdma;
};
struct rtrs_clt_stats {
struct kobject kobj_stats;
struct rtrs_clt_stats_pcpu __percpu *pcpu_stats;
struct rtrs_clt_stats_reconnects reconnects;
atomic_t inflight;
};
struct rtrs_clt_con {
struct rtrs_con c;
struct rtrs_iu *rsp_ius;
u32 queue_num;
unsigned int cpu;
struct mutex con_mutex;
int cm_err;
};
/**
* rtrs_permit - permits the memory allocation for future RDMA operation.
* Combine with irq pinning to keep IO on same CPU.
*/
struct rtrs_permit {
enum rtrs_clt_con_type con_type;
unsigned int cpu_id;
unsigned int mem_id;
unsigned int mem_off;
};
/**
* rtrs_clt_io_req - describes one inflight IO request
*/
struct rtrs_clt_io_req {
struct rtrs_iu *iu;
struct scatterlist *sglist; /* list holding user data */
unsigned int sg_cnt;
unsigned int sg_size;
unsigned int data_len;
unsigned int usr_len;
void *priv;
bool in_use;
enum rtrs_mp_policy mp_policy;
struct rtrs_clt_con *con;
struct ib_sge *sge;
struct rtrs_permit *permit;
enum dma_data_direction dir;
void (*conf)(void *priv, int errno);
struct ib_mr *mr;
struct ib_cqe inv_cqe;
struct completion inv_comp;
int inv_errno;
bool need_inv_comp;
refcount_t ref;
};
struct rtrs_rbuf {
u64 addr;
u32 rkey;
};
struct rtrs_clt_path {
struct rtrs_path s;
struct rtrs_clt_sess *clt;
wait_queue_head_t state_wq;
enum rtrs_clt_state state;
atomic_t connected_cnt;
struct mutex init_mutex;
struct rtrs_clt_io_req *reqs;
struct delayed_work reconnect_dwork;
struct work_struct close_work;
struct work_struct err_recovery_work;
unsigned int reconnect_attempts;
bool established;
struct rtrs_rbuf *rbufs;
size_t max_io_size;
u32 max_hdr_size;
u32 chunk_size;
size_t queue_depth;
u32 max_pages_per_mr;
u32 flags;
struct kobject kobj;
u8 for_new_clt;
/* cache hca_port and hca_name to display in sysfs */
u8 hca_port;
char hca_name[IB_DEVICE_NAME_MAX];
struct list_head __percpu
*mp_skip_entry;
struct rtrs_clt_stats stats[];
};
struct rtrs_clt_sess {
struct list_head paths_list; /* rcu protected list */
size_t paths_num;
struct rtrs_clt_path
__rcu * __percpu *pcpu_path;
uuid_t paths_uuid;
int paths_up;
struct mutex paths_mutex;
struct mutex paths_ev_mutex;
char sessname[NAME_MAX];
u16 port;
unsigned int max_reconnect_attempts;
unsigned int reconnect_delay_sec;
unsigned int max_segments;
void *permits;
unsigned long *permits_map;
size_t queue_depth;
size_t max_io_size;
wait_queue_head_t permits_wait;
size_t pdu_sz;
void *priv;
void (*link_ev)(void *priv,
enum rtrs_clt_link_ev ev);
struct device dev;
struct kobject *kobj_paths;
enum rtrs_mp_policy mp_policy;
};
static inline struct rtrs_clt_con *to_clt_con(struct rtrs_con *c)
{
return container_of(c, struct rtrs_clt_con, c);
}
static inline struct rtrs_clt_path *to_clt_path(struct rtrs_path *s)
{
return container_of(s, struct rtrs_clt_path, s);
}
static inline int permit_size(struct rtrs_clt_sess *clt)
{
return sizeof(struct rtrs_permit) + clt->pdu_sz;
}
static inline struct rtrs_permit *get_permit(struct rtrs_clt_sess *clt,
int idx)
{
return (struct rtrs_permit *)(clt->permits + permit_size(clt) * idx);
}
int rtrs_clt_reconnect_from_sysfs(struct rtrs_clt_path *path);
void rtrs_clt_close_conns(struct rtrs_clt_path *clt_path, bool wait);
int rtrs_clt_create_path_from_sysfs(struct rtrs_clt_sess *clt,
struct rtrs_addr *addr);
int rtrs_clt_remove_path_from_sysfs(struct rtrs_clt_path *path,
const struct attribute *sysfs_self);
void rtrs_clt_set_max_reconnect_attempts(struct rtrs_clt_sess *clt, int value);
int rtrs_clt_get_max_reconnect_attempts(const struct rtrs_clt_sess *clt);
void free_path(struct rtrs_clt_path *clt_path);
void rtrs_clt_ib_event_handler(struct ib_event_handler *handler,
struct ib_event *ibevent);
/* rtrs-clt-stats.c */
int rtrs_clt_init_stats(struct rtrs_clt_stats *stats);
void rtrs_clt_inc_failover_cnt(struct rtrs_clt_stats *s);
void rtrs_clt_update_wc_stats(struct rtrs_clt_con *con);
void rtrs_clt_update_all_stats(struct rtrs_clt_io_req *req, int dir);
int rtrs_clt_reset_rdma_lat_distr_stats(struct rtrs_clt_stats *stats,
bool enable);
ssize_t rtrs_clt_stats_rdma_lat_distr_to_str(struct rtrs_clt_stats *stats,
char *page);
int rtrs_clt_reset_cpu_migr_stats(struct rtrs_clt_stats *stats, bool enable);
int rtrs_clt_stats_migration_from_cnt_to_str(struct rtrs_clt_stats *stats, char *buf);
int rtrs_clt_stats_migration_to_cnt_to_str(struct rtrs_clt_stats *stats, char *buf);
int rtrs_clt_reset_reconnects_stat(struct rtrs_clt_stats *stats, bool enable);
int rtrs_clt_stats_reconnects_to_str(struct rtrs_clt_stats *stats, char *buf);
int rtrs_clt_reset_rdma_stats(struct rtrs_clt_stats *stats, bool enable);
ssize_t rtrs_clt_stats_rdma_to_str(struct rtrs_clt_stats *stats,
char *page);
int rtrs_clt_reset_all_stats(struct rtrs_clt_stats *stats, bool enable);
ssize_t rtrs_clt_reset_all_help(struct rtrs_clt_stats *stats,
char *page);
/* rtrs-clt-sysfs.c */
int rtrs_clt_create_sysfs_root_files(struct rtrs_clt_sess *clt);
void rtrs_clt_destroy_sysfs_root(struct rtrs_clt_sess *clt);
int rtrs_clt_create_path_files(struct rtrs_clt_path *clt_path);
void rtrs_clt_destroy_path_files(struct rtrs_clt_path *clt_path,
const struct attribute *sysfs_self);
#endif /* RTRS_CLT_H */
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#ifndef RTRS_LOG_H
#define RTRS_LOG_H
#define rtrs_log(fn, obj, fmt, ...) \
fn("<%s>: " fmt, obj->sessname, ##__VA_ARGS__)
#define rtrs_err(obj, fmt, ...) \
rtrs_log(pr_err, obj, fmt, ##__VA_ARGS__)
#define rtrs_err_rl(obj, fmt, ...) \
rtrs_log(pr_err_ratelimited, obj, fmt, ##__VA_ARGS__)
#define rtrs_wrn(obj, fmt, ...) \
rtrs_log(pr_warn, obj, fmt, ##__VA_ARGS__)
#define rtrs_wrn_rl(obj, fmt, ...) \
rtrs_log(pr_warn_ratelimited, obj, fmt, ##__VA_ARGS__)
#define rtrs_info(obj, fmt, ...) \
rtrs_log(pr_info, obj, fmt, ##__VA_ARGS__)
#define rtrs_info_rl(obj, fmt, ...) \
rtrs_log(pr_info_ratelimited, obj, fmt, ##__VA_ARGS__)
#endif /* RTRS_LOG_H */
@@ -0,0 +1,418 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#ifndef RTRS_PRI_H
#define RTRS_PRI_H
#include <linux/uuid.h>
#include <rdma/rdma_cm.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib.h>
#include "rtrs.h"
#define RTRS_PROTO_VER_MAJOR 2
#define RTRS_PROTO_VER_MINOR 0
#define RTRS_PROTO_VER_STRING __stringify(RTRS_PROTO_VER_MAJOR) "." \
__stringify(RTRS_PROTO_VER_MINOR)
/*
* Max IB immediate data size is 2^28 (MAX_IMM_PAYL_BITS)
* and the minimum chunk size is 4096 (2^12).
* So the maximum sess_queue_depth is 65535 (2^16 - 1) in theory
* since queue_depth in rtrs_msg_conn_rsp is defined as le16.
* Therefore the pratical max value of sess_queue_depth is
* somewhere between 1 and 65535 and it depends on the system.
*/
#define MAX_SESS_QUEUE_DEPTH 65535
enum rtrs_imm_const {
MAX_IMM_TYPE_BITS = 4,
MAX_IMM_TYPE_MASK = ((1 << MAX_IMM_TYPE_BITS) - 1),
MAX_IMM_PAYL_BITS = 28,
MAX_IMM_PAYL_MASK = ((1 << MAX_IMM_PAYL_BITS) - 1),
};
enum rtrs_imm_type {
RTRS_IO_REQ_IMM = 0, /* client to server */
RTRS_IO_RSP_IMM = 1, /* server to client */
RTRS_IO_RSP_W_INV_IMM = 2, /* server to client */
RTRS_HB_MSG_IMM = 8, /* HB: HeartBeat */
RTRS_HB_ACK_IMM = 9,
RTRS_LAST_IMM,
};
enum {
SERVICE_CON_QUEUE_DEPTH = 512,
MAX_PATHS_NUM = 128,
MIN_CHUNK_SIZE = 8192,
RTRS_HB_INTERVAL_MS = 5000,
RTRS_HB_MISSED_MAX = 5,
RTRS_MAGIC = 0x1BBD,
RTRS_PROTO_VER = (RTRS_PROTO_VER_MAJOR << 8) | RTRS_PROTO_VER_MINOR,
};
struct rtrs_ib_dev;
struct rtrs_rdma_dev_pd_ops {
int (*init)(struct rtrs_ib_dev *dev);
void (*deinit)(struct rtrs_ib_dev *dev);
};
struct rtrs_rdma_dev_pd {
struct mutex mutex;
struct list_head list;
enum ib_pd_flags pd_flags;
const struct rtrs_rdma_dev_pd_ops *ops;
};
struct rtrs_ib_dev {
struct ib_device *ib_dev;
struct ib_pd *ib_pd;
struct kref ref;
struct list_head entry;
struct rtrs_rdma_dev_pd *pool;
struct ib_event_handler event_handler;
};
struct rtrs_con {
struct rtrs_path *path;
struct ib_qp *qp;
struct ib_cq *cq;
struct rdma_cm_id *cm_id;
unsigned int cid;
int nr_cqe;
atomic_t wr_cnt;
atomic_t sq_wr_avail;
};
struct rtrs_path {
struct list_head entry;
struct sockaddr_storage dst_addr;
struct sockaddr_storage src_addr;
char sessname[NAME_MAX];
uuid_t uuid;
struct rtrs_con **con;
unsigned int con_num;
unsigned int irq_con_num;
unsigned int recon_cnt;
unsigned int signal_interval;
struct rtrs_ib_dev *dev;
int dev_ref;
struct ib_cqe *hb_cqe;
void (*hb_err_handler)(struct rtrs_con *con);
struct workqueue_struct *hb_wq;
struct delayed_work hb_dwork;
unsigned int hb_interval_ms;
unsigned int hb_missed_cnt;
unsigned int hb_missed_max;
ktime_t hb_last_sent;
ktime_t hb_cur_latency;
};
/* rtrs information unit */
struct rtrs_iu {
struct ib_cqe cqe;
dma_addr_t dma_addr;
void *buf;
size_t size;
enum dma_data_direction direction;
};
/**
* enum rtrs_msg_types - RTRS message types, see also rtrs/README
* @RTRS_MSG_INFO_REQ: Client additional info request to the server
* @RTRS_MSG_INFO_RSP: Server additional info response to the client
* @RTRS_MSG_WRITE: Client writes data per RDMA to server
* @RTRS_MSG_READ: Client requests data transfer from server
* @RTRS_MSG_RKEY_RSP: Server refreshed rkey for rbuf
*/
enum rtrs_msg_types {
RTRS_MSG_INFO_REQ,
RTRS_MSG_INFO_RSP,
RTRS_MSG_WRITE,
RTRS_MSG_READ,
RTRS_MSG_RKEY_RSP,
};
/**
* enum rtrs_msg_flags - RTRS message flags.
* @RTRS_MSG_NEED_INVAL_F: Send invalidation in response.
* @RTRS_MSG_NEW_RKEY_F: Send refreshed rkey in response.
*/
enum rtrs_msg_flags {
RTRS_MSG_NEED_INVAL_F = 1 << 0,
RTRS_MSG_NEW_RKEY_F = 1 << 1,
};
/**
* struct rtrs_sg_desc - RDMA-Buffer entry description
* @addr: Address of RDMA destination buffer
* @key: Authorization rkey to write to the buffer
* @len: Size of the buffer
*/
struct rtrs_sg_desc {
__le64 addr;
__le32 key;
__le32 len;
};
/**
* struct rtrs_msg_conn_req - Client connection request to the server
* @magic: RTRS magic
* @version: RTRS protocol version
* @cid: Current connection id
* @cid_num: Number of connections per session
* @recon_cnt: Reconnections counter
* @sess_uuid: UUID of a session (path)
* @paths_uuid: UUID of a group of sessions (paths)
* @first_conn: %1 if the connection request is the first for that session,
* otherwise %0
* NOTE: max size 56 bytes, see man rdma_connect().
*/
struct rtrs_msg_conn_req {
/**
* @__cma_version: Is set to 0 by cma.c in case of AF_IB, do not touch
* that. See https://www.spinics.net/lists/linux-rdma/msg22397.html
*/
u8 __cma_version;
/**
* @__ip_version: On sender side that should be set to 0, or
* cma_save_ip_info() extract garbage and will fail.
*/
u8 __ip_version;
__le16 magic;
__le16 version;
__le16 cid;
__le16 cid_num;
__le16 recon_cnt;
uuid_t sess_uuid;
uuid_t paths_uuid;
u8 first_conn : 1;
/* private: */
u8 reserved_bits : 7;
u8 reserved[11];
};
/**
* struct rtrs_msg_conn_rsp - Server connection response to the client
* @magic: RTRS magic
* @version: RTRS protocol version
* @errno: If rdma_accept() then 0, if rdma_reject() indicates error
* @queue_depth: max inflight messages (queue-depth) in this session
* @max_io_size: max io size server supports
* @max_hdr_size: max msg header size server supports
* @flags: RTRS message flags for this message
*
* NOTE: size is 56 bytes, max possible is 136 bytes, see man rdma_accept().
*/
struct rtrs_msg_conn_rsp {
__le16 magic;
__le16 version;
__le16 errno;
__le16 queue_depth;
__le32 max_io_size;
__le32 max_hdr_size;
__le32 flags;
/* private: */
u8 reserved[36];
};
/**
* struct rtrs_msg_info_req - client additional info request
* @type: @RTRS_MSG_INFO_REQ
* @pathname: Path name chosen by client
*/
struct rtrs_msg_info_req {
__le16 type;
u8 pathname[NAME_MAX];
/* private: */
u8 reserved[15];
};
/**
* struct rtrs_msg_info_rsp - server additional info response
* @type: @RTRS_MSG_INFO_RSP
* @sg_cnt: Number of @desc entries
* @desc: RDMA buffers where the client can write to server
*/
struct rtrs_msg_info_rsp {
__le16 type;
__le16 sg_cnt;
/* private: */
u8 reserved[4];
/* public: */
struct rtrs_sg_desc desc[];
};
/**
* struct rtrs_msg_rkey_rsp - server refreshed rkey response
* @type: @RTRS_MSG_RKEY_RSP
* @buf_id: RDMA buf_id of the new rkey
* @rkey: new remote key for RDMA buffers id from server
*/
struct rtrs_msg_rkey_rsp {
__le16 type;
__le16 buf_id;
__le32 rkey;
};
/**
* struct rtrs_msg_rdma_read - RDMA data transfer request from client
* @type: always @RTRS_MSG_READ
* @flags: RTRS message flags (enum rtrs_msg_flags)
* @usr_len: length of user payload
* @sg_cnt: number of @desc entries
* @desc: RDMA buffers where the server can write the result to
*/
struct rtrs_msg_rdma_read {
__le16 type;
__le16 usr_len;
__le16 flags;
__le16 sg_cnt;
struct rtrs_sg_desc desc[];
};
/**
* struct rtrs_msg_rdma_write - Message transferred to server with RDMA-Write
* @type: always @RTRS_MSG_WRITE
* @usr_len: length of user payload
*/
struct rtrs_msg_rdma_write {
__le16 type;
__le16 usr_len;
};
/**
* struct rtrs_msg_rdma_hdr - header for read or write request
* @type: @RTRS_MSG_WRITE | @RTRS_MSG_READ
*/
struct rtrs_msg_rdma_hdr {
__le16 type;
};
/* rtrs.c */
struct rtrs_iu *rtrs_iu_alloc(u32 queue_num, size_t size, gfp_t t,
struct ib_device *dev, enum dma_data_direction,
void (*done)(struct ib_cq *cq, struct ib_wc *wc));
void rtrs_iu_free(struct rtrs_iu *iu, struct ib_device *dev, u32 queue_num);
int rtrs_iu_post_recv(struct rtrs_con *con, struct rtrs_iu *iu);
int rtrs_iu_post_send(struct rtrs_con *con, struct rtrs_iu *iu, size_t size,
struct ib_send_wr *head);
int rtrs_iu_post_rdma_write_imm(struct rtrs_con *con, struct rtrs_iu *iu,
struct ib_sge *sge, unsigned int num_sge,
u32 rkey, u64 rdma_addr, u32 imm_data,
enum ib_send_flags flags,
struct ib_send_wr *head,
struct ib_send_wr *tail);
int rtrs_post_recv_empty(struct rtrs_con *con, struct ib_cqe *cqe);
int rtrs_cq_qp_create(struct rtrs_path *path, struct rtrs_con *con,
u32 max_send_sge, int cq_vector, int nr_cqe,
u32 max_send_wr, u32 max_recv_wr,
enum ib_poll_context poll_ctx);
void rtrs_cq_qp_destroy(struct rtrs_con *con);
void rtrs_init_hb(struct rtrs_path *path, struct ib_cqe *cqe,
unsigned int interval_ms, unsigned int missed_max,
void (*err_handler)(struct rtrs_con *con),
struct workqueue_struct *wq);
void rtrs_start_hb(struct rtrs_path *path);
void rtrs_stop_hb(struct rtrs_path *path);
void rtrs_send_hb_ack(struct rtrs_path *path);
void rtrs_rdma_dev_pd_init(enum ib_pd_flags pd_flags,
struct rtrs_rdma_dev_pd *pool);
void rtrs_rdma_dev_pd_deinit(struct rtrs_rdma_dev_pd *pool);
struct rtrs_ib_dev *rtrs_ib_dev_find_or_add(struct ib_device *ib_dev,
struct rtrs_rdma_dev_pd *pool);
int rtrs_ib_dev_put(struct rtrs_ib_dev *dev);
static inline u32 rtrs_to_imm(u32 type, u32 payload)
{
BUILD_BUG_ON(MAX_IMM_PAYL_BITS + MAX_IMM_TYPE_BITS != 32);
BUILD_BUG_ON(RTRS_LAST_IMM > (1<<MAX_IMM_TYPE_BITS));
return ((type & MAX_IMM_TYPE_MASK) << MAX_IMM_PAYL_BITS) |
(payload & MAX_IMM_PAYL_MASK);
}
static inline void rtrs_from_imm(u32 imm, u32 *type, u32 *payload)
{
*payload = imm & MAX_IMM_PAYL_MASK;
*type = imm >> MAX_IMM_PAYL_BITS;
}
static inline u32 rtrs_to_io_req_imm(u32 addr)
{
return rtrs_to_imm(RTRS_IO_REQ_IMM, addr);
}
static inline u32 rtrs_to_io_rsp_imm(u32 msg_id, int errno, bool w_inval)
{
enum rtrs_imm_type type;
u32 payload;
/* 9 bits for errno, 19 bits for msg_id */
payload = (abs(errno) & 0x1ff) << 19 | (msg_id & 0x7ffff);
type = w_inval ? RTRS_IO_RSP_W_INV_IMM : RTRS_IO_RSP_IMM;
return rtrs_to_imm(type, payload);
}
static inline void rtrs_from_io_rsp_imm(u32 payload, u32 *msg_id, int *errno)
{
/* 9 bits for errno, 19 bits for msg_id */
*msg_id = payload & 0x7ffff;
*errno = -(int)((payload >> 19) & 0x1ff);
}
#define STAT_STORE_FUNC(type, set_value, reset) \
static ssize_t set_value##_store(struct kobject *kobj, \
struct kobj_attribute *attr, \
const char *buf, size_t count) \
{ \
int ret = -EINVAL; \
type *stats = container_of(kobj, type, kobj_stats); \
\
if (sysfs_streq(buf, "1")) \
ret = reset(stats, true); \
else if (sysfs_streq(buf, "0")) \
ret = reset(stats, false); \
if (ret) \
return ret; \
\
return count; \
}
#define STAT_SHOW_FUNC(type, get_value, print) \
static ssize_t get_value##_show(struct kobject *kobj, \
struct kobj_attribute *attr, \
char *page) \
{ \
type *stats = container_of(kobj, type, kobj_stats); \
\
return print(stats, page); \
}
#define STAT_ATTR(type, stat, print, reset) \
STAT_STORE_FUNC(type, stat, reset) \
STAT_SHOW_FUNC(type, stat, print) \
static struct kobj_attribute stat##_attr = __ATTR_RW(stat)
#endif /* RTRS_PRI_H */
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
#include "rtrs-srv.h"
int rtrs_srv_reset_rdma_stats(struct rtrs_srv_stats *stats, bool enable)
{
if (enable) {
int cpu;
struct rtrs_srv_stats_rdma_stats *r;
for_each_possible_cpu(cpu) {
r = per_cpu_ptr(stats->rdma_stats, cpu);
memset(r, 0, sizeof(*r));
}
return 0;
}
return -EINVAL;
}
ssize_t rtrs_srv_stats_rdma_to_str(struct rtrs_srv_stats *stats, char *page)
{
int cpu;
struct rtrs_srv_stats_rdma_stats sum;
struct rtrs_srv_stats_rdma_stats *r;
memset(&sum, 0, sizeof(sum));
for_each_possible_cpu(cpu) {
r = per_cpu_ptr(stats->rdma_stats, cpu);
sum.dir[READ].cnt += r->dir[READ].cnt;
sum.dir[READ].size_total += r->dir[READ].size_total;
sum.dir[WRITE].cnt += r->dir[WRITE].cnt;
sum.dir[WRITE].size_total += r->dir[WRITE].size_total;
}
return sysfs_emit(page, "%llu %llu %llu %llu\n",
sum.dir[READ].cnt, sum.dir[READ].size_total,
sum.dir[WRITE].cnt, sum.dir[WRITE].size_total);
}
@@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
#include "rtrs-pri.h"
#include "rtrs-srv.h"
#include "rtrs-log.h"
static void rtrs_srv_release(struct kobject *kobj)
{
struct rtrs_srv_path *srv_path;
srv_path = container_of(kobj, struct rtrs_srv_path, kobj);
kfree(srv_path);
}
static struct kobj_type ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.release = rtrs_srv_release,
};
static ssize_t rtrs_srv_disconnect_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name);
}
static ssize_t rtrs_srv_disconnect_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct rtrs_srv_path *srv_path;
struct rtrs_path *s;
char str[MAXHOSTNAMELEN];
srv_path = container_of(kobj, struct rtrs_srv_path, kobj);
s = &srv_path->s;
if (!sysfs_streq(buf, "1")) {
rtrs_err(s, "%s: invalid value: '%s'\n",
attr->attr.name, buf);
return -EINVAL;
}
sockaddr_to_str((struct sockaddr *)&srv_path->s.dst_addr, str,
sizeof(str));
rtrs_info(s, "disconnect for path %s requested\n", str);
/* first remove sysfs itself to avoid deadlock */
sysfs_remove_file_self(&srv_path->kobj, &attr->attr);
close_path(srv_path);
return count;
}
static struct kobj_attribute rtrs_srv_disconnect_attr =
__ATTR(disconnect, 0644,
rtrs_srv_disconnect_show, rtrs_srv_disconnect_store);
static ssize_t rtrs_srv_hca_port_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *page)
{
struct rtrs_srv_path *srv_path;
struct rtrs_con *usr_con;
srv_path = container_of(kobj, typeof(*srv_path), kobj);
usr_con = srv_path->s.con[0];
return sysfs_emit(page, "%u\n", usr_con->cm_id->port_num);
}
static struct kobj_attribute rtrs_srv_hca_port_attr =
__ATTR(hca_port, 0444, rtrs_srv_hca_port_show, NULL);
static ssize_t rtrs_srv_hca_name_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *page)
{
struct rtrs_srv_path *srv_path;
srv_path = container_of(kobj, struct rtrs_srv_path, kobj);
return sysfs_emit(page, "%s\n", srv_path->s.dev->ib_dev->name);
}
static struct kobj_attribute rtrs_srv_hca_name_attr =
__ATTR(hca_name, 0444, rtrs_srv_hca_name_show, NULL);
static ssize_t rtrs_srv_src_addr_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *page)
{
struct rtrs_srv_path *srv_path;
int cnt;
srv_path = container_of(kobj, struct rtrs_srv_path, kobj);
cnt = sockaddr_to_str((struct sockaddr *)&srv_path->s.dst_addr,
page, PAGE_SIZE);
return cnt + sysfs_emit_at(page, cnt, "\n");
}
static struct kobj_attribute rtrs_srv_src_addr_attr =
__ATTR(src_addr, 0444, rtrs_srv_src_addr_show, NULL);
static ssize_t rtrs_srv_dst_addr_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *page)
{
struct rtrs_srv_path *srv_path;
int len;
srv_path = container_of(kobj, struct rtrs_srv_path, kobj);
len = sockaddr_to_str((struct sockaddr *)&srv_path->s.src_addr, page,
PAGE_SIZE);
len += sysfs_emit_at(page, len, "\n");
return len;
}
static struct kobj_attribute rtrs_srv_dst_addr_attr =
__ATTR(dst_addr, 0444, rtrs_srv_dst_addr_show, NULL);
static struct attribute *rtrs_srv_path_attrs[] = {
&rtrs_srv_hca_name_attr.attr,
&rtrs_srv_hca_port_attr.attr,
&rtrs_srv_src_addr_attr.attr,
&rtrs_srv_dst_addr_attr.attr,
&rtrs_srv_disconnect_attr.attr,
NULL,
};
static const struct attribute_group rtrs_srv_path_attr_group = {
.attrs = rtrs_srv_path_attrs,
};
STAT_ATTR(struct rtrs_srv_stats, rdma,
rtrs_srv_stats_rdma_to_str,
rtrs_srv_reset_rdma_stats);
static struct attribute *rtrs_srv_stats_attrs[] = {
&rdma_attr.attr,
NULL,
};
static const struct attribute_group rtrs_srv_stats_attr_group = {
.attrs = rtrs_srv_stats_attrs,
};
static int rtrs_srv_create_once_sysfs_root_folders(struct rtrs_srv_path *srv_path)
{
struct rtrs_srv_sess *srv = srv_path->srv;
int err = 0;
mutex_lock(&srv->paths_mutex);
if (srv->dev_ref++) {
/*
* Device needs to be registered only on the first session
*/
goto unlock;
}
srv->dev.class = &rtrs_dev_class;
err = dev_set_name(&srv->dev, "%s", srv_path->s.sessname);
if (err)
goto unlock;
/*
* Suppress user space notification until
* sysfs files are created
*/
dev_set_uevent_suppress(&srv->dev, true);
err = device_add(&srv->dev);
if (err) {
pr_err("device_add(): %pe\n", ERR_PTR(err));
put_device(&srv->dev);
goto unlock;
}
srv->kobj_paths = kobject_create_and_add("paths", &srv->dev.kobj);
if (!srv->kobj_paths) {
err = -ENOMEM;
pr_err("kobject_create_and_add(): %pe\n", ERR_PTR(err));
device_del(&srv->dev);
put_device(&srv->dev);
goto unlock;
}
dev_set_uevent_suppress(&srv->dev, false);
kobject_uevent(&srv->dev.kobj, KOBJ_ADD);
unlock:
mutex_unlock(&srv->paths_mutex);
return err;
}
static void
rtrs_srv_destroy_once_sysfs_root_folders(struct rtrs_srv_path *srv_path)
{
struct rtrs_srv_sess *srv = srv_path->srv;
mutex_lock(&srv->paths_mutex);
if (!--srv->dev_ref) {
kobject_put(srv->kobj_paths);
mutex_unlock(&srv->paths_mutex);
device_del(&srv->dev);
put_device(&srv->dev);
} else {
put_device(&srv->dev);
mutex_unlock(&srv->paths_mutex);
}
}
static void rtrs_srv_path_stats_release(struct kobject *kobj)
{
struct rtrs_srv_stats *stats;
stats = container_of(kobj, struct rtrs_srv_stats, kobj_stats);
free_percpu(stats->rdma_stats);
kfree(stats);
}
static struct kobj_type ktype_stats = {
.sysfs_ops = &kobj_sysfs_ops,
.release = rtrs_srv_path_stats_release,
};
static int rtrs_srv_create_stats_files(struct rtrs_srv_path *srv_path)
{
int err;
struct rtrs_path *s = &srv_path->s;
err = kobject_init_and_add(&srv_path->stats->kobj_stats, &ktype_stats,
&srv_path->kobj, "stats");
if (err) {
rtrs_err(s, "kobject_init_and_add(): %pe\n", ERR_PTR(err));
kobject_put(&srv_path->stats->kobj_stats);
return err;
}
err = sysfs_create_group(&srv_path->stats->kobj_stats,
&rtrs_srv_stats_attr_group);
if (err) {
rtrs_err(s, "sysfs_create_group(): %pe\n", ERR_PTR(err));
goto err;
}
return 0;
err:
kobject_del(&srv_path->stats->kobj_stats);
kobject_put(&srv_path->stats->kobj_stats);
return err;
}
int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path)
{
struct rtrs_srv_sess *srv = srv_path->srv;
struct rtrs_path *s = &srv_path->s;
char str[NAME_MAX];
int err;
struct rtrs_addr path = {
.src = &srv_path->s.dst_addr,
.dst = &srv_path->s.src_addr,
};
rtrs_addr_to_str(&path, str, sizeof(str));
err = rtrs_srv_create_once_sysfs_root_folders(srv_path);
if (err)
return err;
err = kobject_init_and_add(&srv_path->kobj, &ktype, srv->kobj_paths,
"%s", str);
if (err) {
rtrs_err(s, "kobject_init_and_add(): %pe\n", ERR_PTR(err));
goto destroy_root;
}
err = sysfs_create_group(&srv_path->kobj, &rtrs_srv_path_attr_group);
if (err) {
rtrs_err(s, "sysfs_create_group(): %pe\n", ERR_PTR(err));
goto put_kobj;
}
err = rtrs_srv_create_stats_files(srv_path);
if (err)
goto remove_group;
return 0;
remove_group:
sysfs_remove_group(&srv_path->kobj, &rtrs_srv_path_attr_group);
put_kobj:
kobject_del(&srv_path->kobj);
destroy_root:
rtrs_srv_destroy_once_sysfs_root_folders(srv_path);
kobject_put(&srv_path->kobj);
return err;
}
void rtrs_srv_destroy_path_files(struct rtrs_srv_path *srv_path)
{
if (srv_path->stats->kobj_stats.state_in_sysfs) {
sysfs_remove_group(&srv_path->stats->kobj_stats,
&rtrs_srv_stats_attr_group);
kobject_del(&srv_path->stats->kobj_stats);
kobject_put(&srv_path->stats->kobj_stats);
}
if (srv_path->kobj.state_in_sysfs) {
sysfs_remove_group(&srv_path->kobj, &rtrs_srv_path_attr_group);
kobject_put(&srv_path->kobj);
rtrs_srv_destroy_once_sysfs_root_folders(srv_path);
}
}
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* RDMA Network Block Driver
*
* Copyright (c) 2022 1&1 IONOS SE. All rights reserved.
*/
#include "rtrs.h"
#include "rtrs-pri.h"
#include "rtrs-srv.h"
/*
* We include this last to have the helpers above available for the trace
* event implementations.
*/
#define CREATE_TRACE_POINTS
#include "rtrs-srv-trace.h"
@@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* RDMA Network Block Driver
*
* Copyright (c) 2022 1&1 IONOS SE. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM rtrs_srv
#if !defined(_TRACE_RTRS_SRV_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_RTRS_SRV_H
#include <linux/tracepoint.h>
struct rtrs_srv_op;
struct rtrs_srv_con;
struct rtrs_srv_path;
TRACE_DEFINE_ENUM(RTRS_SRV_CONNECTING);
TRACE_DEFINE_ENUM(RTRS_SRV_CONNECTED);
TRACE_DEFINE_ENUM(RTRS_SRV_CLOSING);
TRACE_DEFINE_ENUM(RTRS_SRV_CLOSED);
#define show_rtrs_srv_state(x) \
__print_symbolic(x, \
{ RTRS_SRV_CONNECTING, "CONNECTING" }, \
{ RTRS_SRV_CONNECTED, "CONNECTED" }, \
{ RTRS_SRV_CLOSING, "CLOSING" }, \
{ RTRS_SRV_CLOSED, "CLOSED" })
TRACE_EVENT(send_io_resp_imm,
TP_PROTO(struct rtrs_srv_op *id,
bool need_inval,
bool always_invalidate,
int errno),
TP_ARGS(id, need_inval, always_invalidate, errno),
TP_STRUCT__entry(
__field(u8, dir)
__field(bool, need_inval)
__field(bool, always_invalidate)
__field(u32, msg_id)
__field(int, wr_cnt)
__field(u32, signal_interval)
__field(int, state)
__field(int, errno)
__array(char, sessname, NAME_MAX)
),
TP_fast_assign(
struct rtrs_srv_con *con = id->con;
struct rtrs_path *s = con->c.path;
struct rtrs_srv_path *srv_path = to_srv_path(s);
__entry->dir = id->dir;
__entry->state = srv_path->state;
__entry->errno = errno;
__entry->need_inval = need_inval;
__entry->always_invalidate = always_invalidate;
__entry->msg_id = id->msg_id;
__entry->wr_cnt = atomic_read(&con->c.wr_cnt);
__entry->signal_interval = s->signal_interval;
memcpy(__entry->sessname, kobject_name(&srv_path->kobj), NAME_MAX);
),
TP_printk("sess='%s' state='%s' dir=%s err='%d' inval='%d' glob-inval='%d' msgid='%u' wrcnt='%d' sig-interval='%u'",
__entry->sessname,
show_rtrs_srv_state(__entry->state),
__print_symbolic(__entry->dir,
{ READ, "READ" },
{ WRITE, "WRITE" }),
__entry->errno,
__entry->need_inval,
__entry->always_invalidate,
__entry->msg_id,
__entry->wr_cnt,
__entry->signal_interval
)
);
#endif /* _TRACE_RTRS_SRV_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE rtrs-srv-trace
#include <trace/define_trace.h>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,157 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#ifndef RTRS_SRV_H
#define RTRS_SRV_H
#include <linux/device.h>
#include <linux/refcount.h>
#include <linux/percpu.h>
#include "rtrs-pri.h"
/*
* enum rtrs_srv_state - Server states.
*/
enum rtrs_srv_state {
RTRS_SRV_CONNECTING,
RTRS_SRV_CONNECTED,
RTRS_SRV_CLOSING,
RTRS_SRV_CLOSED,
};
/* stats for Read and write operation.
* see Documentation/ABI/testing/sysfs-class-rtrs-server for details
*/
struct rtrs_srv_stats_rdma_stats {
struct {
u64 cnt;
u64 size_total;
} dir[2];
};
struct rtrs_srv_stats {
struct kobject kobj_stats;
struct rtrs_srv_stats_rdma_stats __percpu *rdma_stats;
struct rtrs_srv_path *srv_path;
};
struct rtrs_srv_con {
struct rtrs_con c;
struct list_head rsp_wr_wait_list;
spinlock_t rsp_wr_wait_lock;
};
/* IO context in rtrs_srv, each io has one */
struct rtrs_srv_op {
struct rtrs_srv_con *con;
u32 msg_id;
u8 dir;
struct rtrs_msg_rdma_read *rd_msg;
struct ib_rdma_wr tx_wr;
struct ib_sge tx_sg;
struct list_head wait_list;
int status;
};
/*
* server side memory region context, when always_invalidate=Y, we need
* queue_depth of memory region to invalidate each memory region.
*/
struct rtrs_srv_mr {
struct ib_mr *mr;
struct sg_table sgt;
struct ib_cqe inv_cqe; /* only for always_invalidate=true */
u32 msg_id; /* only for always_invalidate=true */
u32 msg_off; /* only for always_invalidate=true */
struct rtrs_iu *iu; /* send buffer for new rkey msg */
};
struct rtrs_srv_path {
struct rtrs_path s;
struct rtrs_srv_sess *srv;
struct work_struct close_work;
enum rtrs_srv_state state;
spinlock_t state_lock;
int cur_cq_vector;
struct rtrs_srv_op **ops_ids;
struct percpu_ref ids_inflight_ref;
struct completion complete_done;
struct rtrs_srv_mr *mrs;
unsigned int mrs_num;
dma_addr_t *dma_addr;
bool established;
unsigned int mem_bits;
struct kobject kobj;
struct rtrs_srv_stats *stats;
unsigned long connection_timeout;
};
static inline struct rtrs_srv_path *to_srv_path(struct rtrs_path *s)
{
return container_of(s, struct rtrs_srv_path, s);
}
struct rtrs_srv_sess {
struct list_head paths_list;
int paths_up;
struct mutex paths_ev_mutex;
size_t paths_num;
struct mutex paths_mutex;
uuid_t paths_uuid;
refcount_t refcount;
struct rtrs_srv_ctx *ctx;
struct list_head ctx_list;
void *priv;
size_t queue_depth;
struct page **chunks;
struct device dev;
unsigned int dev_ref;
struct kobject *kobj_paths;
};
struct rtrs_srv_ctx {
struct rtrs_srv_ops ops;
struct rdma_cm_id *cm_id_ip;
struct rdma_cm_id *cm_id_ib;
struct mutex srv_mutex;
struct list_head srv_list;
};
struct rtrs_srv_ib_ctx {
struct rtrs_srv_ctx *srv_ctx;
u16 port;
struct mutex ib_dev_mutex;
int ib_dev_count;
};
extern const struct class rtrs_dev_class;
void close_path(struct rtrs_srv_path *srv_path);
void rtrs_srv_ib_event_handler(struct ib_event_handler *handler,
struct ib_event *ibevent);
static inline void rtrs_srv_update_rdma_stats(struct rtrs_srv_stats *s,
size_t size, int d)
{
this_cpu_inc(s->rdma_stats->dir[d].cnt);
this_cpu_add(s->rdma_stats->dir[d].size_total, size);
}
/* functions which are implemented in rtrs-srv-stats.c */
int rtrs_srv_reset_rdma_stats(struct rtrs_srv_stats *stats, bool enable);
ssize_t rtrs_srv_stats_rdma_to_str(struct rtrs_srv_stats *stats, char *page);
int rtrs_srv_reset_all_stats(struct rtrs_srv_stats *stats, bool enable);
ssize_t rtrs_srv_reset_all_help(struct rtrs_srv_stats *stats,
char *page, size_t len);
/* functions which are implemented in rtrs-srv-sysfs.c */
int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path);
void rtrs_srv_destroy_path_files(struct rtrs_srv_path *srv_path);
#endif /* RTRS_SRV_H */
@@ -0,0 +1,648 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
#include <linux/module.h>
#include <linux/inet.h>
#include "rtrs-pri.h"
#include "rtrs-log.h"
MODULE_DESCRIPTION("RDMA Transport Core");
MODULE_LICENSE("GPL");
struct rtrs_iu *rtrs_iu_alloc(u32 iu_num, size_t size, gfp_t gfp_mask,
struct ib_device *dma_dev,
enum dma_data_direction dir,
void (*done)(struct ib_cq *cq, struct ib_wc *wc))
{
struct rtrs_iu *ius, *iu;
int i;
ius = kzalloc_objs(*ius, iu_num, gfp_mask);
if (!ius)
return NULL;
for (i = 0; i < iu_num; i++) {
iu = &ius[i];
iu->direction = dir;
iu->buf = kzalloc(size, gfp_mask);
if (!iu->buf)
goto err;
iu->dma_addr = ib_dma_map_single(dma_dev, iu->buf, size, dir);
if (ib_dma_mapping_error(dma_dev, iu->dma_addr)) {
kfree(iu->buf);
goto err;
}
iu->cqe.done = done;
iu->size = size;
}
return ius;
err:
rtrs_iu_free(ius, dma_dev, i);
return NULL;
}
EXPORT_SYMBOL_GPL(rtrs_iu_alloc);
void rtrs_iu_free(struct rtrs_iu *ius, struct ib_device *ibdev, u32 queue_num)
{
struct rtrs_iu *iu;
int i;
if (!ius)
return;
for (i = 0; i < queue_num; i++) {
iu = &ius[i];
ib_dma_unmap_single(ibdev, iu->dma_addr, iu->size, iu->direction);
kfree(iu->buf);
}
kfree(ius);
}
EXPORT_SYMBOL_GPL(rtrs_iu_free);
int rtrs_iu_post_recv(struct rtrs_con *con, struct rtrs_iu *iu)
{
struct rtrs_path *path = con->path;
struct ib_recv_wr wr;
struct ib_sge list;
list.addr = iu->dma_addr;
list.length = iu->size;
list.lkey = path->dev->ib_pd->local_dma_lkey;
if (list.length == 0) {
rtrs_wrn(con->path,
"Posting receive work request failed, sg list is empty\n");
return -EINVAL;
}
wr = (struct ib_recv_wr) {
.wr_cqe = &iu->cqe,
.sg_list = &list,
.num_sge = 1,
};
return ib_post_recv(con->qp, &wr, NULL);
}
EXPORT_SYMBOL_GPL(rtrs_iu_post_recv);
int rtrs_post_recv_empty(struct rtrs_con *con, struct ib_cqe *cqe)
{
struct ib_recv_wr wr;
wr = (struct ib_recv_wr) {
.wr_cqe = cqe,
};
return ib_post_recv(con->qp, &wr, NULL);
}
EXPORT_SYMBOL_GPL(rtrs_post_recv_empty);
static int rtrs_post_send(struct ib_qp *qp, struct ib_send_wr *head,
struct ib_send_wr *wr, struct ib_send_wr *tail)
{
if (head) {
struct ib_send_wr *next = head;
while (next->next)
next = next->next;
next->next = wr;
} else {
head = wr;
}
if (tail)
wr->next = tail;
return ib_post_send(qp, head, NULL);
}
int rtrs_iu_post_send(struct rtrs_con *con, struct rtrs_iu *iu, size_t size,
struct ib_send_wr *head)
{
struct rtrs_path *path = con->path;
struct ib_send_wr wr;
struct ib_sge list;
if (WARN_ON(size == 0))
return -EINVAL;
list.addr = iu->dma_addr;
list.length = size;
list.lkey = path->dev->ib_pd->local_dma_lkey;
wr = (struct ib_send_wr) {
.wr_cqe = &iu->cqe,
.sg_list = &list,
.num_sge = 1,
.opcode = IB_WR_SEND,
.send_flags = IB_SEND_SIGNALED,
};
return rtrs_post_send(con->qp, head, &wr, NULL);
}
EXPORT_SYMBOL_GPL(rtrs_iu_post_send);
int rtrs_iu_post_rdma_write_imm(struct rtrs_con *con, struct rtrs_iu *iu,
struct ib_sge *sge, unsigned int num_sge,
u32 rkey, u64 rdma_addr, u32 imm_data,
enum ib_send_flags flags,
struct ib_send_wr *head,
struct ib_send_wr *tail)
{
struct ib_rdma_wr wr;
int i;
wr = (struct ib_rdma_wr) {
.wr.wr_cqe = &iu->cqe,
.wr.sg_list = sge,
.wr.num_sge = num_sge,
.rkey = rkey,
.remote_addr = rdma_addr,
.wr.opcode = IB_WR_RDMA_WRITE_WITH_IMM,
.wr.ex.imm_data = cpu_to_be32(imm_data),
.wr.send_flags = flags,
};
/*
* If one of the sges has 0 size, the operation will fail with a
* length error
*/
for (i = 0; i < num_sge; i++)
if (WARN_ONCE(sge[i].length == 0, "sg %d is zero length\n", i))
return -EINVAL;
return rtrs_post_send(con->qp, head, &wr.wr, tail);
}
EXPORT_SYMBOL_GPL(rtrs_iu_post_rdma_write_imm);
static int rtrs_post_rdma_write_imm_empty(struct rtrs_con *con,
struct ib_cqe *cqe,
u32 imm_data,
struct ib_send_wr *head)
{
struct ib_rdma_wr wr;
struct rtrs_path *path = con->path;
enum ib_send_flags sflags;
atomic_dec_if_positive(&con->sq_wr_avail);
sflags = (atomic_inc_return(&con->wr_cnt) % path->signal_interval) ?
0 : IB_SEND_SIGNALED;
wr = (struct ib_rdma_wr) {
.wr.wr_cqe = cqe,
.wr.send_flags = sflags,
.wr.opcode = IB_WR_RDMA_WRITE_WITH_IMM,
.wr.ex.imm_data = cpu_to_be32(imm_data),
};
return rtrs_post_send(con->qp, head, &wr.wr, NULL);
}
static void qp_event_handler(struct ib_event *ev, void *ctx)
{
struct rtrs_con *con = ctx;
switch (ev->event) {
case IB_EVENT_COMM_EST:
rtrs_info(con->path, "QP event %s (%d) received\n",
ib_event_msg(ev->event), ev->event);
rdma_notify(con->cm_id, IB_EVENT_COMM_EST);
break;
default:
rtrs_info(con->path, "Unhandled QP event %s (%d) received\n",
ib_event_msg(ev->event), ev->event);
break;
}
}
static bool is_pollqueue(struct rtrs_con *con)
{
return con->cid >= con->path->irq_con_num;
}
static int create_cq(struct rtrs_con *con, int cq_vector, int nr_cqe,
enum ib_poll_context poll_ctx)
{
struct rdma_cm_id *cm_id = con->cm_id;
struct ib_cq *cq;
if (is_pollqueue(con))
cq = ib_alloc_cq(cm_id->device, con, nr_cqe, cq_vector,
poll_ctx);
else
cq = ib_cq_pool_get(cm_id->device, nr_cqe, cq_vector, poll_ctx);
if (IS_ERR(cq)) {
rtrs_err(con->path, "Creating completion queue failed, errno: %pe\n",
cq);
return PTR_ERR(cq);
}
con->cq = cq;
con->nr_cqe = nr_cqe;
return 0;
}
static int create_qp(struct rtrs_con *con, struct ib_pd *pd,
u32 max_send_wr, u32 max_recv_wr, u32 max_sge)
{
struct ib_qp_init_attr init_attr = {};
struct rdma_cm_id *cm_id = con->cm_id;
int ret;
init_attr.cap.max_send_wr = max_send_wr;
init_attr.cap.max_recv_wr = max_recv_wr;
init_attr.cap.max_recv_sge = 1;
init_attr.event_handler = qp_event_handler;
init_attr.qp_context = con;
init_attr.cap.max_send_sge = max_sge;
init_attr.qp_type = IB_QPT_RC;
init_attr.send_cq = con->cq;
init_attr.recv_cq = con->cq;
init_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
ret = rdma_create_qp(cm_id, pd, &init_attr);
if (ret) {
rtrs_err(con->path, "Creating QP failed, err: %pe\n",
ERR_PTR(ret));
return ret;
}
con->qp = cm_id->qp;
return ret;
}
static void destroy_cq(struct rtrs_con *con)
{
if (con->cq) {
if (is_pollqueue(con))
ib_free_cq(con->cq);
else
ib_cq_pool_put(con->cq, con->nr_cqe);
}
con->cq = NULL;
}
int rtrs_cq_qp_create(struct rtrs_path *path, struct rtrs_con *con,
u32 max_send_sge, int cq_vector, int nr_cqe,
u32 max_send_wr, u32 max_recv_wr,
enum ib_poll_context poll_ctx)
{
int err;
err = create_cq(con, cq_vector, nr_cqe, poll_ctx);
if (err)
return err;
err = create_qp(con, path->dev->ib_pd, max_send_wr, max_recv_wr,
max_send_sge);
if (err) {
destroy_cq(con);
return err;
}
con->path = path;
return 0;
}
EXPORT_SYMBOL_GPL(rtrs_cq_qp_create);
void rtrs_cq_qp_destroy(struct rtrs_con *con)
{
if (con->qp) {
rdma_destroy_qp(con->cm_id);
con->qp = NULL;
}
destroy_cq(con);
}
EXPORT_SYMBOL_GPL(rtrs_cq_qp_destroy);
static void schedule_hb(struct rtrs_path *path)
{
queue_delayed_work(path->hb_wq, &path->hb_dwork,
msecs_to_jiffies(path->hb_interval_ms));
}
void rtrs_send_hb_ack(struct rtrs_path *path)
{
struct rtrs_con *usr_con = path->con[0];
u32 imm;
int err;
imm = rtrs_to_imm(RTRS_HB_ACK_IMM, 0);
err = rtrs_post_rdma_write_imm_empty(usr_con, path->hb_cqe, imm,
NULL);
if (err) {
rtrs_err(path, "send HB ACK failed, errno: %pe\n",
ERR_PTR(err));
path->hb_err_handler(usr_con);
return;
}
}
EXPORT_SYMBOL_GPL(rtrs_send_hb_ack);
static void hb_work(struct work_struct *work)
{
struct rtrs_con *usr_con;
struct rtrs_path *path;
u32 imm;
int err;
path = container_of(to_delayed_work(work), typeof(*path), hb_dwork);
usr_con = path->con[0];
if (path->hb_missed_cnt > path->hb_missed_max) {
rtrs_err(path, "HB missed max reached.\n");
path->hb_err_handler(usr_con);
return;
}
if (path->hb_missed_cnt++) {
/* Reschedule work without sending hb */
schedule_hb(path);
return;
}
path->hb_last_sent = ktime_get();
imm = rtrs_to_imm(RTRS_HB_MSG_IMM, 0);
err = rtrs_post_rdma_write_imm_empty(usr_con, path->hb_cqe, imm,
NULL);
if (err) {
rtrs_err(path, "HB send failed, errno: %pe\n",
ERR_PTR(err));
path->hb_err_handler(usr_con);
return;
}
schedule_hb(path);
}
void rtrs_init_hb(struct rtrs_path *path, struct ib_cqe *cqe,
unsigned int interval_ms, unsigned int missed_max,
void (*err_handler)(struct rtrs_con *con),
struct workqueue_struct *wq)
{
path->hb_cqe = cqe;
path->hb_interval_ms = interval_ms;
path->hb_err_handler = err_handler;
path->hb_wq = wq;
path->hb_missed_max = missed_max;
path->hb_missed_cnt = 0;
INIT_DELAYED_WORK(&path->hb_dwork, hb_work);
}
EXPORT_SYMBOL_GPL(rtrs_init_hb);
void rtrs_start_hb(struct rtrs_path *path)
{
schedule_hb(path);
}
EXPORT_SYMBOL_GPL(rtrs_start_hb);
void rtrs_stop_hb(struct rtrs_path *path)
{
cancel_delayed_work_sync(&path->hb_dwork);
path->hb_missed_cnt = 0;
}
EXPORT_SYMBOL_GPL(rtrs_stop_hb);
static int rtrs_str_gid_to_sockaddr(const char *addr, size_t len,
short port, struct sockaddr_storage *dst)
{
struct sockaddr_ib *dst_ib = (struct sockaddr_ib *)dst;
int ret;
/*
* We can use some of the IPv6 functions since GID is a valid
* IPv6 address format
*/
ret = in6_pton(addr, len, dst_ib->sib_addr.sib_raw, '\0', NULL);
if (ret == 0)
return -EINVAL;
dst_ib->sib_family = AF_IB;
/*
* Use the same TCP server port number as the IB service ID
* on the IB port space range
*/
dst_ib->sib_sid = cpu_to_be64(RDMA_IB_IP_PS_IB | port);
dst_ib->sib_sid_mask = cpu_to_be64(0xffffffffffffffffULL);
dst_ib->sib_pkey = cpu_to_be16(0xffff);
return 0;
}
/**
* rtrs_str_to_sockaddr() - Convert rtrs address string to sockaddr
* @addr: String representation of an addr (IPv4, IPv6 or IB GID):
* - "ip:192.168.1.1"
* - "ip:fe80::200:5aee:feaa:20a2"
* - "gid:fe80::200:5aee:feaa:20a2"
* @len: String address length
* @port: Destination port
* @dst: Destination sockaddr structure
*
* Returns 0 if conversion successful. Non-zero on error.
*/
static int rtrs_str_to_sockaddr(const char *addr, size_t len,
u16 port, struct sockaddr_storage *dst)
{
if (strncmp(addr, "gid:", 4) == 0) {
return rtrs_str_gid_to_sockaddr(addr + 4, len - 4, port, dst);
} else if (strncmp(addr, "ip:", 3) == 0) {
char port_str[8];
char *cpy;
int err;
snprintf(port_str, sizeof(port_str), "%u", port);
cpy = kstrndup(addr + 3, len - 3, GFP_KERNEL);
err = cpy ? inet_pton_with_scope(&init_net, AF_UNSPEC,
cpy, port_str, dst) : -ENOMEM;
kfree(cpy);
return err;
}
return -EPROTONOSUPPORT;
}
/**
* sockaddr_to_str() - convert sockaddr to a string.
* @addr: the sockadddr structure to be converted.
* @buf: string containing socket addr.
* @len: string length.
*
* The return value is the number of characters written into buf not
* including the trailing '\0'. If len is == 0 the function returns 0..
*/
int sockaddr_to_str(const struct sockaddr *addr, char *buf, size_t len)
{
switch (addr->sa_family) {
case AF_IB:
return scnprintf(buf, len, "gid:%pI6",
&((struct sockaddr_ib *)addr)->sib_addr.sib_raw);
case AF_INET:
return scnprintf(buf, len, "ip:%pI4",
&((struct sockaddr_in *)addr)->sin_addr);
case AF_INET6:
return scnprintf(buf, len, "ip:%pI6c",
&((struct sockaddr_in6 *)addr)->sin6_addr);
}
return scnprintf(buf, len, "<invalid address family>");
}
EXPORT_SYMBOL(sockaddr_to_str);
/**
* rtrs_addr_to_str() - convert rtrs_addr to a string "src@dst"
* @addr: the rtrs_addr structure to be converted
* @buf: string containing source and destination addr of a path
* separated by '@' I.e. "ip:1.1.1.1@ip:1.1.1.2"
* "ip:1.1.1.1@ip:1.1.1.2".
* @len: string length
*
* The return value is the number of characters written into buf not
* including the trailing '\0'.
*/
int rtrs_addr_to_str(const struct rtrs_addr *addr, char *buf, size_t len)
{
int cnt;
cnt = sockaddr_to_str((struct sockaddr *)addr->src,
buf, len);
cnt += scnprintf(buf + cnt, len - cnt, "@");
sockaddr_to_str((struct sockaddr *)addr->dst,
buf + cnt, len - cnt);
return cnt;
}
EXPORT_SYMBOL(rtrs_addr_to_str);
/**
* rtrs_addr_to_sockaddr() - convert path string "src,dst" or "src@dst"
* to sockaddreses
* @str: string containing source and destination addr of a path
* separated by ',' or '@' I.e. "ip:1.1.1.1,ip:1.1.1.2" or
* "ip:1.1.1.1@ip:1.1.1.2". If str contains only one address it's
* considered to be destination.
* @len: string length
* @port: Destination port number.
* @addr: will be set to the source/destination address or to NULL
* if str doesn't contain any source address.
*
* Returns zero if conversion successful. Non-zero otherwise.
*/
int rtrs_addr_to_sockaddr(const char *str, size_t len, u16 port,
struct rtrs_addr *addr)
{
const char *d;
d = strchr(str, ',');
if (!d)
d = strchr(str, '@');
if (d) {
if (rtrs_str_to_sockaddr(str, d - str, 0, addr->src))
return -EINVAL;
d += 1;
len -= d - str;
str = d;
} else {
addr->src = NULL;
}
return rtrs_str_to_sockaddr(str, len, port, addr->dst);
}
EXPORT_SYMBOL(rtrs_addr_to_sockaddr);
void rtrs_rdma_dev_pd_init(enum ib_pd_flags pd_flags,
struct rtrs_rdma_dev_pd *pool)
{
INIT_LIST_HEAD(&pool->list);
mutex_init(&pool->mutex);
pool->pd_flags = pd_flags;
}
EXPORT_SYMBOL(rtrs_rdma_dev_pd_init);
void rtrs_rdma_dev_pd_deinit(struct rtrs_rdma_dev_pd *pool)
{
mutex_destroy(&pool->mutex);
WARN_ON(!list_empty(&pool->list));
}
EXPORT_SYMBOL(rtrs_rdma_dev_pd_deinit);
static void dev_free(struct kref *ref)
{
struct rtrs_rdma_dev_pd *pool;
struct rtrs_ib_dev *dev;
dev = container_of(ref, typeof(*dev), ref);
pool = dev->pool;
mutex_lock(&pool->mutex);
list_del(&dev->entry);
mutex_unlock(&pool->mutex);
if (pool->ops && pool->ops->deinit)
pool->ops->deinit(dev);
ib_dealloc_pd(dev->ib_pd);
kfree(dev);
}
int rtrs_ib_dev_put(struct rtrs_ib_dev *dev)
{
return kref_put(&dev->ref, dev_free);
}
EXPORT_SYMBOL(rtrs_ib_dev_put);
static int rtrs_ib_dev_get(struct rtrs_ib_dev *dev)
{
return kref_get_unless_zero(&dev->ref);
}
struct rtrs_ib_dev *
rtrs_ib_dev_find_or_add(struct ib_device *ib_dev,
struct rtrs_rdma_dev_pd *pool)
{
struct rtrs_ib_dev *dev;
mutex_lock(&pool->mutex);
list_for_each_entry(dev, &pool->list, entry) {
if (dev->ib_dev->node_guid == ib_dev->node_guid &&
rtrs_ib_dev_get(dev))
goto out_unlock;
}
mutex_unlock(&pool->mutex);
dev = kzalloc_obj(*dev);
if (!dev)
goto out_err;
kref_init(&dev->ref);
dev->pool = pool;
dev->ib_dev = ib_dev;
dev->ib_pd = ib_alloc_pd(ib_dev, pool->pd_flags);
if (IS_ERR(dev->ib_pd))
goto out_free_dev;
if (pool->ops && pool->ops->init && pool->ops->init(dev))
goto out_free_pd;
mutex_lock(&pool->mutex);
list_add(&dev->entry, &pool->list);
out_unlock:
mutex_unlock(&pool->mutex);
return dev;
out_free_pd:
ib_dealloc_pd(dev->ib_pd);
out_free_dev:
kfree(dev);
out_err:
return NULL;
}
EXPORT_SYMBOL(rtrs_ib_dev_find_or_add);
@@ -0,0 +1,194 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* RDMA Transport Layer
*
* Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
* Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
* Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
*/
#ifndef RTRS_H
#define RTRS_H
#include <linux/socket.h>
#include <linux/scatterlist.h>
struct rtrs_permit;
struct rtrs_clt_sess;
struct rtrs_srv_ctx;
struct rtrs_srv_sess;
struct rtrs_srv_op;
/*
* RDMA transport (RTRS) client API
*/
/**
* enum rtrs_clt_link_ev - Events about connectivity state of a client
* @RTRS_CLT_LINK_EV_RECONNECTED: Client was reconnected.
* @RTRS_CLT_LINK_EV_DISCONNECTED: Client was disconnected.
*/
enum rtrs_clt_link_ev {
RTRS_CLT_LINK_EV_RECONNECTED,
RTRS_CLT_LINK_EV_DISCONNECTED,
};
/**
* struct rtrs_addr - Source and destination address of a path to be established
* @src: source address
* @dst: destination address
*/
struct rtrs_addr {
struct sockaddr_storage *src;
struct sockaddr_storage *dst;
};
/**
* struct rtrs_clt_ops - it holds the link event callback and private pointer.
* @priv: User supplied private data.
* @link_ev: Event notification callback function for connection state changes
* @priv: User supplied data that was passed to rtrs_clt_open()
* @ev: Occurred event
*/
struct rtrs_clt_ops {
void *priv;
void (*link_ev)(void *priv, enum rtrs_clt_link_ev ev);
};
struct rtrs_clt_sess *rtrs_clt_open(struct rtrs_clt_ops *ops,
const char *pathname,
const struct rtrs_addr *paths,
size_t path_cnt, u16 port,
size_t pdu_sz, u8 reconnect_delay_sec,
s16 max_reconnect_attempts, u32 nr_poll_queues);
void rtrs_clt_close(struct rtrs_clt_sess *clt);
enum wait_type {
RTRS_PERMIT_NOWAIT = 0,
RTRS_PERMIT_WAIT = 1
};
/**
* enum rtrs_clt_con_type - type of ib connection to use with a given
* rtrs_permit
* @RTRS_ADMIN_CON: use connection reserved for "service" messages
* @RTRS_IO_CON: use a connection reserved for IO
*/
enum rtrs_clt_con_type {
RTRS_ADMIN_CON,
RTRS_IO_CON
};
struct rtrs_permit *rtrs_clt_get_permit(struct rtrs_clt_sess *sess,
enum rtrs_clt_con_type con_type,
enum wait_type wait);
void rtrs_clt_put_permit(struct rtrs_clt_sess *sess,
struct rtrs_permit *permit);
/**
* struct rtrs_clt_req_ops - it holds the request confirmation callback
* and a private pointer.
* @priv: User supplied private data.
* @conf_fn: callback function to be called as confirmation
* @priv: User provided data, passed back with corresponding
* @(conf) confirmation.
* @errno: error number.
*/
struct rtrs_clt_req_ops {
void *priv;
void (*conf_fn)(void *priv, int errno);
};
int rtrs_clt_request(int dir, struct rtrs_clt_req_ops *ops,
struct rtrs_clt_sess *sess, struct rtrs_permit *permit,
const struct kvec *vec, size_t nr, size_t len,
struct scatterlist *sg, unsigned int sg_cnt);
int rtrs_clt_rdma_cq_direct(struct rtrs_clt_sess *clt, unsigned int index);
/**
* struct rtrs_attrs - RTRS session attributes
* @queue_depth: queue_depth saved from rtrs_clt_sess message
* @max_io_size: max_io_size from rtrs_clt_sess message, capped to
* @max_segments * %SZ_4K
* @max_segments: max_segments saved from rtrs_clt_sess message
*/
struct rtrs_attrs {
u32 queue_depth;
u32 max_io_size;
u32 max_segments;
};
int rtrs_clt_query(struct rtrs_clt_sess *sess, struct rtrs_attrs *attr);
/*
* Here goes RTRS server API
*/
/**
* enum rtrs_srv_link_ev - Server link events
* @RTRS_SRV_LINK_EV_CONNECTED: Connection from client established
* @RTRS_SRV_LINK_EV_DISCONNECTED: Connection was disconnected, all
* connection RTRS resources were freed.
*/
enum rtrs_srv_link_ev {
RTRS_SRV_LINK_EV_CONNECTED,
RTRS_SRV_LINK_EV_DISCONNECTED,
};
struct rtrs_srv_ops {
/**
* rdma_ev(): Event notification for RDMA operations
* If the callback returns a value != 0, an error
* message for the data transfer will be sent to
* the client.
* @priv: Private data set by rtrs_srv_set_sess_priv()
* @id: internal RTRS operation id
* @data: Pointer to (bidirectional) rdma memory area:
* - in case of %RTRS_SRV_RDMA_EV_RECV contains
* data sent by the client
* - in case of %RTRS_SRV_RDMA_EV_WRITE_REQ points
* to the memory area where the response is to be
* written to
* @datalen: Size of the memory area in @data
* @usr: The extra user message sent by the client (%vec)
* @usrlen: Size of the user message
*/
int (*rdma_ev)(void *priv,
struct rtrs_srv_op *id,
void *data, size_t datalen, const void *usr,
size_t usrlen);
/**
* link_ev(): Events about connectivity state changes
* If the callback returns != 0 and the event
* %RTRS_SRV_LINK_EV_CONNECTED the corresponding
* session will be destroyed.
* @sess: Session
* @ev: event
* @priv: Private data from user if previously set with
* rtrs_srv_set_sess_priv()
*/
int (*link_ev)(struct rtrs_srv_sess *sess, enum rtrs_srv_link_ev ev,
void *priv);
};
struct rtrs_srv_ctx *rtrs_srv_open(struct rtrs_srv_ops *ops, u16 port);
void rtrs_srv_close(struct rtrs_srv_ctx *ctx);
bool rtrs_srv_resp_rdma(struct rtrs_srv_op *id, int errno);
void rtrs_srv_set_sess_priv(struct rtrs_srv_sess *sess, void *priv);
int rtrs_srv_get_path_name(struct rtrs_srv_sess *sess, char *pathname,
size_t len);
int rtrs_srv_get_queue_depth(struct rtrs_srv_sess *sess);
int rtrs_addr_to_sockaddr(const char *str, size_t len, u16 port,
struct rtrs_addr *addr);
int sockaddr_to_str(const struct sockaddr *addr, char *buf, size_t len);
int rtrs_addr_to_str(const struct rtrs_addr *addr, char *buf, size_t len);
#endif