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:
break;
case SMBDIRECT_SOCKET_CREATED:
+ case SMBDIRECT_SOCKET_LISTENING:
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
break;
*/
#include "smbdirect_internal.h"
+#include <net/sock.h>
#include "../../common/smb2status.h"
static int smbdirect_accept_rdma_event_handler(struct rdma_cm_id *id,
*/
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:
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);
#include "smbdirect_debug.c"
#include "smbdirect_connect.c"
#include "smbdirect_accept.c"
+#include "smbdirect_listen.c"
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...
* 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);
--- /dev/null
+// 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;
+}
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,
* 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);
int error,
enum smbdirect_socket_status *force_status)
{
+ struct smbdirect_socket *psc, *tsc;
+ unsigned long flags;
bool was_first = false;
if (!sc->first_error) {
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:
break;
case SMBDIRECT_SOCKET_CREATED:
+ case SMBDIRECT_SOCKET_LISTENING:
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
break;
{
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!
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:
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:
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);
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.
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);
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);
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;
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,
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:
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
*/
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);
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:
break;
case SMBDIRECT_SOCKET_CREATED:
+ case SMBDIRECT_SOCKET_LISTENING:
sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
break;