]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
smb: smbdirect: introduce smbdirect_connection_recvmsg()
authorStefan Metzmacher <metze@samba.org>
Mon, 13 Oct 2025 16:34:08 +0000 (18:34 +0200)
committerSteve French <stfrench@microsoft.com>
Thu, 16 Apr 2026 02:58:19 +0000 (21:58 -0500)
This is basically a copy of smbd_recv() in the client.

And it's very similar to smb_direct_read() in the server.

It will replace both in the following commits.

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>
fs/smb/common/smbdirect/smbdirect_connection.c

index 33fa460849b7a0809a6011ac9f43151e94b534bd..0eebc7f336f5ab32692d698f3215003c86703b23 100644 (file)
@@ -1078,6 +1078,157 @@ static void smbdirect_connection_recv_io_refill_work(struct work_struct *work)
        }
 }
 
+__maybe_unused /* this is temporary while this file is included in others */
+static int smbdirect_connection_recvmsg(struct smbdirect_socket *sc,
+                                       struct msghdr *msg,
+                                       unsigned int flags)
+{
+       struct smbdirect_recv_io *response;
+       struct smbdirect_data_transfer *data_transfer;
+       size_t size = iov_iter_count(&msg->msg_iter);
+       int to_copy, to_read, data_read, offset;
+       u32 data_length, remaining_data_length, data_offset;
+       int ret;
+
+       if (WARN_ONCE(flags, "unexpected flags=0x%x\n", flags))
+               return -EINVAL; /* no flags support for now */
+
+       if (WARN_ON_ONCE(iov_iter_rw(&msg->msg_iter) != ITER_DEST))
+               return -EINVAL; /* It's a bug in upper layer to get there */
+
+again:
+       if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
+               smbdirect_log_read(sc, SMBDIRECT_LOG_INFO,
+                       "status=%s first_error=%1pe => %1pe\n",
+                       smbdirect_socket_status_string(sc->status),
+                       SMBDIRECT_DEBUG_ERR_PTR(sc->first_error),
+                       SMBDIRECT_DEBUG_ERR_PTR(-ENOTCONN));
+               return -ENOTCONN;
+       }
+
+       /*
+        * No need to hold the reassembly queue lock all the time as we are
+        * the only one reading from the front of the queue. The transport
+        * may add more entries to the back of the queue at the same time
+        */
+       smbdirect_log_read(sc, SMBDIRECT_LOG_INFO,
+               "size=%zd sc->recv_io.reassembly.data_length=%d\n",
+               size, sc->recv_io.reassembly.data_length);
+       if (sc->recv_io.reassembly.data_length >= size) {
+               int queue_length;
+               int queue_removed = 0;
+               unsigned long flags;
+
+               /*
+                * Need to make sure reassembly_data_length is read before
+                * reading reassembly_queue_length and calling
+                * smbdirect_connection_reassembly_first_recv_io. This call is lock free
+                * as we never read at the end of the queue which are being
+                * updated in SOFTIRQ as more data is received
+                */
+               virt_rmb();
+               queue_length = sc->recv_io.reassembly.queue_length;
+               data_read = 0;
+               to_read = size;
+               offset = sc->recv_io.reassembly.first_entry_offset;
+               while (data_read < size) {
+                       response = smbdirect_connection_reassembly_first_recv_io(sc);
+                       data_transfer = (void *)response->packet;
+                       data_length = le32_to_cpu(data_transfer->data_length);
+                       remaining_data_length =
+                               le32_to_cpu(
+                                       data_transfer->remaining_data_length);
+                       data_offset = le32_to_cpu(data_transfer->data_offset);
+
+                       /*
+                        * The upper layer expects RFC1002 length at the
+                        * beginning of the payload. Return it to indicate
+                        * the total length of the packet. This minimize the
+                        * change to upper layer packet processing logic. This
+                        * will be eventually remove when an intermediate
+                        * transport layer is added
+                        */
+                       if (response->first_segment && size == 4) {
+                               unsigned int rfc1002_len =
+                                       data_length + remaining_data_length;
+                               __be32 rfc1002_hdr = cpu_to_be32(rfc1002_len);
+
+                               if (copy_to_iter(&rfc1002_hdr, sizeof(rfc1002_hdr),
+                                                &msg->msg_iter) != sizeof(rfc1002_hdr))
+                                       return -EFAULT;
+                               data_read = 4;
+                               response->first_segment = false;
+                               smbdirect_log_read(sc, SMBDIRECT_LOG_INFO,
+                                       "returning rfc1002 length %d\n",
+                                       rfc1002_len);
+                               goto read_rfc1002_done;
+                       }
+
+                       to_copy = min_t(int, data_length - offset, to_read);
+                       if (copy_to_iter((u8 *)data_transfer + data_offset + offset,
+                                        to_copy, &msg->msg_iter) != to_copy)
+                               return -EFAULT;
+
+                       /* move on to the next buffer? */
+                       if (to_copy == data_length - offset) {
+                               queue_length--;
+                               /*
+                                * No need to lock if we are not at the
+                                * end of the queue
+                                */
+                               if (queue_length)
+                                       list_del(&response->list);
+                               else {
+                                       spin_lock_irqsave(
+                                               &sc->recv_io.reassembly.lock, flags);
+                                       list_del(&response->list);
+                                       spin_unlock_irqrestore(
+                                               &sc->recv_io.reassembly.lock, flags);
+                               }
+                               queue_removed++;
+                               sc->statistics.dequeue_reassembly_queue++;
+                               smbdirect_connection_put_recv_io(response);
+                               offset = 0;
+                               smbdirect_log_read(sc, SMBDIRECT_LOG_INFO,
+                                       "smbdirect_connection_put_recv_io offset=0\n");
+                       } else
+                               offset += to_copy;
+
+                       to_read -= to_copy;
+                       data_read += to_copy;
+
+                       smbdirect_log_read(sc, SMBDIRECT_LOG_INFO,
+                                "memcpy %d bytes len-ofs=%u => todo=%u done=%u ofs=%u\n",
+                                to_copy, data_length - offset,
+                                to_read, data_read, offset);
+               }
+
+               spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
+               sc->recv_io.reassembly.data_length -= data_read;
+               sc->recv_io.reassembly.queue_length -= queue_removed;
+               spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
+
+               sc->recv_io.reassembly.first_entry_offset = offset;
+               smbdirect_log_read(sc, SMBDIRECT_LOG_INFO,
+                        "returning data_read=%d reassembly_length=%d first_ofs=%u\n",
+                        data_read, sc->recv_io.reassembly.data_length,
+                        sc->recv_io.reassembly.first_entry_offset);
+read_rfc1002_done:
+               return data_read;
+       }
+
+       smbdirect_log_read(sc, SMBDIRECT_LOG_INFO,
+               "wait_event on more data\n");
+       ret = wait_event_interruptible(sc->recv_io.reassembly.wait_queue,
+                                      sc->recv_io.reassembly.data_length >= size ||
+                                      sc->status != SMBDIRECT_SOCKET_CONNECTED);
+       /* Don't return any data if interrupted */
+       if (ret)
+               return ret;
+
+       goto again;
+}
+
 static bool smbdirect_map_sges_single_page(struct smbdirect_map_sges *state,
                                           struct page *page, size_t off, size_t len)
 {