[PATCH v2 000/127] smb: smbdirect/client/server: moving to common functions and smbdirect.ko

Namjae Jeon linkinjeon at kernel.org
Tue Nov 4 14:49:49 UTC 2025


On Tue, Nov 4, 2025 at 7:49 PM Stefan Metzmacher <metze at samba.org> wrote:
>
> 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...
I will replace them with new ones.
Thanks!

>
> 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