mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 07:33:19 +02:00
smb: smbdirect: introduce smbdirect_socket_{listen,accept}()
These will be used by the server soon instead of using smbdirect_accept_connect_request() together with rdma_listen(). Cc: Steve French <smfrench@gmail.com> Cc: Tom Talpey <tom@talpey.com> Cc: Long Li <longli@microsoft.com> Cc: Namjae Jeon <linkinjeon@kernel.org> Cc: linux-cifs@vger.kernel.org Cc: samba-technical@lists.samba.org Signed-off-by: Stefan Metzmacher <metze@samba.org> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
03f9e2c15f
commit
dc691b91ad
|
|
@ -244,6 +244,7 @@ static void smbd_disconnect_rdma_work(struct work_struct *work)
|
|||
break;
|
||||
|
||||
case SMBDIRECT_SOCKET_CREATED:
|
||||
case SMBDIRECT_SOCKET_LISTENING:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
|
||||
|
|
@ -323,6 +324,7 @@ static void smbd_disconnect_rdma_connection(struct smbdirect_socket *sc)
|
|||
break;
|
||||
|
||||
case SMBDIRECT_SOCKET_CREATED:
|
||||
case SMBDIRECT_SOCKET_LISTENING:
|
||||
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include <net/sock.h>
|
||||
#include "../../common/smb2status.h"
|
||||
|
||||
static int smbdirect_accept_rdma_event_handler(struct rdma_cm_id *id,
|
||||
|
|
@ -454,6 +455,28 @@ static void smbdirect_accept_negotiate_recv_work(struct work_struct *work)
|
|||
*/
|
||||
sp->max_fragmented_send_size = max_fragmented_size;
|
||||
|
||||
if (sc->accept.listener) {
|
||||
struct smbdirect_socket *lsc = sc->accept.listener;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&lsc->listen.lock, flags);
|
||||
list_del(&sc->accept.list);
|
||||
list_add_tail(&sc->accept.list, &lsc->listen.ready);
|
||||
wake_up(&lsc->listen.wait_queue);
|
||||
spin_unlock_irqrestore(&lsc->listen.lock, flags);
|
||||
|
||||
/*
|
||||
* smbdirect_socket_accept() will call
|
||||
* smbdirect_accept_negotiate_finish(nsc, 0);
|
||||
*
|
||||
* So that we don't send the negotiation
|
||||
* response that grants credits to the peer
|
||||
* before the socket is accepted by the
|
||||
* application.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
ntstatus = le32_to_cpu(STATUS_SUCCESS);
|
||||
|
||||
not_supported:
|
||||
|
|
@ -748,3 +771,90 @@ static int smbdirect_accept_rdma_event_handler(struct rdma_cm_id *id,
|
|||
smbdirect_socket_schedule_cleanup(sc, -ECONNABORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long smbdirect_socket_wait_for_accept(struct smbdirect_socket *lsc, long timeo)
|
||||
{
|
||||
long ret;
|
||||
|
||||
ret = wait_event_interruptible_timeout(lsc->listen.wait_queue,
|
||||
!list_empty_careful(&lsc->listen.ready) ||
|
||||
lsc->status != SMBDIRECT_SOCKET_LISTENING ||
|
||||
lsc->first_error,
|
||||
timeo);
|
||||
if (lsc->status != SMBDIRECT_SOCKET_LISTENING)
|
||||
return -EINVAL;
|
||||
if (lsc->first_error)
|
||||
return lsc->first_error;
|
||||
if (!ret)
|
||||
ret = -ETIMEDOUT;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__SMBDIRECT_PUBLIC__
|
||||
struct smbdirect_socket *smbdirect_socket_accept(struct smbdirect_socket *lsc,
|
||||
long timeo,
|
||||
struct proto_accept_arg *arg)
|
||||
{
|
||||
struct smbdirect_socket *nsc;
|
||||
unsigned long flags;
|
||||
|
||||
if (lsc->status != SMBDIRECT_SOCKET_LISTENING) {
|
||||
arg->err = -EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lsc->first_error) {
|
||||
arg->err = lsc->first_error;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (list_empty_careful(&lsc->listen.ready)) {
|
||||
int ret;
|
||||
|
||||
if (timeo == 0) {
|
||||
arg->err = -EAGAIN;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = smbdirect_socket_wait_for_accept(lsc, timeo);
|
||||
if (ret) {
|
||||
arg->err = ret;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&lsc->listen.lock, flags);
|
||||
nsc = list_first_entry_or_null(&lsc->listen.ready,
|
||||
struct smbdirect_socket,
|
||||
accept.list);
|
||||
if (nsc) {
|
||||
nsc->accept.listener = NULL;
|
||||
list_del_init_careful(&nsc->accept.list);
|
||||
arg->is_empty = list_empty_careful(&lsc->listen.ready);
|
||||
}
|
||||
spin_unlock_irqrestore(&lsc->listen.lock, flags);
|
||||
if (!nsc) {
|
||||
arg->err = -EAGAIN;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We did not send the negotiation response
|
||||
* yet, so we did not grant any credits to the client,
|
||||
* so it didn't grant any credits to us.
|
||||
*
|
||||
* The caller expects a connected socket
|
||||
* now as there are no credits anyway.
|
||||
*
|
||||
* Then we send the negotiation response in
|
||||
* order to grant credits to the peer.
|
||||
*/
|
||||
nsc->status = SMBDIRECT_SOCKET_CONNECTED;
|
||||
smbdirect_accept_negotiate_finish(nsc, 0);
|
||||
|
||||
return nsc;
|
||||
}
|
||||
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_accept);
|
||||
|
|
|
|||
|
|
@ -22,3 +22,4 @@
|
|||
#include "smbdirect_debug.c"
|
||||
#include "smbdirect_connect.c"
|
||||
#include "smbdirect_accept.c"
|
||||
#include "smbdirect_listen.c"
|
||||
|
|
|
|||
|
|
@ -163,6 +163,14 @@ void smbdirect_connection_negotiation_done(struct smbdirect_socket *sc)
|
|||
if (unlikely(sc->first_error))
|
||||
return;
|
||||
|
||||
if (sc->status == SMBDIRECT_SOCKET_CONNECTED)
|
||||
/*
|
||||
* This is the accept case where
|
||||
* smbdirect_socket_accept() already sets
|
||||
* SMBDIRECT_SOCKET_CONNECTED
|
||||
*/
|
||||
goto done;
|
||||
|
||||
if (sc->status != SMBDIRECT_SOCKET_NEGOTIATE_RUNNING) {
|
||||
/*
|
||||
* Something went wrong...
|
||||
|
|
@ -189,6 +197,7 @@ void smbdirect_connection_negotiation_done(struct smbdirect_socket *sc)
|
|||
* We need to setup the refill and send immediate work
|
||||
* in order to get a working connection.
|
||||
*/
|
||||
done:
|
||||
INIT_WORK(&sc->recv_io.posted.refill_work, smbdirect_connection_recv_io_refill_work);
|
||||
INIT_WORK(&sc->idle.immediate_work, smbdirect_connection_send_immediate_work);
|
||||
|
||||
|
|
|
|||
309
fs/smb/common/smbdirect/smbdirect_listen.c
Normal file
309
fs/smb/common/smbdirect/smbdirect_listen.c
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2017, Microsoft Corporation.
|
||||
* Copyright (C) 2018, LG Electronics.
|
||||
* Copyright (c) 2025, Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
|
||||
static int smbdirect_listen_rdma_event_handler(struct rdma_cm_id *id,
|
||||
struct rdma_cm_event *event);
|
||||
|
||||
__SMBDIRECT_PUBLIC__
|
||||
int smbdirect_socket_listen(struct smbdirect_socket *sc, int backlog)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (backlog < 0)
|
||||
return -EINVAL;
|
||||
if (!backlog)
|
||||
backlog = 1; /* use 1 as default for now */
|
||||
|
||||
if (sc->first_error)
|
||||
return -EINVAL;
|
||||
|
||||
if (sc->status != SMBDIRECT_SOCKET_CREATED)
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON_ONCE(!sc->rdma.cm_id))
|
||||
return -EINVAL;
|
||||
|
||||
if (sc->rdma.cm_id->device)
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"try to listen on addr: %pISpsfc dev: %.*s\n",
|
||||
&sc->rdma.cm_id->route.addr.src_addr,
|
||||
IB_DEVICE_NAME_MAX,
|
||||
sc->rdma.cm_id->device->name);
|
||||
else
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"try to listen on addr: %pISpsfc\n",
|
||||
&sc->rdma.cm_id->route.addr.src_addr);
|
||||
|
||||
/* already checked above */
|
||||
WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_CREATED);
|
||||
sc->status = SMBDIRECT_SOCKET_LISTENING;
|
||||
sc->rdma.expected_event = RDMA_CM_EVENT_CONNECT_REQUEST;
|
||||
rdma_lock_handler(sc->rdma.cm_id);
|
||||
sc->rdma.cm_id->event_handler = smbdirect_listen_rdma_event_handler;
|
||||
rdma_unlock_handler(sc->rdma.cm_id);
|
||||
|
||||
ret = rdma_listen(sc->rdma.cm_id, backlog);
|
||||
if (ret) {
|
||||
sc->first_error = ret;
|
||||
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
|
||||
if (sc->rdma.cm_id->device)
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"listening failed %1pe on addr: %pISpsfc dev: %.*s\n",
|
||||
SMBDIRECT_DEBUG_ERR_PTR(ret),
|
||||
&sc->rdma.cm_id->route.addr.src_addr,
|
||||
IB_DEVICE_NAME_MAX,
|
||||
sc->rdma.cm_id->device->name);
|
||||
else
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"listening failed %1pe on addr: %pISpsfc\n",
|
||||
SMBDIRECT_DEBUG_ERR_PTR(ret),
|
||||
&sc->rdma.cm_id->route.addr.src_addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a value > 0, checked above,
|
||||
* so we are able to use sc->listen.backlog == -1,
|
||||
* as indication that the socket was never
|
||||
* a listener.
|
||||
*/
|
||||
sc->listen.backlog = backlog;
|
||||
|
||||
if (sc->rdma.cm_id->device)
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"listening on addr: %pISpsfc dev: %.*s\n",
|
||||
&sc->rdma.cm_id->route.addr.src_addr,
|
||||
IB_DEVICE_NAME_MAX,
|
||||
sc->rdma.cm_id->device->name);
|
||||
else
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"listening on addr: %pISpsfc\n",
|
||||
&sc->rdma.cm_id->route.addr.src_addr);
|
||||
|
||||
/*
|
||||
* The rest happens async via smbdirect_listen_rdma_event_handler()
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_listen);
|
||||
|
||||
static int smbdirect_new_rdma_event_handler(struct rdma_cm_id *new_id,
|
||||
struct rdma_cm_event *event)
|
||||
{
|
||||
int ret = -ESTALE;
|
||||
|
||||
/*
|
||||
* This should be replaced before any real work
|
||||
* starts! So it should never be called!
|
||||
*/
|
||||
|
||||
if (event->event == RDMA_CM_EVENT_DEVICE_REMOVAL)
|
||||
ret = -ENETDOWN;
|
||||
if (IS_ERR(SMBDIRECT_DEBUG_ERR_PTR(event->status)))
|
||||
ret = event->status;
|
||||
WARN_ONCE(1,
|
||||
"%s should not be called! event=%s status=%d => ret=%1pe\n",
|
||||
__func__,
|
||||
rdma_event_msg(event->event),
|
||||
event->status,
|
||||
SMBDIRECT_DEBUG_ERR_PTR(ret));
|
||||
return -ESTALE;
|
||||
}
|
||||
|
||||
static int smbdirect_listen_connect_request(struct smbdirect_socket *lsc,
|
||||
struct rdma_cm_id *new_id,
|
||||
const struct rdma_cm_event *event);
|
||||
|
||||
static int smbdirect_listen_rdma_event_handler(struct rdma_cm_id *new_id,
|
||||
struct rdma_cm_event *event)
|
||||
{
|
||||
struct smbdirect_socket *lsc = new_id->context;
|
||||
int ret;
|
||||
|
||||
if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) {
|
||||
new_id->context = NULL;
|
||||
new_id->event_handler = smbdirect_new_rdma_event_handler;
|
||||
} else
|
||||
new_id = NULL;
|
||||
|
||||
/*
|
||||
* cma_cm_event_handler() has
|
||||
* lockdep_assert_held(&id_priv->handler_mutex);
|
||||
*
|
||||
* Mutexes are not allowed in interrupts,
|
||||
* and we rely on not being in an interrupt here,
|
||||
* as we might sleep.
|
||||
*/
|
||||
WARN_ON_ONCE(in_interrupt());
|
||||
|
||||
if (event->status || event->event != lsc->rdma.expected_event) {
|
||||
ret = -ECONNABORTED;
|
||||
|
||||
if (event->event == RDMA_CM_EVENT_DEVICE_REMOVAL)
|
||||
ret = -ENETDOWN;
|
||||
if (IS_ERR(SMBDIRECT_DEBUG_ERR_PTR(event->status)))
|
||||
ret = event->status;
|
||||
|
||||
smbdirect_log_rdma_event(lsc, SMBDIRECT_LOG_ERR,
|
||||
"%s (first_error=%1pe, expected=%s) => event=%s status=%d => ret=%1pe\n",
|
||||
smbdirect_socket_status_string(lsc->status),
|
||||
SMBDIRECT_DEBUG_ERR_PTR(lsc->first_error),
|
||||
rdma_event_msg(lsc->rdma.expected_event),
|
||||
rdma_event_msg(event->event),
|
||||
event->status,
|
||||
SMBDIRECT_DEBUG_ERR_PTR(ret));
|
||||
|
||||
/*
|
||||
* In case of error return it and let the caller
|
||||
* destroy new_id
|
||||
*/
|
||||
smbdirect_socket_schedule_cleanup(lsc, ret);
|
||||
return new_id ? ret : 0;
|
||||
}
|
||||
|
||||
smbdirect_log_rdma_event(lsc, SMBDIRECT_LOG_INFO,
|
||||
"%s (first_error=%1pe) event=%s\n",
|
||||
smbdirect_socket_status_string(lsc->status),
|
||||
SMBDIRECT_DEBUG_ERR_PTR(lsc->first_error),
|
||||
rdma_event_msg(event->event));
|
||||
|
||||
/*
|
||||
* In case of error return it and let the caller
|
||||
* destroy new_id
|
||||
*/
|
||||
if (lsc->first_error)
|
||||
return new_id ? lsc->first_error : 0;
|
||||
|
||||
switch (event->event) {
|
||||
case RDMA_CM_EVENT_CONNECT_REQUEST:
|
||||
WARN_ON_ONCE(lsc->status != SMBDIRECT_SOCKET_LISTENING);
|
||||
|
||||
/*
|
||||
* In case of error return it and let the caller
|
||||
* destroy new_id
|
||||
*/
|
||||
ret = smbdirect_listen_connect_request(lsc, new_id, event);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an internal error
|
||||
*/
|
||||
WARN_ON_ONCE(lsc->rdma.expected_event != RDMA_CM_EVENT_CONNECT_REQUEST);
|
||||
smbdirect_socket_schedule_cleanup(lsc, -EINVAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smbdirect_listen_connect_request(struct smbdirect_socket *lsc,
|
||||
struct rdma_cm_id *new_id,
|
||||
const struct rdma_cm_event *event)
|
||||
{
|
||||
const struct smbdirect_socket_parameters *lsp = &lsc->parameters;
|
||||
struct smbdirect_socket *nsc;
|
||||
unsigned long flags;
|
||||
size_t backlog = max_t(size_t, 1, lsc->listen.backlog);
|
||||
size_t psockets;
|
||||
size_t rsockets;
|
||||
int ret;
|
||||
|
||||
if (!smbdirect_frwr_is_supported(&new_id->device->attrs)) {
|
||||
smbdirect_log_rdma_event(lsc, SMBDIRECT_LOG_ERR,
|
||||
"Fast Registration Work Requests (FRWR) is not supported device %.*s\n",
|
||||
IB_DEVICE_NAME_MAX,
|
||||
new_id->device->name);
|
||||
smbdirect_log_rdma_event(lsc, SMBDIRECT_LOG_ERR,
|
||||
"Device capability flags = %llx max_fast_reg_page_list_len = %u\n",
|
||||
new_id->device->attrs.device_cap_flags,
|
||||
new_id->device->attrs.max_fast_reg_page_list_len);
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
|
||||
if (lsp->flags & SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB &&
|
||||
!rdma_ib_or_roce(new_id->device, new_id->port_num)) {
|
||||
smbdirect_log_rdma_event(lsc, SMBDIRECT_LOG_ERR,
|
||||
"Not IB: device: %.*s IW:%u local: %pISpsfc remote: %pISpsfc\n",
|
||||
IB_DEVICE_NAME_MAX,
|
||||
new_id->device->name,
|
||||
rdma_protocol_iwarp(new_id->device, new_id->port_num),
|
||||
&new_id->route.addr.src_addr,
|
||||
&new_id->route.addr.dst_addr);
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
if (lsp->flags & SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW &&
|
||||
!rdma_protocol_iwarp(new_id->device, new_id->port_num)) {
|
||||
smbdirect_log_rdma_event(lsc, SMBDIRECT_LOG_ERR,
|
||||
"Not IW: device: %.*s IB:%u local: %pISpsfc remote: %pISpsfc\n",
|
||||
IB_DEVICE_NAME_MAX,
|
||||
new_id->device->name,
|
||||
rdma_ib_or_roce(new_id->device, new_id->port_num),
|
||||
&new_id->route.addr.src_addr,
|
||||
&new_id->route.addr.dst_addr);
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&lsc->listen.lock, flags);
|
||||
psockets = list_count_nodes(&lsc->listen.pending);
|
||||
rsockets = list_count_nodes(&lsc->listen.ready);
|
||||
spin_unlock_irqrestore(&lsc->listen.lock, flags);
|
||||
|
||||
if (psockets > backlog ||
|
||||
rsockets > backlog ||
|
||||
(psockets + rsockets) > backlog) {
|
||||
smbdirect_log_rdma_event(lsc, SMBDIRECT_LOG_ERR,
|
||||
"Backlog[%d][%zu] full pending[%zu] ready[%zu]\n",
|
||||
lsc->listen.backlog, backlog, psockets, rsockets);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = smbdirect_socket_create_accepting(new_id, &nsc);
|
||||
if (ret)
|
||||
goto socket_init_failed;
|
||||
|
||||
nsc->logging = lsc->logging;
|
||||
ret = smbdirect_socket_set_initial_parameters(nsc, &lsc->parameters);
|
||||
if (ret)
|
||||
goto set_params_failed;
|
||||
ret = smbdirect_socket_set_kernel_settings(nsc,
|
||||
lsc->ib.poll_ctx,
|
||||
lsc->send_io.mem.gfp_mask);
|
||||
if (ret)
|
||||
goto set_settings_failed;
|
||||
|
||||
spin_lock_irqsave(&lsc->listen.lock, flags);
|
||||
list_add_tail(&nsc->accept.list, &lsc->listen.pending);
|
||||
nsc->accept.listener = lsc;
|
||||
spin_unlock_irqrestore(&lsc->listen.lock, flags);
|
||||
|
||||
ret = smbdirect_accept_connect_request(nsc, &event->param.conn);
|
||||
if (ret)
|
||||
goto accept_connect_failed;
|
||||
|
||||
return 0;
|
||||
|
||||
accept_connect_failed:
|
||||
spin_lock_irqsave(&lsc->listen.lock, flags);
|
||||
list_del_init(&nsc->accept.list);
|
||||
nsc->accept.listener = NULL;
|
||||
spin_unlock_irqrestore(&lsc->listen.lock, flags);
|
||||
set_settings_failed:
|
||||
set_params_failed:
|
||||
/*
|
||||
* The caller will destroy new_id
|
||||
*/
|
||||
nsc->ib.dev = NULL;
|
||||
nsc->rdma.cm_id = NULL;
|
||||
smbdirect_socket_release(nsc);
|
||||
socket_init_failed:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -147,10 +147,18 @@ __SMBDIRECT_PUBLIC__
|
|||
int smbdirect_connect_sync(struct smbdirect_socket *sc,
|
||||
const struct sockaddr *dst);
|
||||
|
||||
__SMBDIRECT_PUBLIC__
|
||||
int smbdirect_socket_listen(struct smbdirect_socket *sc, int backlog);
|
||||
|
||||
__SMBDIRECT_PUBLIC__
|
||||
int smbdirect_accept_connect_request(struct smbdirect_socket *sc,
|
||||
const struct rdma_conn_param *param);
|
||||
|
||||
__SMBDIRECT_PUBLIC__
|
||||
struct smbdirect_socket *smbdirect_socket_accept(struct smbdirect_socket *lsc,
|
||||
long timeo,
|
||||
struct proto_accept_arg *arg);
|
||||
|
||||
__SMBDIRECT_PUBLIC__
|
||||
int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,
|
||||
void *buf, size_t buf_len,
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@ static void smbdirect_socket_wake_up_all(struct smbdirect_socket *sc)
|
|||
* in order to notice the broken connection.
|
||||
*/
|
||||
wake_up_all(&sc->status_wait);
|
||||
wake_up_all(&sc->listen.wait_queue);
|
||||
wake_up_all(&sc->send_io.bcredits.wait_queue);
|
||||
wake_up_all(&sc->send_io.lcredits.wait_queue);
|
||||
wake_up_all(&sc->send_io.credits.wait_queue);
|
||||
|
|
@ -319,6 +320,8 @@ void __smbdirect_socket_schedule_cleanup(struct smbdirect_socket *sc,
|
|||
int error,
|
||||
enum smbdirect_socket_status *force_status)
|
||||
{
|
||||
struct smbdirect_socket *psc, *tsc;
|
||||
unsigned long flags;
|
||||
bool was_first = false;
|
||||
|
||||
if (!sc->first_error) {
|
||||
|
|
@ -351,6 +354,18 @@ void __smbdirect_socket_schedule_cleanup(struct smbdirect_socket *sc,
|
|||
sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE;
|
||||
disable_delayed_work(&sc->idle.timer_work);
|
||||
|
||||
/*
|
||||
* In case we were a listener we need to
|
||||
* disconnect all pending and ready sockets
|
||||
*
|
||||
* First we move ready sockets to pending again.
|
||||
*/
|
||||
spin_lock_irqsave(&sc->listen.lock, flags);
|
||||
list_splice_init(&sc->listen.ready, &sc->listen.pending);
|
||||
list_for_each_entry_safe(psc, tsc, &sc->listen.pending, accept.list)
|
||||
smbdirect_socket_schedule_cleanup(psc, sc->first_error);
|
||||
spin_unlock_irqrestore(&sc->listen.lock, flags);
|
||||
|
||||
switch (sc->status) {
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
|
||||
|
|
@ -386,6 +401,7 @@ void __smbdirect_socket_schedule_cleanup(struct smbdirect_socket *sc,
|
|||
break;
|
||||
|
||||
case SMBDIRECT_SOCKET_CREATED:
|
||||
case SMBDIRECT_SOCKET_LISTENING:
|
||||
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
|
||||
break;
|
||||
|
||||
|
|
@ -410,6 +426,8 @@ static void smbdirect_socket_cleanup_work(struct work_struct *work)
|
|||
{
|
||||
struct smbdirect_socket *sc =
|
||||
container_of(work, struct smbdirect_socket, disconnect_work);
|
||||
struct smbdirect_socket *psc, *tsc;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* This should not never be called in an interrupt!
|
||||
|
|
@ -437,6 +455,18 @@ static void smbdirect_socket_cleanup_work(struct work_struct *work)
|
|||
sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE;
|
||||
disable_delayed_work(&sc->idle.timer_work);
|
||||
|
||||
/*
|
||||
* In case we were a listener we need to
|
||||
* disconnect all pending and ready sockets
|
||||
*
|
||||
* First we move ready sockets to pending again.
|
||||
*/
|
||||
spin_lock_irqsave(&sc->listen.lock, flags);
|
||||
list_splice_init(&sc->listen.ready, &sc->listen.pending);
|
||||
list_for_each_entry_safe(psc, tsc, &sc->listen.pending, accept.list)
|
||||
smbdirect_socket_schedule_cleanup(psc, sc->first_error);
|
||||
spin_unlock_irqrestore(&sc->listen.lock, flags);
|
||||
|
||||
switch (sc->status) {
|
||||
case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
|
||||
case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
|
||||
|
|
@ -448,6 +478,7 @@ static void smbdirect_socket_cleanup_work(struct work_struct *work)
|
|||
break;
|
||||
|
||||
case SMBDIRECT_SOCKET_CREATED:
|
||||
case SMBDIRECT_SOCKET_LISTENING:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
|
||||
|
|
@ -479,6 +510,8 @@ static void smbdirect_socket_cleanup_work(struct work_struct *work)
|
|||
|
||||
static void smbdirect_socket_destroy(struct smbdirect_socket *sc)
|
||||
{
|
||||
struct smbdirect_socket *psc, *tsc;
|
||||
size_t psockets;
|
||||
struct smbdirect_recv_io *recv_io;
|
||||
struct smbdirect_recv_io *recv_tmp;
|
||||
LIST_HEAD(all_list);
|
||||
|
|
@ -502,6 +535,14 @@ static void smbdirect_socket_destroy(struct smbdirect_socket *sc)
|
|||
smbdirect_socket_status_string(sc->status),
|
||||
SMBDIRECT_DEBUG_ERR_PTR(sc->first_error));
|
||||
|
||||
/*
|
||||
* The listener should clear this before we reach this
|
||||
*/
|
||||
WARN_ONCE(sc->accept.listener,
|
||||
"status=%s first_error=%1pe",
|
||||
smbdirect_socket_status_string(sc->status),
|
||||
SMBDIRECT_DEBUG_ERR_PTR(sc->first_error));
|
||||
|
||||
/*
|
||||
* Wake up all waiters in all wait queues
|
||||
* in order to notice the broken connection.
|
||||
|
|
@ -527,9 +568,34 @@ static void smbdirect_socket_destroy(struct smbdirect_socket *sc)
|
|||
ib_drain_qp(sc->ib.qp);
|
||||
}
|
||||
|
||||
/*
|
||||
* In case we were a listener we need to
|
||||
* disconnect all pending and ready sockets
|
||||
*
|
||||
* We move ready sockets to pending again.
|
||||
*/
|
||||
spin_lock_irqsave(&sc->listen.lock, flags);
|
||||
list_splice_tail_init(&sc->listen.ready, &all_list);
|
||||
list_splice_tail_init(&sc->listen.pending, &all_list);
|
||||
spin_unlock_irqrestore(&sc->listen.lock, flags);
|
||||
psockets = list_count_nodes(&all_list);
|
||||
if (sc->listen.backlog != -1) /* was a listener */
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"release %zu pending sockets\n", psockets);
|
||||
list_for_each_entry_safe(psc, tsc, &all_list, accept.list) {
|
||||
list_del_init(&psc->accept.list);
|
||||
psc->accept.listener = NULL;
|
||||
smbdirect_socket_release(psc);
|
||||
}
|
||||
if (sc->listen.backlog != -1) /* was a listener */
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"released %zu pending sockets\n", psockets);
|
||||
INIT_LIST_HEAD(&all_list);
|
||||
|
||||
/* It's not possible for upper layer to get to reassembly */
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"drain the reassembly queue\n");
|
||||
if (sc->listen.backlog == -1) /* was not a listener */
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"drain the reassembly queue\n");
|
||||
spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
|
||||
list_splice_tail_init(&sc->recv_io.reassembly.list, &all_list);
|
||||
spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
|
||||
|
|
@ -537,12 +603,14 @@ static void smbdirect_socket_destroy(struct smbdirect_socket *sc)
|
|||
smbdirect_connection_put_recv_io(recv_io);
|
||||
sc->recv_io.reassembly.data_length = 0;
|
||||
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"freeing mr list\n");
|
||||
if (sc->listen.backlog == -1) /* was not a listener */
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"freeing mr list\n");
|
||||
smbdirect_connection_destroy_mr_list(sc);
|
||||
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"destroying qp\n");
|
||||
if (sc->listen.backlog == -1) /* was not a listener */
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"destroying qp\n");
|
||||
smbdirect_connection_destroy_qp(sc);
|
||||
if (sc->rdma.cm_id) {
|
||||
rdma_unlock_handler(sc->rdma.cm_id);
|
||||
|
|
@ -552,8 +620,9 @@ static void smbdirect_socket_destroy(struct smbdirect_socket *sc)
|
|||
sc->rdma.cm_id = NULL;
|
||||
}
|
||||
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"destroying mem pools\n");
|
||||
if (sc->listen.backlog == -1) /* was not a listener */
|
||||
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
|
||||
"destroying mem pools\n");
|
||||
smbdirect_connection_destroy_mem_pools(sc);
|
||||
|
||||
sc->status = SMBDIRECT_SOCKET_DESTROYED;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
enum smbdirect_socket_status {
|
||||
SMBDIRECT_SOCKET_CREATED,
|
||||
SMBDIRECT_SOCKET_LISTENING,
|
||||
SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED,
|
||||
SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING,
|
||||
SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED,
|
||||
|
|
@ -42,6 +43,8 @@ const char *smbdirect_socket_status_string(enum smbdirect_socket_status status)
|
|||
switch (status) {
|
||||
case SMBDIRECT_SOCKET_CREATED:
|
||||
return "CREATED";
|
||||
case SMBDIRECT_SOCKET_LISTENING:
|
||||
return "LISTENING";
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
|
||||
return "RESOLVE_ADDR_NEEDED";
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
|
||||
|
|
@ -193,6 +196,35 @@ struct smbdirect_socket {
|
|||
struct delayed_work timer_work;
|
||||
} idle;
|
||||
|
||||
/*
|
||||
* The state for listen sockets
|
||||
*/
|
||||
struct {
|
||||
spinlock_t lock;
|
||||
struct list_head pending;
|
||||
struct list_head ready;
|
||||
wait_queue_head_t wait_queue;
|
||||
/*
|
||||
* This starts as -1 and a value != -1
|
||||
* means this socket was in LISTENING state
|
||||
* before. Note the valid backlog can
|
||||
* only be > 0.
|
||||
*/
|
||||
int backlog;
|
||||
} listen;
|
||||
|
||||
/*
|
||||
* The state for sockets waiting
|
||||
* for accept, either still waiting
|
||||
* for the negotiation to finish
|
||||
* or already ready with a usable
|
||||
* connection.
|
||||
*/
|
||||
struct {
|
||||
struct smbdirect_socket *listener;
|
||||
struct list_head list;
|
||||
} accept;
|
||||
|
||||
/*
|
||||
* The state for posted send buffers
|
||||
*/
|
||||
|
|
@ -552,6 +584,14 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)
|
|||
INIT_DELAYED_WORK(&sc->idle.timer_work, __smbdirect_socket_disabled_work);
|
||||
disable_delayed_work_sync(&sc->idle.timer_work);
|
||||
|
||||
spin_lock_init(&sc->listen.lock);
|
||||
INIT_LIST_HEAD(&sc->listen.pending);
|
||||
INIT_LIST_HEAD(&sc->listen.ready);
|
||||
sc->listen.backlog = -1; /* not a listener */
|
||||
init_waitqueue_head(&sc->listen.wait_queue);
|
||||
|
||||
INIT_LIST_HEAD(&sc->accept.list);
|
||||
|
||||
sc->send_io.mem.gfp_mask = GFP_KERNEL;
|
||||
|
||||
atomic_set(&sc->send_io.bcredits.count, 0);
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ static void smb_direct_disconnect_rdma_work(struct work_struct *work)
|
|||
break;
|
||||
|
||||
case SMBDIRECT_SOCKET_CREATED:
|
||||
case SMBDIRECT_SOCKET_LISTENING:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
|
||||
case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
|
||||
|
|
@ -364,6 +365,7 @@ smb_direct_disconnect_rdma_connection(struct smbdirect_socket *sc)
|
|||
break;
|
||||
|
||||
case SMBDIRECT_SOCKET_CREATED:
|
||||
case SMBDIRECT_SOCKET_LISTENING:
|
||||
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
|
||||
break;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user