summaryrefslogtreecommitdiff
path: root/fs/smb/client
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-12 08:31:12 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-12 08:31:12 -0800
commitd53f4d93f3d686fd64513abb3977c9116bbfdaf8 (patch)
tree0e321b1b2c23ff74cfdc3d3f17c1f9b4f92640e4 /fs/smb/client
parent2831fa8b8bcf1083f9526aa0c41fafb0796cf874 (diff)
parent8f7df60fe063b6b8f039af1042a4b99214347dd1 (diff)
downloadlinux-next-d53f4d93f3d686fd64513abb3977c9116bbfdaf8.tar.gz
linux-next-d53f4d93f3d686fd64513abb3977c9116bbfdaf8.zip
Merge tag 'v7.0-rc-part1-ksmbd-and-smbdirect-fixes' of git://git.samba.org/ksmbd
Pull smb server and smbdirect updates from Steve French: - Fix tcp connection leak - Fix potential use after free when freeing multichannel - Fix locking problem in showing channel list - Locking improvement for tree connection - Fix infinite loop when signing errors - Add /proc interface for monitoring server state - Fixes to avoid mixing iWarp and InfiniBand/RoCEv1/RoCEv2 port ranges used for smbdirect - Fixes for smbdirect credit handling problems, these make the connections more reliable * tag 'v7.0-rc-part1-ksmbd-and-smbdirect-fixes' of git://git.samba.org/ksmbd: (32 commits) ksmbd: fix non-IPv6 build ksmbd: convert tree_conns_lock to rw_semaphore ksmbd: fix missing chann_lock while iterating session channel list ksmbd: add chann_lock to protect ksmbd_chann_list xarray smb: server: correct value for smb_direct_max_fragmented_recv_size smb: client: correct value for smbd_max_fragmented_recv_size smb: server: fix leak of active_num_conn in ksmbd_tcp_new_connection() ksmbd: add procfs interface for runtime monitoring and statistics ksmbd: fix infinite loop caused by next_smb2_rcv_hdr_off reset in error paths smb: server: make use of rdma_restrict_node_type() smb: client: make use of rdma_restrict_node_type() RDMA/core: introduce rdma_restrict_node_type() smb: client: let send_done handle a completion without IB_SEND_SIGNALED smb: client: let smbd_post_send_negotiate_req() use smbd_post_send() smb: client: fix last send credit problem causing disconnects smb: client: make use of smbdirect_socket.send_io.bcredits smb: client: use smbdirect_send_batch processing smb: client: introduce and use smbd_{alloc, free}_send_io() smb: client: split out smbd_ib_post_send() smb: client: port and use the wait_for_credits logic used by server ...
Diffstat (limited to 'fs/smb/client')
-rw-r--r--fs/smb/client/smbdirect.c568
1 files changed, 440 insertions, 128 deletions
diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c
index 788a0670c4a8..d44847c9d8fc 100644
--- a/fs/smb/client/smbdirect.c
+++ b/fs/smb/client/smbdirect.c
@@ -35,6 +35,10 @@ static void enqueue_reassembly(
static struct smbdirect_recv_io *_get_first_reassembly(
struct smbdirect_socket *sc);
+static int smbd_post_send(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *batch,
+ struct smbdirect_send_io *request);
+
static int smbd_post_recv(
struct smbdirect_socket *sc,
struct smbdirect_recv_io *response);
@@ -97,8 +101,23 @@ int smbd_send_credit_target = 255;
/* The maximum single message size can be sent to remote peer */
int smbd_max_send_size = 1364;
-/* The maximum fragmented upper-layer payload receive size supported */
-int smbd_max_fragmented_recv_size = 1024 * 1024;
+/*
+ * The maximum fragmented upper-layer payload receive size supported
+ *
+ * Assume max_payload_per_credit is
+ * smbd_max_receive_size - 24 = 1340
+ *
+ * The maximum number would be
+ * smbd_receive_credit_max * max_payload_per_credit
+ *
+ * 1340 * 255 = 341700 (0x536C4)
+ *
+ * The minimum value from the spec is 131072 (0x20000)
+ *
+ * For now we use the logic we used in ksmbd before:
+ * (1364 * 255) / 2 = 173910 (0x2A756)
+ */
+int smbd_max_fragmented_recv_size = (1364 * 255) / 2;
/* The maximum single-message size which can be received */
int smbd_max_receive_size = 1364;
@@ -493,27 +512,103 @@ static inline void *smbdirect_recv_io_payload(struct smbdirect_recv_io *response
return (void *)response->packet;
}
+static struct smbdirect_send_io *smbd_alloc_send_io(struct smbdirect_socket *sc)
+{
+ struct smbdirect_send_io *msg;
+
+ msg = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
+ if (!msg)
+ return ERR_PTR(-ENOMEM);
+ msg->socket = sc;
+ INIT_LIST_HEAD(&msg->sibling_list);
+ msg->num_sge = 0;
+
+ return msg;
+}
+
+static void smbd_free_send_io(struct smbdirect_send_io *msg)
+{
+ struct smbdirect_socket *sc = msg->socket;
+ size_t i;
+
+ /*
+ * The list needs to be empty!
+ * The caller should take care of it.
+ */
+ WARN_ON_ONCE(!list_empty(&msg->sibling_list));
+
+ /*
+ * Note we call ib_dma_unmap_page(), even if some sges are mapped using
+ * ib_dma_map_single().
+ *
+ * The difference between _single() and _page() only matters for the
+ * ib_dma_map_*() case.
+ *
+ * For the ib_dma_unmap_*() case it does not matter as both take the
+ * dma_addr_t and dma_unmap_single_attrs() is just an alias to
+ * dma_unmap_page_attrs().
+ */
+ for (i = 0; i < msg->num_sge; i++)
+ ib_dma_unmap_page(sc->ib.dev,
+ msg->sge[i].addr,
+ msg->sge[i].length,
+ DMA_TO_DEVICE);
+
+ mempool_free(msg, sc->send_io.mem.pool);
+}
+
/* Called when a RDMA send is done */
static void send_done(struct ib_cq *cq, struct ib_wc *wc)
{
- int i;
struct smbdirect_send_io *request =
container_of(wc->wr_cqe, struct smbdirect_send_io, cqe);
struct smbdirect_socket *sc = request->socket;
+ struct smbdirect_send_io *sibling, *next;
int lcredits = 0;
log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n",
request, ib_wc_status_msg(wc->status));
- for (i = 0; i < request->num_sge; i++)
- ib_dma_unmap_single(sc->ib.dev,
- request->sge[i].addr,
- request->sge[i].length,
- DMA_TO_DEVICE);
- mempool_free(request, sc->send_io.mem.pool);
+ if (unlikely(!(request->wr.send_flags & IB_SEND_SIGNALED))) {
+ /*
+ * This happens when smbdirect_send_io is a sibling
+ * before the final message, it is signaled on
+ * error anyway, so we need to skip
+ * smbdirect_connection_free_send_io here,
+ * otherwise is will destroy the memory
+ * of the siblings too, which will cause
+ * use after free problems for the others
+ * triggered from ib_drain_qp().
+ */
+ if (wc->status != IB_WC_SUCCESS)
+ goto skip_free;
+
+ /*
+ * This should not happen!
+ * But we better just close the
+ * connection...
+ */
+ log_rdma_send(ERR,
+ "unexpected send completion wc->status=%s (%d) wc->opcode=%d\n",
+ ib_wc_status_msg(wc->status), wc->status, wc->opcode);
+ smbd_disconnect_rdma_connection(sc);
+ return;
+ }
+
+ /*
+ * Free possible siblings and then the main send_io
+ */
+ list_for_each_entry_safe(sibling, next, &request->sibling_list, sibling_list) {
+ list_del_init(&sibling->sibling_list);
+ smbd_free_send_io(sibling);
+ lcredits += 1;
+ }
+ /* Note this frees wc->wr_cqe, but not wc */
+ smbd_free_send_io(request);
lcredits += 1;
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
+skip_free:
if (wc->status != IB_WC_WR_FLUSH_ERR)
log_rdma_send(ERR, "wc->status=%s wc->opcode=%d\n",
ib_wc_status_msg(wc->status), wc->opcode);
@@ -608,6 +703,7 @@ static bool process_negotiation_response(
sp->max_frmr_depth * PAGE_SIZE);
sp->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE;
+ atomic_set(&sc->send_io.bcredits.count, 1);
sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
return true;
}
@@ -618,6 +714,7 @@ static void smbd_post_send_credits(struct work_struct *work)
struct smbdirect_recv_io *response;
struct smbdirect_socket *sc =
container_of(work, struct smbdirect_socket, recv_io.posted.refill_work);
+ int posted = 0;
if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
return;
@@ -640,9 +737,21 @@ static void smbd_post_send_credits(struct work_struct *work)
}
atomic_inc(&sc->recv_io.posted.count);
+ posted += 1;
}
}
+ atomic_add(posted, &sc->recv_io.credits.available);
+
+ /*
+ * If the last send credit is waiting for credits
+ * it can grant we need to wake it up
+ */
+ if (posted &&
+ atomic_read(&sc->send_io.bcredits.count) == 0 &&
+ atomic_read(&sc->send_io.credits.count) == 0)
+ wake_up(&sc->send_io.credits.wait_queue);
+
/* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */
if (atomic_read(&sc->recv_io.credits.count) <
sc->recv_io.credits.target - 1) {
@@ -659,6 +768,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
struct smbdirect_socket *sc = response->socket;
struct smbdirect_socket_parameters *sp = &sc->parameters;
+ int current_recv_credits;
u16 old_recv_credit_target;
u32 data_offset = 0;
u32 data_length = 0;
@@ -743,7 +853,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
}
atomic_dec(&sc->recv_io.posted.count);
- atomic_dec(&sc->recv_io.credits.count);
+ current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count);
+
old_recv_credit_target = sc->recv_io.credits.target;
sc->recv_io.credits.target =
le16_to_cpu(data_transfer->credits_requested);
@@ -779,7 +890,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
* reassembly queue and wake up the reading thread
*/
if (data_length) {
- if (sc->recv_io.credits.target > old_recv_credit_target)
+ if (current_recv_credits <= (sc->recv_io.credits.target / 4) ||
+ sc->recv_io.credits.target > old_recv_credit_target)
queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
enqueue_reassembly(sc, response, data_length);
@@ -810,6 +922,7 @@ static struct rdma_cm_id *smbd_create_id(
{
struct smbdirect_socket_parameters *sp = &sc->parameters;
struct rdma_cm_id *id;
+ u8 node_type = RDMA_NODE_UNSPECIFIED;
int rc;
__be16 *sport;
@@ -821,6 +934,31 @@ static struct rdma_cm_id *smbd_create_id(
return id;
}
+ switch (port) {
+ case SMBD_PORT:
+ /*
+ * only allow iWarp devices
+ * for port 5445.
+ */
+ node_type = RDMA_NODE_RNIC;
+ break;
+ case SMB_PORT:
+ /*
+ * only allow InfiniBand, RoCEv1 or RoCEv2
+ * devices for port 445.
+ *
+ * (Basically don't allow iWarp devices)
+ */
+ node_type = RDMA_NODE_IB_CA;
+ break;
+ }
+ rc = rdma_restrict_node_type(id, node_type);
+ if (rc) {
+ log_rdma_event(ERR, "rdma_restrict_node_type(%u) failed %i\n",
+ node_type, rc);
+ goto out;
+ }
+
if (dstaddr->sa_family == AF_INET6)
sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port;
else
@@ -955,16 +1093,13 @@ out1:
static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
{
struct smbdirect_socket_parameters *sp = &sc->parameters;
- struct ib_send_wr send_wr;
- int rc = -ENOMEM;
+ int rc;
struct smbdirect_send_io *request;
struct smbdirect_negotiate_req *packet;
- request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
- if (!request)
- return rc;
-
- request->socket = sc;
+ request = smbd_alloc_send_io(sc);
+ if (IS_ERR(request))
+ return PTR_ERR(request);
packet = smbdirect_send_io_payload(request);
packet->min_version = cpu_to_le16(SMBDIRECT_V1);
@@ -976,7 +1111,6 @@ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
packet->max_fragmented_size =
cpu_to_le32(sp->max_fragmented_recv_size);
- request->num_sge = 1;
request->sge[0].addr = ib_dma_map_single(
sc->ib.dev, (void *)packet,
sizeof(*packet), DMA_TO_DEVICE);
@@ -984,42 +1118,20 @@ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
rc = -EIO;
goto dma_mapping_failed;
}
+ request->num_sge = 1;
request->sge[0].length = sizeof(*packet);
request->sge[0].lkey = sc->ib.pd->local_dma_lkey;
- ib_dma_sync_single_for_device(
- sc->ib.dev, request->sge[0].addr,
- request->sge[0].length, DMA_TO_DEVICE);
-
- request->cqe.done = send_done;
-
- send_wr.next = NULL;
- send_wr.wr_cqe = &request->cqe;
- send_wr.sg_list = request->sge;
- send_wr.num_sge = request->num_sge;
- send_wr.opcode = IB_WR_SEND;
- send_wr.send_flags = IB_SEND_SIGNALED;
-
- log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n",
- request->sge[0].addr,
- request->sge[0].length, request->sge[0].lkey);
-
- atomic_inc(&sc->send_io.pending.count);
- rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
+ rc = smbd_post_send(sc, NULL, request);
if (!rc)
return 0;
- /* if we reach here, post send failed */
- log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
- atomic_dec(&sc->send_io.pending.count);
- ib_dma_unmap_single(sc->ib.dev, request->sge[0].addr,
- request->sge[0].length, DMA_TO_DEVICE);
-
- smbd_disconnect_rdma_connection(sc);
+ if (rc == -EAGAIN)
+ rc = -EIO;
dma_mapping_failed:
- mempool_free(request, sc->send_io.mem.pool);
+ smbd_free_send_io(request);
return rc;
}
@@ -1033,19 +1145,38 @@ dma_mapping_failed:
*/
static int manage_credits_prior_sending(struct smbdirect_socket *sc)
{
+ int missing;
+ int available;
int new_credits;
if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target)
return 0;
- new_credits = atomic_read(&sc->recv_io.posted.count);
- if (new_credits == 0)
+ missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count);
+ available = atomic_xchg(&sc->recv_io.credits.available, 0);
+ new_credits = (u16)min3(U16_MAX, missing, available);
+ if (new_credits <= 0) {
+ /*
+ * If credits are available, but not granted
+ * we need to re-add them again.
+ */
+ if (available)
+ atomic_add(available, &sc->recv_io.credits.available);
return 0;
+ }
- new_credits -= atomic_read(&sc->recv_io.credits.count);
- if (new_credits <= 0)
- return 0;
+ if (new_credits < available) {
+ /*
+ * Readd the remaining available again.
+ */
+ available -= new_credits;
+ atomic_add(available, &sc->recv_io.credits.available);
+ }
+ /*
+ * Remember we granted the credits
+ */
+ atomic_add(new_credits, &sc->recv_io.credits.count);
return new_credits;
}
@@ -1075,12 +1206,27 @@ static int manage_keep_alive_before_sending(struct smbdirect_socket *sc)
return 0;
}
+static int smbd_ib_post_send(struct smbdirect_socket *sc,
+ struct ib_send_wr *wr)
+{
+ int ret;
+
+ atomic_inc(&sc->send_io.pending.count);
+ ret = ib_post_send(sc->ib.qp, wr, NULL);
+ if (ret) {
+ pr_err("failed to post send: %d\n", ret);
+ smbd_disconnect_rdma_connection(sc);
+ ret = -EAGAIN;
+ }
+ return ret;
+}
+
/* Post the send request */
static int smbd_post_send(struct smbdirect_socket *sc,
- struct smbdirect_send_io *request)
+ struct smbdirect_send_batch *batch,
+ struct smbdirect_send_io *request)
{
- struct ib_send_wr send_wr;
- int rc, i;
+ int i;
for (i = 0; i < request->num_sge; i++) {
log_rdma_send(INFO,
@@ -1094,79 +1240,245 @@ static int smbd_post_send(struct smbdirect_socket *sc,
}
request->cqe.done = send_done;
+ request->wr.next = NULL;
+ request->wr.sg_list = request->sge;
+ request->wr.num_sge = request->num_sge;
+ request->wr.opcode = IB_WR_SEND;
+
+ if (batch) {
+ request->wr.wr_cqe = NULL;
+ request->wr.send_flags = 0;
+ if (!list_empty(&batch->msg_list)) {
+ struct smbdirect_send_io *last;
+
+ last = list_last_entry(&batch->msg_list,
+ struct smbdirect_send_io,
+ sibling_list);
+ last->wr.next = &request->wr;
+ }
+ list_add_tail(&request->sibling_list, &batch->msg_list);
+ batch->wr_cnt++;
+ return 0;
+ }
- send_wr.next = NULL;
- send_wr.wr_cqe = &request->cqe;
- send_wr.sg_list = request->sge;
- send_wr.num_sge = request->num_sge;
- send_wr.opcode = IB_WR_SEND;
- send_wr.send_flags = IB_SEND_SIGNALED;
+ request->wr.wr_cqe = &request->cqe;
+ request->wr.send_flags = IB_SEND_SIGNALED;
+ return smbd_ib_post_send(sc, &request->wr);
+}
- rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
- if (rc) {
- log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
- smbd_disconnect_rdma_connection(sc);
- rc = -EAGAIN;
+static void smbd_send_batch_init(struct smbdirect_send_batch *batch,
+ bool need_invalidate_rkey,
+ unsigned int remote_key)
+{
+ INIT_LIST_HEAD(&batch->msg_list);
+ batch->wr_cnt = 0;
+ batch->need_invalidate_rkey = need_invalidate_rkey;
+ batch->remote_key = remote_key;
+ batch->credit = 0;
+}
+
+static int smbd_send_batch_flush(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *batch,
+ bool is_last)
+{
+ struct smbdirect_send_io *first, *last;
+ int ret = 0;
+
+ if (list_empty(&batch->msg_list))
+ goto release_credit;
+
+ first = list_first_entry(&batch->msg_list,
+ struct smbdirect_send_io,
+ sibling_list);
+ last = list_last_entry(&batch->msg_list,
+ struct smbdirect_send_io,
+ sibling_list);
+
+ if (batch->need_invalidate_rkey) {
+ first->wr.opcode = IB_WR_SEND_WITH_INV;
+ first->wr.ex.invalidate_rkey = batch->remote_key;
+ batch->need_invalidate_rkey = false;
+ batch->remote_key = 0;
}
- return rc;
+ last->wr.send_flags = IB_SEND_SIGNALED;
+ last->wr.wr_cqe = &last->cqe;
+
+ /*
+ * Remove last from batch->msg_list
+ * and splice the rest of batch->msg_list
+ * to last->sibling_list.
+ *
+ * batch->msg_list is a valid empty list
+ * at the end.
+ */
+ list_del_init(&last->sibling_list);
+ list_splice_tail_init(&batch->msg_list, &last->sibling_list);
+ batch->wr_cnt = 0;
+
+ ret = smbd_ib_post_send(sc, &first->wr);
+ if (ret) {
+ struct smbdirect_send_io *sibling, *next;
+
+ list_for_each_entry_safe(sibling, next, &last->sibling_list, sibling_list) {
+ list_del_init(&sibling->sibling_list);
+ smbd_free_send_io(sibling);
+ }
+ smbd_free_send_io(last);
+ }
+
+release_credit:
+ if (is_last && !ret && batch->credit) {
+ atomic_add(batch->credit, &sc->send_io.bcredits.count);
+ batch->credit = 0;
+ wake_up(&sc->send_io.bcredits.wait_queue);
+ }
+
+ return ret;
+}
+
+static int wait_for_credits(struct smbdirect_socket *sc,
+ wait_queue_head_t *waitq, atomic_t *total_credits,
+ int needed)
+{
+ int ret;
+
+ do {
+ if (atomic_sub_return(needed, total_credits) >= 0)
+ return 0;
+
+ atomic_add(needed, total_credits);
+ ret = wait_event_interruptible(*waitq,
+ atomic_read(total_credits) >= needed ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
+
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+ return -ENOTCONN;
+ else if (ret < 0)
+ return ret;
+ } while (true);
+}
+
+static int wait_for_send_bcredit(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *batch)
+{
+ int ret;
+
+ if (batch->credit)
+ return 0;
+
+ ret = wait_for_credits(sc,
+ &sc->send_io.bcredits.wait_queue,
+ &sc->send_io.bcredits.count,
+ 1);
+ if (ret)
+ return ret;
+
+ batch->credit = 1;
+ return 0;
+}
+
+static int wait_for_send_lcredit(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *batch)
+{
+ if (batch && (atomic_read(&sc->send_io.lcredits.count) <= 1)) {
+ int ret;
+
+ ret = smbd_send_batch_flush(sc, batch, false);
+ if (ret)
+ return ret;
+ }
+
+ return wait_for_credits(sc,
+ &sc->send_io.lcredits.wait_queue,
+ &sc->send_io.lcredits.count,
+ 1);
+}
+
+static int wait_for_send_credits(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *batch)
+{
+ if (batch &&
+ (batch->wr_cnt >= 16 || atomic_read(&sc->send_io.credits.count) <= 1)) {
+ int ret;
+
+ ret = smbd_send_batch_flush(sc, batch, false);
+ if (ret)
+ return ret;
+ }
+
+ return wait_for_credits(sc,
+ &sc->send_io.credits.wait_queue,
+ &sc->send_io.credits.count,
+ 1);
}
static int smbd_post_send_iter(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *batch,
struct iov_iter *iter,
int *_remaining_data_length)
{
struct smbdirect_socket_parameters *sp = &sc->parameters;
- int i, rc;
+ int rc;
int header_length;
int data_length;
struct smbdirect_send_io *request;
struct smbdirect_data_transfer *packet;
int new_credits = 0;
+ struct smbdirect_send_batch _batch;
-wait_lcredit:
- /* Wait for local send credits */
- rc = wait_event_interruptible(sc->send_io.lcredits.wait_queue,
- atomic_read(&sc->send_io.lcredits.count) > 0 ||
- sc->status != SMBDIRECT_SOCKET_CONNECTED);
- if (rc)
- goto err_wait_lcredit;
+ if (!batch) {
+ smbd_send_batch_init(&_batch, false, 0);
+ batch = &_batch;
+ }
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
- log_outgoing(ERR, "disconnected not sending on wait_credit\n");
+ rc = wait_for_send_bcredit(sc, batch);
+ if (rc) {
+ log_outgoing(ERR, "disconnected not sending on wait_bcredit\n");
rc = -EAGAIN;
- goto err_wait_lcredit;
- }
- if (unlikely(atomic_dec_return(&sc->send_io.lcredits.count) < 0)) {
- atomic_inc(&sc->send_io.lcredits.count);
- goto wait_lcredit;
+ goto err_wait_bcredit;
}
-wait_credit:
- /* Wait for send credits. A SMBD packet needs one credit */
- rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
- atomic_read(&sc->send_io.credits.count) > 0 ||
- sc->status != SMBDIRECT_SOCKET_CONNECTED);
- if (rc)
- goto err_wait_credit;
+ rc = wait_for_send_lcredit(sc, batch);
+ if (rc) {
+ log_outgoing(ERR, "disconnected not sending on wait_lcredit\n");
+ rc = -EAGAIN;
+ goto err_wait_lcredit;
+ }
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
+ rc = wait_for_send_credits(sc, batch);
+ if (rc) {
log_outgoing(ERR, "disconnected not sending on wait_credit\n");
rc = -EAGAIN;
goto err_wait_credit;
}
- if (unlikely(atomic_dec_return(&sc->send_io.credits.count) < 0)) {
- atomic_inc(&sc->send_io.credits.count);
- goto wait_credit;
+
+ new_credits = manage_credits_prior_sending(sc);
+ if (new_credits == 0 &&
+ atomic_read(&sc->send_io.credits.count) == 0 &&
+ atomic_read(&sc->recv_io.credits.count) == 0) {
+ queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
+ rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
+ atomic_read(&sc->send_io.credits.count) >= 1 ||
+ atomic_read(&sc->recv_io.credits.available) >= 1 ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+ rc = -ENOTCONN;
+ if (rc < 0) {
+ log_outgoing(ERR, "disconnected not sending on last credit\n");
+ rc = -EAGAIN;
+ goto err_wait_credit;
+ }
+
+ new_credits = manage_credits_prior_sending(sc);
}
- request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
- if (!request) {
- rc = -ENOMEM;
+ request = smbd_alloc_send_io(sc);
+ if (IS_ERR(request)) {
+ rc = PTR_ERR(request);
goto err_alloc;
}
- request->socket = sc;
memset(request->sge, 0, sizeof(request->sge));
/* Map the packet to DMA */
@@ -1215,9 +1527,6 @@ wait_credit:
/* Fill in the packet header */
packet->credits_requested = cpu_to_le16(sp->send_credit_target);
-
- new_credits = manage_credits_prior_sending(sc);
- atomic_add(new_credits, &sc->recv_io.credits.count);
packet->credits_granted = cpu_to_le16(new_credits);
packet->flags = 0;
@@ -1240,32 +1549,18 @@ wait_credit:
le32_to_cpu(packet->data_length),
le32_to_cpu(packet->remaining_data_length));
- /*
- * Now that we got a local and a remote credit
- * we add us as pending
- */
- atomic_inc(&sc->send_io.pending.count);
-
- rc = smbd_post_send(sc, request);
- if (!rc)
- return 0;
-
- if (atomic_dec_and_test(&sc->send_io.pending.count))
- wake_up(&sc->send_io.pending.zero_wait_queue);
+ rc = smbd_post_send(sc, batch, request);
+ if (!rc) {
+ if (batch != &_batch)
+ return 0;
- wake_up(&sc->send_io.pending.dec_wait_queue);
+ rc = smbd_send_batch_flush(sc, batch, true);
+ if (!rc)
+ return 0;
+ }
err_dma:
- for (i = 0; i < request->num_sge; i++)
- if (request->sge[i].addr)
- ib_dma_unmap_single(sc->ib.dev,
- request->sge[i].addr,
- request->sge[i].length,
- DMA_TO_DEVICE);
- mempool_free(request, sc->send_io.mem.pool);
-
- /* roll back the granted receive credits */
- atomic_sub(new_credits, &sc->recv_io.credits.count);
+ smbd_free_send_io(request);
err_alloc:
atomic_inc(&sc->send_io.credits.count);
@@ -1276,6 +1571,11 @@ err_wait_credit:
wake_up(&sc->send_io.lcredits.wait_queue);
err_wait_lcredit:
+ atomic_add(batch->credit, &sc->send_io.bcredits.count);
+ batch->credit = 0;
+ wake_up(&sc->send_io.bcredits.wait_queue);
+
+err_wait_bcredit:
return rc;
}
@@ -1289,10 +1589,11 @@ static int smbd_post_send_empty(struct smbdirect_socket *sc)
int remaining_data_length = 0;
sc->statistics.send_empty++;
- return smbd_post_send_iter(sc, NULL, &remaining_data_length);
+ return smbd_post_send_iter(sc, NULL, NULL, &remaining_data_length);
}
static int smbd_post_send_full_iter(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *batch,
struct iov_iter *iter,
int *_remaining_data_length)
{
@@ -1305,7 +1606,7 @@ static int smbd_post_send_full_iter(struct smbdirect_socket *sc,
*/
while (iov_iter_count(iter) > 0) {
- rc = smbd_post_send_iter(sc, iter, _remaining_data_length);
+ rc = smbd_post_send_iter(sc, batch, iter, _remaining_data_length);
if (rc < 0)
break;
}
@@ -2227,8 +2528,10 @@ int smbd_send(struct TCP_Server_Info *server,
struct smbdirect_socket_parameters *sp = &sc->parameters;
struct smb_rqst *rqst;
struct iov_iter iter;
+ struct smbdirect_send_batch batch;
unsigned int remaining_data_length, klen;
int rc, i, rqst_idx;
+ int error = 0;
if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
return -EAGAIN;
@@ -2253,6 +2556,7 @@ int smbd_send(struct TCP_Server_Info *server,
num_rqst, remaining_data_length);
rqst_idx = 0;
+ smbd_send_batch_init(&batch, false, 0);
do {
rqst = &rqst_array[rqst_idx];
@@ -2271,20 +2575,28 @@ int smbd_send(struct TCP_Server_Info *server,
klen += rqst->rq_iov[i].iov_len;
iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, klen);
- rc = smbd_post_send_full_iter(sc, &iter, &remaining_data_length);
- if (rc < 0)
+ rc = smbd_post_send_full_iter(sc, &batch, &iter, &remaining_data_length);
+ if (rc < 0) {
+ error = rc;
break;
+ }
if (iov_iter_count(&rqst->rq_iter) > 0) {
/* And then the data pages if there are any */
- rc = smbd_post_send_full_iter(sc, &rqst->rq_iter,
+ rc = smbd_post_send_full_iter(sc, &batch, &rqst->rq_iter,
&remaining_data_length);
- if (rc < 0)
+ if (rc < 0) {
+ error = rc;
break;
+ }
}
} while (++rqst_idx < num_rqst);
+ rc = smbd_send_batch_flush(sc, &batch, true);
+ if (unlikely(!rc && error))
+ rc = error;
+
/*
* As an optimization, we don't wait for individual I/O to finish
* before sending the next one.