// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2012,2016,2017,2025 Stefan Metzmacher
*/
#include "internal.h"
#include "../common/smb2status.h"
static int smbdirect_connect_setup_connection(struct smbdirect_socket *sc);
static int smbdirect_connect_resolve_addr(struct smbdirect_socket *sc,
const struct sockaddr *src,
const struct sockaddr *dst);
static int smbdirect_connect_rdma_event_handler(struct rdma_cm_id *id,
struct rdma_cm_event *event);
static int smbdirect_connect_negotiate_start(struct smbdirect_socket *sc);
static void smbdirect_connect_negotiate_send_done(struct ib_cq *cq, struct ib_wc *wc);
static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc *wc);
int smbdirect_connect(struct smbdirect_socket *sc, const struct sockaddr *dst)
{
const struct sockaddr *src = NULL;
union {
struct sockaddr sa;
struct sockaddr_storage ss;
} src_addr = {
.sa = {
.sa_family = AF_UNSPEC,
},
};
int ret;
if (sc->first_error)
return -ENOTCONN;
if (sc->status != SMBDIRECT_SOCKET_CREATED)
return -EALREADY;
if (WARN_ON_ONCE(!sc->rdma.cm_id))
return -EINVAL;
src_addr.ss = sc->rdma.cm_id->route.addr.src_addr;
if (src_addr.sa.sa_family != AF_UNSPEC)
src = &src_addr.sa;
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
"connect: src: %pISpsfc dst: %pISpsfc\n",
src, dst);
ret = smbdirect_connect_setup_connection(sc);
if (ret)
return ret;
ret = smbdirect_connect_resolve_addr(sc, src, dst);
if (ret)
return ret;
/*
* The rest happens async via smbdirect_connect_rdma_event_handler()
* the caller will decide to wait or not.
*/
return 0;
}
EXPORT_SYMBOL_GPL(smbdirect_connect);
static int smbdirect_connect_setup_connection(struct smbdirect_socket *sc)
{
rdma_lock_handler(sc->rdma.cm_id);
sc->rdma.cm_id->event_handler = smbdirect_connect_rdma_event_handler;
rdma_unlock_handler(sc->rdma.cm_id);
if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_CREATED))
return -EINVAL;
sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED;
return 0;
}
static int smbdirect_connect_resolve_addr(struct smbdirect_socket *sc,
const struct sockaddr *src,
const struct sockaddr *dst)
{
const struct smbdirect_socket_parameters *sp = &sc->parameters;
struct sockaddr *src_addr = NULL;
struct sockaddr *dst_addr = NULL;
int ret;
src_addr = (struct sockaddr *)src;
if (src_addr && src_addr->sa_family == AF_UNSPEC)
src_addr = NULL;
dst_addr = (struct sockaddr *)dst;
if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED))
return -EINVAL;
sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING;
sc->rdma.expected_event = RDMA_CM_EVENT_ADDR_RESOLVED;
ret = rdma_resolve_addr(sc->rdma.cm_id, src_addr, dst_addr,
sp->resolve_addr_timeout_msec);
if (ret) {
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"rdma_resolve_addr() failed %1pe\n",
SMBDIRECT_DEBUG_ERR_PTR(ret));
return ret;
}
return 0;
}
static int smbdirect_connect_resolve_route(struct smbdirect_socket *sc)
{
const struct smbdirect_socket_parameters *sp = &sc->parameters;
int ret;
if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED))
return sc->first_error;
sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING;
sc->rdma.expected_event = RDMA_CM_EVENT_ROUTE_RESOLVED;
ret = rdma_resolve_route(sc->rdma.cm_id, sp->resolve_route_timeout_msec);
if (ret) {
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"rdma_resolve_route() failed %1pe\n",
SMBDIRECT_DEBUG_ERR_PTR(ret));
return ret;
}
return 0;
}
static int smbdirect_connect_rdma_connect(struct smbdirect_socket *sc)
{
struct smbdirect_socket_parameters *sp = &sc->parameters;
struct rdma_conn_param conn_param;
__be32 ird_ord_hdr[2];
int ret;
sc->ib.dev = sc->rdma.cm_id->device;
if (!smbdirect_frwr_is_supported(