[PATCH v2 000/127] smb: smbdirect/client/server: moving to common functions and smbdirect.ko
Stefan Metzmacher
metze at samba.org
Tue Nov 4 10:49:33 UTC 2025
Hi,
I already used these debugging features:
CONFIG_KASAN_SHADOW_OFFSET=0xdffffc0000000000
CONFIG_HAVE_ARCH_KASAN=y
CONFIG_HAVE_ARCH_KASAN_VMALLOC=y
CONFIG_CC_HAS_KASAN_GENERIC=y
CONFIG_KASAN=y
CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX=y
CONFIG_KASAN_GENERIC=y
CONFIG_KASAN_INLINE=y
CONFIG_KASAN_STACK=y
CONFIG_KASAN_VMALLOC=y
CONFIG_KASAN_EXTRA_INFO=y
But now I turned on a lot more debugging features:
CONFIG_PRINTK_CALLER=y
CONFIG_STACKTRACE_BUILD_ID=y
CONFIG_DEBUG_INFO_COMPRESSED_ZSTD=y
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0xffffffff
CONFIG_PAGE_EXTENSION=y
CONFIG_PAGE_OWNER=y
CONFIG_PAGE_TABLE_CHECK=y
CONFIG_DEBUG_PAGE_REF=y
CONFIG_PTDUMP_DEBUGFS=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_MEM_POOL_SIZE=16000
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
CONFIG_DEBUG_OBJECTS=y
CONFIG_DEBUG_OBJECTS_FREE=y
CONFIG_DEBUG_OBJECTS_TIMERS=y
CONFIG_DEBUG_OBJECTS_WORK=y
CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=0
CONFIG_WQ_WATCHDOG=y
CONFIG_PROVE_LOCKING=y
CONFIG_PROVE_RAW_LOCK_NESTING=y
CONFIG_LOCK_STAT=y
CONFIG_DEBUG_RT_MUTEXES=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
CONFIG_DEBUG_RWSEMS=y
CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_LOCKDEP=y
CONFIG_LOCKDEP_BITS=15
CONFIG_LOCKDEP_CHAINS_BITS=16
CONFIG_LOCKDEP_STACK_TRACE_BITS=19
CONFIG_LOCKDEP_STACK_TRACE_HASH_BITS=14
CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS=12
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_TRACE_IRQFLAGS=y
CONFIG_TRACE_IRQFLAGS_NMI=y
CONFIG_DEBUG_IRQFLAGS=y
CONFIG_PROVE_RCU=y
CONFIG_RCU_TRACE=y
CONFIG_RING_BUFFER_ALLOW_SWAP=y
CONFIG_PREEMPTIRQ_TRACEPOINTS=y
CONFIG_TRACE_PREEMPT_TOGGLE=y
CONFIG_IRQSOFF_TRACER=y
CONFIG_PREEMPT_TRACER=y
CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP=y
And that luckily revealed some cases where
I called functions which might sleep in a
ib_post_recv completion function that
is called in a softirq as the client uses
IB_POLL_SOFTIRQ.
I means that I needed to deferr some of the
smbdirect_connect_negotiate_recv_done and
smbdirect_accept_negotiate_recv_done logic
to the workqueue.
And I added some includes to smbdirect_socket.h,
as some sparc cross compile bot was complaining
about a failing build.
I also did a few minor changes like using
sc->ib.dev consistently.
As well as setting sc->first_error as early as possible.
I took the current ksmbd-for-next-next at
commit 2bd117c715790585d69a06bd5f795520fc67ce86
and rebased it on v6.18-rc4 and then removed my patches
and added the modified once on top.
The result can be found in my for-6.19/fs-smb-20251104-v3 branch,
at commit a73e9e7470238121b87b2128da2ed1fff9fd6d4b:
git fetch https://git.samba.org/metze/linux/wip.git for-6.19/fs-smb-20251104-v3
https://git.samba.org/?p=metze/linux/wip.git;a=shortlog;h=refs/heads/for-6.19/fs-smb-20251104-v3
The whole diff between the old and new patchset
follows below:
The new branch should replace ksmbd-for-next-next...
Thanks!
metze
fs/smb/common/smbdirect/smbdirect_accept.c | 154 +++++++++++++++-------
fs/smb/common/smbdirect/smbdirect_connect.c | 171 +++++++++++++++++--------
fs/smb/common/smbdirect/smbdirect_connection.c | 91 +++++++++++--
fs/smb/common/smbdirect/smbdirect_socket.h | 15 +++
4 files changed, 316 insertions(+), 115 deletions(-)
diff --git a/fs/smb/common/smbdirect/smbdirect_accept.c b/fs/smb/common/smbdirect/smbdirect_accept.c
index 0ed3f43a7397..3455e7327663 100644
--- a/fs/smb/common/smbdirect/smbdirect_accept.c
+++ b/fs/smb/common/smbdirect/smbdirect_accept.c
@@ -192,25 +192,14 @@ static int smbdirect_accept_init_params(struct smbdirect_socket *sc)
return 0;
}
+static void smbdirect_accept_negotiate_recv_work(struct work_struct *work);
+
static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc *wc)
{
struct smbdirect_recv_io *recv_io =
container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
struct smbdirect_socket *sc = recv_io->socket;
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- struct smbdirect_negotiate_req *nreq;
- u16 min_version;
- u16 max_version;
- u16 credits_requested;
- u32 preferred_send_size;
- u32 max_receive_size;
- u32 max_fragmented_size;
- struct smbdirect_send_io *send_io = NULL;
- struct smbdirect_negotiate_resp *nrep;
- struct ib_send_wr send_wr;
- u32 ntstatus;
- int posted;
- int ret;
+ const struct smbdirect_socket_parameters *sp = &sc->parameters;
if (unlikely(wc->status != IB_WC_SUCCESS || WARN_ON_ONCE(wc->opcode != IB_WC_RECV))) {
if (wc->status != IB_WC_WR_FLUSH_ERR)
@@ -235,18 +224,98 @@ static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
msecs_to_jiffies(sp->keepalive_interval_msec));
- ib_dma_sync_single_for_cpu(wc->qp->device,
+ ib_dma_sync_single_for_cpu(sc->ib.dev,
recv_io->sge.addr,
recv_io->sge.length,
DMA_FROM_DEVICE);
- if (wc->byte_len < sizeof(*nreq)) {
+ if (wc->byte_len < sizeof(struct smbdirect_negotiate_req)) {
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"wc->byte_len=%u < %zu\n",
- wc->byte_len, sizeof(*nreq));
+ wc->byte_len, sizeof(struct smbdirect_negotiate_req));
goto error;
}
+ /*
+ * We continue via the workqueue as we may have
+ * complex work that might sleep.
+ *
+ * The work should already/still be disabled,
+ * but smbdirect_connection_put_recv_io() disables
+ * it again.
+ *
+ * Note that smbdirect_connection_put_recv_io()
+ * only moved recv_io into the free list, but
+ * we didn't call smbdirect_connection_recv_io_refill()
+ * yet, so it won't be reused, but the cleanup code
+ * on disconnect is able to find it, disables
+ * recv_io->complex_work again.
+ */
+ smbdirect_connection_put_recv_io(recv_io);
+ INIT_WORK(&recv_io->complex_work, smbdirect_accept_negotiate_recv_work);
+ queue_work(sc->workqueue, &recv_io->complex_work);
+ return;
+
+error:
+ /*
+ * recv_io.posted.refill_work is still disabled,
+ * so smbdirect_connection_put_recv_io() won't
+ * start it.
+ */
+ smbdirect_connection_put_recv_io(recv_io);
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+}
+
+static void smbdirect_accept_negotiate_recv_work(struct work_struct *work)
+{
+ struct smbdirect_recv_io *recv_io =
+ container_of(work, struct smbdirect_recv_io, complex_work);
+ struct smbdirect_socket *sc = recv_io->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_negotiate_req *nreq;
+ u16 min_version;
+ u16 max_version;
+ u16 credits_requested;
+ u32 preferred_send_size;
+ u32 max_receive_size;
+ u32 max_fragmented_size;
+ struct smbdirect_send_io *send_io = NULL;
+ struct smbdirect_negotiate_resp *nrep;
+ struct ib_send_wr send_wr;
+ u32 ntstatus;
+ int posted;
+ int ret;
+
+ /*
+ * make sure we won't start again...
+ */
+ disable_work(work);
+
+ /*
+ * Note recv_io is already part of the free list,
+ * as smbdirect_connect_negotiate_recv_done() called
+ * smbdirect_connection_put_recv_io(), but
+ * it won't be reused before we call
+ * smbdirect_connection_recv_io_refill() below.
+ */
+
+ if (unlikely(sc->first_error))
+ return;
+
+ if (sc->status != SMBDIRECT_SOCKET_NEGOTIATE_RUNNING) {
+ /*
+ * Something went wrong...
+ */
+ smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
+ "status=%s first_error=%1pe local: %pISpsfc remote: %pISpsfc\n",
+ smbdirect_socket_status_string(sc->status),
+ SMBDIRECT_DEBUG_ERR_PTR(sc->first_error),
+ &sc->rdma.cm_id->route.addr.src_addr,
+ &sc->rdma.cm_id->route.addr.dst_addr);
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
+ }
+
nreq = (struct smbdirect_negotiate_req *)recv_io->packet;
min_version = le16_to_cpu(nreq->min_version);
max_version = le16_to_cpu(nreq->max_version);
@@ -281,7 +350,8 @@ static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
if (credits_requested == 0) {
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"invalid: credits_requested == 0\n");
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
if (max_receive_size < SMBDIRECT_MIN_RECEIVE_SIZE) {
@@ -289,7 +359,8 @@ static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
"invalid: max_receive_size=%u < %u\n",
max_receive_size,
SMBDIRECT_MIN_RECEIVE_SIZE);
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
if (max_fragmented_size < SMBDIRECT_MIN_FRAGMENTED_SIZE) {
@@ -297,7 +368,8 @@ static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
"invalid: max_fragmented_size=%u < %u\n",
max_fragmented_size,
SMBDIRECT_MIN_FRAGMENTED_SIZE);
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
/*
@@ -327,12 +399,6 @@ static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
*/
sp->max_fragmented_send_size = max_fragmented_size;
- /*
- * Give recv_io back to the recv_io.free.list,
- * so that the refill can also post it.
- */
- smbdirect_connection_put_recv_io(recv_io);
-
/*
* Prepare for receiving data_transfer messages
*/
@@ -350,7 +416,8 @@ static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"smbdirect_connection_recv_io_refill() failed %1pe\n",
SMBDIRECT_DEBUG_ERR_PTR(posted));
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
/*
@@ -365,11 +432,11 @@ static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
send_io = smbdirect_connection_alloc_send_io(sc);
if (IS_ERR(send_io)) {
ret = PTR_ERR(send_io);
- send_io = NULL;
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"smbdirect_connection_alloc_send_io() failed %1pe\n",
SMBDIRECT_DEBUG_ERR_PTR(ret));
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, ret);
+ return;
}
send_io->cqe.done = smbdirect_accept_negotiate_send_done;
@@ -430,7 +497,9 @@ static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"ib_dma_mapping_error() failed %1pe\n",
SMBDIRECT_DEBUG_ERR_PTR(ret));
- goto error;
+ smbdirect_connection_free_send_io(send_io);
+ smbdirect_connection_schedule_disconnect(sc, ret);
+ return;
}
send_io->sge[0].length = sizeof(*nrep);
@@ -455,30 +524,19 @@ static void smbdirect_accept_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
smbdirect_log_rdma_send(sc, SMBDIRECT_LOG_ERR,
"smbdirect_connection_post_send_wr() failed %1pe\n",
SMBDIRECT_DEBUG_ERR_PTR(ret));
- goto error;
+ /*
+ * Note smbdirect_connection_free_send_io()
+ * does ib_dma_unmap_page()
+ */
+ smbdirect_connection_free_send_io(send_io);
+ smbdirect_connection_schedule_disconnect(sc, ret);
+ return;
}
/*
* smbdirect_accept_negotiate_send_done
* will do all remaining work...
*/
- return;
-
-error:
- /*
- * recv_io.posted.refill_work is still disabled,
- * so smbdirect_connection_put_recv_io() won't
- * start it.
- */
- if (recv_io)
- smbdirect_connection_put_recv_io(recv_io);
- /*
- * Note smbdirect_connection_free_send_io()
- * does ib_dma_unmap_page()
- */
- if (send_io)
- smbdirect_connection_free_send_io(send_io);
- smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
}
static void smbdirect_accept_negotiate_send_done(struct ib_cq *cq, struct ib_wc *wc)
diff --git a/fs/smb/common/smbdirect/smbdirect_connect.c b/fs/smb/common/smbdirect/smbdirect_connect.c
index eb8e903c9fce..b9544a6ef59b 100644
--- a/fs/smb/common/smbdirect/smbdirect_connect.c
+++ b/fs/smb/common/smbdirect/smbdirect_connect.c
@@ -536,23 +536,14 @@ static void smbdirect_connect_negotiate_send_done(struct ib_cq *cq, struct ib_wc
}
}
+static void smbdirect_connect_negotiate_recv_work(struct work_struct *work);
+
static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc *wc)
{
struct smbdirect_recv_io *recv_io =
container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
struct smbdirect_socket *sc = recv_io->socket;
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- struct smbdirect_negotiate_resp *nrep;
- u16 negotiated_version;
- u16 credits_requested;
- u16 credits_granted;
- u32 status;
- u32 max_readwrite_size;
- u32 preferred_send_size;
- u32 max_receive_size;
- u32 max_fragmented_size;
- int posted;
- int ret;
+ const struct smbdirect_socket_parameters *sp = &sc->parameters;
if (unlikely(wc->status != IB_WC_SUCCESS || WARN_ON_ONCE(wc->opcode != IB_WC_RECV))) {
if (wc->status != IB_WC_WR_FLUSH_ERR)
@@ -574,18 +565,96 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
msecs_to_jiffies(sp->keepalive_interval_msec));
- ib_dma_sync_single_for_cpu(wc->qp->device,
+ ib_dma_sync_single_for_cpu(sc->ib.dev,
recv_io->sge.addr,
recv_io->sge.length,
DMA_FROM_DEVICE);
- if (wc->byte_len < sizeof(*nrep)) {
+ if (wc->byte_len < sizeof(struct smbdirect_negotiate_resp)) {
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"wc->byte_len=%u < %zu\n",
- wc->byte_len, sizeof(*nrep));
+ wc->byte_len, sizeof(struct smbdirect_negotiate_resp));
goto error;
}
+ /*
+ * We continue via the workqueue as we may have
+ * complex work that might sleep.
+ *
+ * The work should already/still be disabled,
+ * but smbdirect_connection_put_recv_io() disables
+ * it again.
+ *
+ * Note that smbdirect_connection_put_recv_io()
+ * only moved recv_io into the free list, but
+ * we didn't call smbdirect_connection_recv_io_refill()
+ * yet, so it won't be reused, but the cleanup code
+ * on disconnect is able to find it, disables
+ * recv_io->complex_work again.
+ */
+ smbdirect_connection_put_recv_io(recv_io);
+ INIT_WORK(&recv_io->complex_work, smbdirect_connect_negotiate_recv_work);
+ queue_work(sc->workqueue, &recv_io->complex_work);
+ return;
+
+error:
+ /*
+ * recv_io.posted.refill_work is still disabled,
+ * so smbdirect_connection_put_recv_io() won't
+ * start it.
+ */
+ smbdirect_connection_put_recv_io(recv_io);
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+}
+
+static void smbdirect_connect_negotiate_recv_work(struct work_struct *work)
+{
+ struct smbdirect_recv_io *recv_io =
+ container_of(work, struct smbdirect_recv_io, complex_work);
+ struct smbdirect_socket *sc = recv_io->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_negotiate_resp *nrep;
+ u16 negotiated_version;
+ u16 credits_requested;
+ u16 credits_granted;
+ u32 status;
+ u32 max_readwrite_size;
+ u32 preferred_send_size;
+ u32 max_receive_size;
+ u32 max_fragmented_size;
+ int posted;
+ int ret;
+
+ /*
+ * make sure we won't start again...
+ */
+ disable_work(work);
+
+ /*
+ * Note recv_io is already part of the free list,
+ * as smbdirect_connect_negotiate_recv_done() called
+ * smbdirect_connection_put_recv_io(), but
+ * it won't be reused before we call
+ * smbdirect_connection_recv_io_refill() below.
+ */
+
+ if (unlikely(sc->first_error))
+ return;
+
+ if (sc->status != SMBDIRECT_SOCKET_NEGOTIATE_RUNNING) {
+ /*
+ * Something went wrong...
+ */
+ smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
+ "status=%s first_error=%1pe local: %pISpsfc remote: %pISpsfc\n",
+ smbdirect_socket_status_string(sc->status),
+ SMBDIRECT_DEBUG_ERR_PTR(sc->first_error),
+ &sc->rdma.cm_id->route.addr.src_addr,
+ &sc->rdma.cm_id->route.addr.dst_addr);
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
+ }
+
nrep = (struct smbdirect_negotiate_resp *)recv_io->packet;
negotiated_version = le16_to_cpu(nrep->negotiated_version);
credits_requested = le16_to_cpu(nrep->credits_requested);
@@ -623,7 +692,16 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"invalid: negotiated_version=0x%x\n",
negotiated_version);
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNREFUSED);
+ return;
+ }
+
+ if (status != 0) {
+ smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
+ "invalid: status=0x%x != 0x0\n",
+ status);
+ smbdirect_connection_schedule_disconnect(sc, -ECONNREFUSED);
+ return;
}
if (max_receive_size < SMBDIRECT_MIN_RECEIVE_SIZE) {
@@ -631,7 +709,8 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
"invalid: max_receive_size=%u < %u\n",
max_receive_size,
SMBDIRECT_MIN_RECEIVE_SIZE);
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
if (max_fragmented_size < SMBDIRECT_MIN_FRAGMENTED_SIZE) {
@@ -639,19 +718,22 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
"invalid: max_fragmented_size=%u < %u\n",
max_fragmented_size,
SMBDIRECT_MIN_FRAGMENTED_SIZE);
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
if (credits_granted == 0) {
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"invalid: credits_granted == 0\n");
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
if (credits_requested == 0) {
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"invalid: credits_requested == 0\n");
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
if (preferred_send_size > sp->max_recv_size) {
@@ -659,14 +741,8 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
"invalid: preferred_send_size=%u < max_recv_size=%u\n",
preferred_send_size,
sp->max_recv_size);
- goto error;
- }
-
- if (status != 0) {
- smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
- "invalid: status=0x%x != 0x0\n",
- status);
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
/*
@@ -701,7 +777,8 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
"invalid: max_readwrite_size=%u < PAGE_SIZE(%lu)\n",
max_readwrite_size,
PAGE_SIZE);
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
sp->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE;
@@ -716,11 +793,14 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
*/
sp->max_fragmented_send_size = max_fragmented_size;
- /*
- * Give recv_io back to the recv_io.free.list,
- * so that the refill can also post it.
- */
- smbdirect_connection_put_recv_io(recv_io);
+ ret = smbdirect_connection_create_mr_list(sc);
+ if (ret) {
+ smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR,
+ "smbdirect_connection_create_mr_list() failed %1pe\n",
+ SMBDIRECT_DEBUG_ERR_PTR(ret));
+ smbdirect_connection_schedule_disconnect(sc, ret);
+ return;
+ }
/*
* Prepare for receiving data_transfer messages
@@ -731,14 +811,6 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
recv_io->cqe.done = smbdirect_connection_recv_io_done;
recv_io = NULL;
- ret = smbdirect_connection_create_mr_list(sc);
- if (ret) {
- smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR,
- "smbdirect_connection_create_mr_list() failed %1pe\n",
- SMBDIRECT_DEBUG_ERR_PTR(ret));
- goto error;
- }
-
/*
* We should at least post 1 smbdirect_recv_io!
*/
@@ -747,7 +819,8 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR,
"smbdirect_connection_recv_io_refill() failed %1pe\n",
SMBDIRECT_DEBUG_ERR_PTR(ret));
- goto error;
+ smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
+ return;
}
/*
@@ -756,18 +829,6 @@ static void smbdirect_connect_negotiate_recv_done(struct ib_cq *cq, struct ib_wc
* the waiter.
*/
smbdirect_connection_negotiation_done(sc);
-
- return;
-
-error:
- /*
- * recv_io.posted.refill_work is still disabled,
- * so smbdirect_connection_put_recv_io() won't
- * start it.
- */
- if (recv_io)
- smbdirect_connection_put_recv_io(recv_io);
- smbdirect_connection_schedule_disconnect(sc, -ECONNABORTED);
}
__SMBDIRECT_PUBLIC__
diff --git a/fs/smb/common/smbdirect/smbdirect_connection.c b/fs/smb/common/smbdirect/smbdirect_connection.c
index ae9626888b5c..cc22e8b2f4d3 100644
--- a/fs/smb/common/smbdirect/smbdirect_connection.c
+++ b/fs/smb/common/smbdirect/smbdirect_connection.c
@@ -742,6 +742,8 @@ int smbdirect_connection_create_mem_pools(struct smbdirect_socket *sc)
goto err;
recv_io->socket = sc;
recv_io->sge.length = 0;
+ INIT_WORK(&recv_io->complex_work, __smbdirect_socket_disabled_work);
+ disable_work_sync(&recv_io->complex_work);
list_add_tail(&recv_io->list, &sc->recv_io.free.list);
}
@@ -757,6 +759,10 @@ void smbdirect_connection_destroy_mem_pools(struct smbdirect_socket *sc)
struct smbdirect_recv_io *recv_io, *next_io;
list_for_each_entry_safe(recv_io, next_io, &sc->recv_io.free.list, list) {
+ /*
+ * The work should already be disabled
+ */
+ disable_work_sync(&recv_io->complex_work);
list_del(&recv_io->list);
mempool_free(recv_io, sc->recv_io.mem.pool);
}
@@ -833,9 +839,10 @@ struct smbdirect_recv_io *smbdirect_connection_get_recv_io(struct smbdirect_sock
unsigned long flags;
spin_lock_irqsave(&sc->recv_io.free.lock, flags);
- msg = list_first_entry_or_null(&sc->recv_io.free.list,
- struct smbdirect_recv_io,
- list);
+ if (likely(!sc->first_error))
+ msg = list_first_entry_or_null(&sc->recv_io.free.list,
+ struct smbdirect_recv_io,
+ list);
if (likely(msg)) {
list_del(&msg->list);
sc->statistics.get_receive_buffer++;
@@ -851,6 +858,11 @@ void smbdirect_connection_put_recv_io(struct smbdirect_recv_io *msg)
struct smbdirect_socket *sc = msg->socket;
unsigned long flags;
+ /*
+ * Should already be disabled anyway.
+ */
+ disable_work(&msg->complex_work);
+
if (likely(msg->sge.length != 0)) {
ib_dma_unmap_single(sc->ib.dev,
msg->sge.addr,
@@ -874,6 +886,11 @@ static void smbdirect_connection_reassembly_append_recv_io(struct smbdirect_sock
{
unsigned long flags;
+ /*
+ * The work should already/still be disabled
+ */
+ disable_work(&msg->complex_work);
+
spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
list_add_tail(&msg->list, &sc->recv_io.reassembly.list);
sc->recv_io.reassembly.queue_length++;
@@ -968,6 +985,14 @@ __SMBDIRECT_PRIVATE__
void smbdirect_connection_schedule_disconnect(struct smbdirect_socket *sc,
int error)
{
+ struct smbdirect_recv_io *recv_io, *recv_tmp;
+ unsigned long flags;
+
+ if (sc->first_error == 0)
+ sc->first_error = error;
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
+
/*
* make sure other work (than disconnect_work)
* is not queued again but here we don't block and avoid
@@ -979,10 +1004,19 @@ void smbdirect_connection_schedule_disconnect(struct smbdirect_socket *sc,
sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE;
disable_delayed_work(&sc->idle.timer_work);
- if (sc->first_error == 0)
- sc->first_error = error;
- if (sc->first_error == 0)
- sc->first_error = -ECONNABORTED;
+ /*
+ * If any complex work was scheduled we
+ * should disable it (only happens during
+ * negotiation)...
+ *
+ * Note that sc->first_error is set before,
+ * so any future smbdirect_connection_get_recv_io()
+ * will see it and return NULL.
+ */
+ spin_lock_irqsave(&sc->recv_io.free.lock, flags);
+ list_for_each_entry_safe(recv_io, recv_tmp, &sc->recv_io.free.list, list)
+ disable_work(&recv_io->complex_work);
+ spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
switch (sc->status) {
case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
@@ -1037,12 +1071,17 @@ static void smbdirect_connection_disconnect_work(struct work_struct *work)
{
struct smbdirect_socket *sc =
container_of(work, struct smbdirect_socket, disconnect_work);
+ struct smbdirect_recv_io *recv_io, *recv_tmp;
+ unsigned long flags;
/*
* This should not never be called in an interrupt!
*/
WARN_ON_ONCE(in_interrupt());
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
+
/*
* make sure this and other work is not queued again
* but here we don't block and avoid
@@ -1055,8 +1094,19 @@ static void smbdirect_connection_disconnect_work(struct work_struct *work)
sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE;
disable_delayed_work(&sc->idle.timer_work);
- if (sc->first_error == 0)
- sc->first_error = -ECONNABORTED;
+ /*
+ * If any complex work was scheduled we
+ * should disable it (only happens during
+ * negotiation)...
+ *
+ * Note that sc->first_error is set before,
+ * so any future smbdirect_connection_get_recv_io()
+ * will see it and return NULL.
+ */
+ spin_lock_irqsave(&sc->recv_io.free.lock, flags);
+ list_for_each_entry_safe(recv_io, recv_tmp, &sc->recv_io.free.list, list)
+ disable_work(&recv_io->complex_work);
+ spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
switch (sc->status) {
case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
@@ -1216,6 +1266,24 @@ static void smbdirect_connection_destroy(struct smbdirect_socket *sc)
disable_work_sync(&sc->idle.immediate_work);
disable_delayed_work_sync(&sc->idle.timer_work);
+ /*
+ * If any complex work was scheduled we
+ * should disable it (only happens during
+ * negotiation)...
+ *
+ * Note was already set in sc->first_error in
+ * smbdirect_connection_schedule_disconnect() or
+ * smbdirect_connection_disconnect_work(), both
+ * before time before:
+ * spin_lock_irqsave(&sc->recv_io.free.lock, flags),
+ * so any future smbdirect_connection_get_recv_io()
+ * will see it and return NULL. And we don't
+ * need to get the lock here again, while
+ * trying disable_work_sync().
+ */
+ list_for_each_entry_safe(recv_io, recv_tmp, &sc->recv_io.free.list, list)
+ disable_work_sync(&recv_io->complex_work);
+
if (sc->rdma.cm_id)
rdma_lock_handler(sc->rdma.cm_id);
@@ -1231,9 +1299,8 @@ static void smbdirect_connection_destroy(struct smbdirect_socket *sc)
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);
- list_for_each_entry_safe(recv_io, recv_tmp, &all_list, list) {
+ list_for_each_entry_safe(recv_io, recv_tmp, &all_list, list)
smbdirect_connection_put_recv_io(recv_io);
- }
sc->recv_io.reassembly.data_length = 0;
smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,
@@ -2002,7 +2069,7 @@ void smbdirect_connection_recv_io_done(struct ib_cq *cq, struct ib_wc *wc)
mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
msecs_to_jiffies(sp->keepalive_interval_msec));
- ib_dma_sync_single_for_cpu(wc->qp->device,
+ ib_dma_sync_single_for_cpu(sc->ib.dev,
recv_io->sge.addr,
recv_io->sge.length,
DMA_FROM_DEVICE);
diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h
index 65f25fc4b4a7..ef151ff1c02a 100644
--- a/fs/smb/common/smbdirect/smbdirect_socket.h
+++ b/fs/smb/common/smbdirect/smbdirect_socket.h
@@ -6,6 +6,13 @@
#ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_SOCKET_H__
#define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_SOCKET_H__
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/kref.h>
+#include <linux/mempool.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
#include <rdma/rw.h>
enum smbdirect_socket_status {
@@ -617,6 +624,14 @@ struct smbdirect_recv_io {
#define SMBDIRECT_RECV_IO_MAX_SGE 1
struct ib_sge sge;
+ /*
+ * We may need to handle complex
+ * work that might sleep and are
+ * not allowed to run in an interrupt
+ * context.
+ */
+ struct work_struct complex_work;
+
/* Link to free or reassembly list */
struct list_head list;
More information about the samba-technical
mailing list