mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
smb: smbdirect: introduce smbdirect_connection_recvmsg()
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>
This commit is contained in:
parent
b895bc4d21
commit
20c55c6910
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user