1*1249c01aSStefan Metzmacher // SPDX-License-Identifier: GPL-2.0-or-later 2*1249c01aSStefan Metzmacher /* 3*1249c01aSStefan Metzmacher * Copyright (C) 2017, Microsoft Corporation. 4*1249c01aSStefan Metzmacher * Copyright (C) 2018, LG Electronics. 5*1249c01aSStefan Metzmacher * Copyright (c) 2025, Stefan Metzmacher 6*1249c01aSStefan Metzmacher */ 7*1249c01aSStefan Metzmacher 8*1249c01aSStefan Metzmacher #include "internal.h" 9*1249c01aSStefan Metzmacher 10*1249c01aSStefan Metzmacher static int smbdirect_connection_wait_for_rw_credits(struct smbdirect_socket *sc, 11*1249c01aSStefan Metzmacher int credits) 12*1249c01aSStefan Metzmacher { 13*1249c01aSStefan Metzmacher return smbdirect_socket_wait_for_credits(sc, 14*1249c01aSStefan Metzmacher SMBDIRECT_SOCKET_CONNECTED, 15*1249c01aSStefan Metzmacher -ENOTCONN, 16*1249c01aSStefan Metzmacher &sc->rw_io.credits.wait_queue, 17*1249c01aSStefan Metzmacher &sc->rw_io.credits.count, 18*1249c01aSStefan Metzmacher credits); 19*1249c01aSStefan Metzmacher } 20*1249c01aSStefan Metzmacher 21*1249c01aSStefan Metzmacher static int smbdirect_connection_calc_rw_credits(struct smbdirect_socket *sc, 22*1249c01aSStefan Metzmacher const void *buf, 23*1249c01aSStefan Metzmacher size_t len) 24*1249c01aSStefan Metzmacher { 25*1249c01aSStefan Metzmacher return DIV_ROUND_UP(smbdirect_get_buf_page_count(buf, len), 26*1249c01aSStefan Metzmacher sc->rw_io.credits.num_pages); 27*1249c01aSStefan Metzmacher } 28*1249c01aSStefan Metzmacher 29*1249c01aSStefan Metzmacher static int smbdirect_connection_rdma_get_sg_list(void *buf, 30*1249c01aSStefan Metzmacher size_t size, 31*1249c01aSStefan Metzmacher struct scatterlist *sg_list, 32*1249c01aSStefan Metzmacher size_t nentries) 33*1249c01aSStefan Metzmacher { 34*1249c01aSStefan Metzmacher bool high = is_vmalloc_addr(buf); 35*1249c01aSStefan Metzmacher struct page *page; 36*1249c01aSStefan Metzmacher size_t offset, len; 37*1249c01aSStefan Metzmacher int i = 0; 38*1249c01aSStefan Metzmacher 39*1249c01aSStefan Metzmacher if (size == 0 || nentries < smbdirect_get_buf_page_count(buf, size)) 40*1249c01aSStefan Metzmacher return -EINVAL; 41*1249c01aSStefan Metzmacher 42*1249c01aSStefan Metzmacher offset = offset_in_page(buf); 43*1249c01aSStefan Metzmacher buf -= offset; 44*1249c01aSStefan Metzmacher while (size > 0) { 45*1249c01aSStefan Metzmacher len = min_t(size_t, PAGE_SIZE - offset, size); 46*1249c01aSStefan Metzmacher if (high) 47*1249c01aSStefan Metzmacher page = vmalloc_to_page(buf); 48*1249c01aSStefan Metzmacher else 49*1249c01aSStefan Metzmacher page = kmap_to_page(buf); 50*1249c01aSStefan Metzmacher 51*1249c01aSStefan Metzmacher if (!sg_list) 52*1249c01aSStefan Metzmacher return -EINVAL; 53*1249c01aSStefan Metzmacher sg_set_page(sg_list, page, len, offset); 54*1249c01aSStefan Metzmacher sg_list = sg_next(sg_list); 55*1249c01aSStefan Metzmacher 56*1249c01aSStefan Metzmacher buf += PAGE_SIZE; 57*1249c01aSStefan Metzmacher size -= len; 58*1249c01aSStefan Metzmacher offset = 0; 59*1249c01aSStefan Metzmacher i++; 60*1249c01aSStefan Metzmacher } 61*1249c01aSStefan Metzmacher 62*1249c01aSStefan Metzmacher return i; 63*1249c01aSStefan Metzmacher } 64*1249c01aSStefan Metzmacher 65*1249c01aSStefan Metzmacher static void smbdirect_connection_rw_io_free(struct smbdirect_rw_io *msg, 66*1249c01aSStefan Metzmacher enum dma_data_direction dir) 67*1249c01aSStefan Metzmacher { 68*1249c01aSStefan Metzmacher struct smbdirect_socket *sc = msg->socket; 69*1249c01aSStefan Metzmacher 70*1249c01aSStefan Metzmacher rdma_rw_ctx_destroy(&msg->rdma_ctx, 71*1249c01aSStefan Metzmacher sc->ib.qp, 72*1249c01aSStefan Metzmacher sc->ib.qp->port, 73*1249c01aSStefan Metzmacher msg->sgt.sgl, 74*1249c01aSStefan Metzmacher msg->sgt.nents, 75*1249c01aSStefan Metzmacher dir); 76*1249c01aSStefan Metzmacher sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); 77*1249c01aSStefan Metzmacher kfree(msg); 78*1249c01aSStefan Metzmacher } 79*1249c01aSStefan Metzmacher 80*1249c01aSStefan Metzmacher static void smbdirect_connection_rdma_rw_done(struct ib_cq *cq, struct ib_wc *wc, 81*1249c01aSStefan Metzmacher enum dma_data_direction dir) 82*1249c01aSStefan Metzmacher { 83*1249c01aSStefan Metzmacher struct smbdirect_rw_io *msg = 84*1249c01aSStefan Metzmacher container_of(wc->wr_cqe, struct smbdirect_rw_io, cqe); 85*1249c01aSStefan Metzmacher struct smbdirect_socket *sc = msg->socket; 86*1249c01aSStefan Metzmacher 87*1249c01aSStefan Metzmacher if (wc->status != IB_WC_SUCCESS) { 88*1249c01aSStefan Metzmacher msg->error = -EIO; 89*1249c01aSStefan Metzmacher pr_err("read/write error. opcode = %d, status = %s(%d)\n", 90*1249c01aSStefan Metzmacher wc->opcode, ib_wc_status_msg(wc->status), wc->status); 91*1249c01aSStefan Metzmacher if (wc->status != IB_WC_WR_FLUSH_ERR) 92*1249c01aSStefan Metzmacher smbdirect_socket_schedule_cleanup(sc, msg->error); 93*1249c01aSStefan Metzmacher } 94*1249c01aSStefan Metzmacher 95*1249c01aSStefan Metzmacher complete(msg->completion); 96*1249c01aSStefan Metzmacher } 97*1249c01aSStefan Metzmacher 98*1249c01aSStefan Metzmacher static void smbdirect_connection_rdma_read_done(struct ib_cq *cq, struct ib_wc *wc) 99*1249c01aSStefan Metzmacher { 100*1249c01aSStefan Metzmacher smbdirect_connection_rdma_rw_done(cq, wc, DMA_FROM_DEVICE); 101*1249c01aSStefan Metzmacher } 102*1249c01aSStefan Metzmacher 103*1249c01aSStefan Metzmacher static void smbdirect_connection_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc) 104*1249c01aSStefan Metzmacher { 105*1249c01aSStefan Metzmacher smbdirect_connection_rdma_rw_done(cq, wc, DMA_TO_DEVICE); 106*1249c01aSStefan Metzmacher } 107*1249c01aSStefan Metzmacher 108*1249c01aSStefan Metzmacher int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc, 109*1249c01aSStefan Metzmacher void *buf, size_t buf_len, 110*1249c01aSStefan Metzmacher struct smbdirect_buffer_descriptor_v1 *desc, 111*1249c01aSStefan Metzmacher size_t desc_len, 112*1249c01aSStefan Metzmacher bool is_read) 113*1249c01aSStefan Metzmacher { 114*1249c01aSStefan Metzmacher const struct smbdirect_socket_parameters *sp = &sc->parameters; 115*1249c01aSStefan Metzmacher enum dma_data_direction direction = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; 116*1249c01aSStefan Metzmacher struct smbdirect_rw_io *msg, *next_msg; 117*1249c01aSStefan Metzmacher size_t i; 118*1249c01aSStefan Metzmacher int ret; 119*1249c01aSStefan Metzmacher DECLARE_COMPLETION_ONSTACK(completion); 120*1249c01aSStefan Metzmacher struct ib_send_wr *first_wr; 121*1249c01aSStefan Metzmacher LIST_HEAD(msg_list); 122*1249c01aSStefan Metzmacher u8 *desc_buf; 123*1249c01aSStefan Metzmacher int credits_needed; 124*1249c01aSStefan Metzmacher size_t desc_buf_len, desc_num = 0; 125*1249c01aSStefan Metzmacher 126*1249c01aSStefan Metzmacher if (sc->status != SMBDIRECT_SOCKET_CONNECTED) 127*1249c01aSStefan Metzmacher return -ENOTCONN; 128*1249c01aSStefan Metzmacher 129*1249c01aSStefan Metzmacher if (buf_len > sp->max_read_write_size) 130*1249c01aSStefan Metzmacher return -EINVAL; 131*1249c01aSStefan Metzmacher 132*1249c01aSStefan Metzmacher /* calculate needed credits */ 133*1249c01aSStefan Metzmacher credits_needed = 0; 134*1249c01aSStefan Metzmacher desc_buf = buf; 135*1249c01aSStefan Metzmacher for (i = 0; i < desc_len / sizeof(*desc); i++) { 136*1249c01aSStefan Metzmacher if (!buf_len) 137*1249c01aSStefan Metzmacher break; 138*1249c01aSStefan Metzmacher 139*1249c01aSStefan Metzmacher desc_buf_len = le32_to_cpu(desc[i].length); 140*1249c01aSStefan Metzmacher if (!desc_buf_len) 141*1249c01aSStefan Metzmacher return -EINVAL; 142*1249c01aSStefan Metzmacher 143*1249c01aSStefan Metzmacher if (desc_buf_len > buf_len) { 144*1249c01aSStefan Metzmacher desc_buf_len = buf_len; 145*1249c01aSStefan Metzmacher desc[i].length = cpu_to_le32(desc_buf_len); 146*1249c01aSStefan Metzmacher buf_len = 0; 147*1249c01aSStefan Metzmacher } 148*1249c01aSStefan Metzmacher 149*1249c01aSStefan Metzmacher credits_needed += smbdirect_connection_calc_rw_credits(sc, 150*1249c01aSStefan Metzmacher desc_buf, 151*1249c01aSStefan Metzmacher desc_buf_len); 152*1249c01aSStefan Metzmacher desc_buf += desc_buf_len; 153*1249c01aSStefan Metzmacher buf_len -= desc_buf_len; 154*1249c01aSStefan Metzmacher desc_num++; 155*1249c01aSStefan Metzmacher } 156*1249c01aSStefan Metzmacher 157*1249c01aSStefan Metzmacher smbdirect_log_rdma_rw(sc, SMBDIRECT_LOG_INFO, 158*1249c01aSStefan Metzmacher "RDMA %s, len %zu, needed credits %d\n", 159*1249c01aSStefan Metzmacher str_read_write(is_read), buf_len, credits_needed); 160*1249c01aSStefan Metzmacher 161*1249c01aSStefan Metzmacher ret = smbdirect_connection_wait_for_rw_credits(sc, credits_needed); 162*1249c01aSStefan Metzmacher if (ret < 0) 163*1249c01aSStefan Metzmacher return ret; 164*1249c01aSStefan Metzmacher 165*1249c01aSStefan Metzmacher /* build rdma_rw_ctx for each descriptor */ 166*1249c01aSStefan Metzmacher desc_buf = buf; 167*1249c01aSStefan Metzmacher for (i = 0; i < desc_num; i++) { 168*1249c01aSStefan Metzmacher size_t page_count; 169*1249c01aSStefan Metzmacher 170*1249c01aSStefan Metzmacher msg = kzalloc_flex(*msg, sg_list, SG_CHUNK_SIZE, 171*1249c01aSStefan Metzmacher sc->rw_io.mem.gfp_mask); 172*1249c01aSStefan Metzmacher if (!msg) { 173*1249c01aSStefan Metzmacher ret = -ENOMEM; 174*1249c01aSStefan Metzmacher goto out; 175*1249c01aSStefan Metzmacher } 176*1249c01aSStefan Metzmacher 177*1249c01aSStefan Metzmacher desc_buf_len = le32_to_cpu(desc[i].length); 178*1249c01aSStefan Metzmacher page_count = smbdirect_get_buf_page_count(desc_buf, desc_buf_len); 179*1249c01aSStefan Metzmacher 180*1249c01aSStefan Metzmacher msg->socket = sc; 181*1249c01aSStefan Metzmacher msg->cqe.done = is_read ? 182*1249c01aSStefan Metzmacher smbdirect_connection_rdma_read_done : 183*1249c01aSStefan Metzmacher smbdirect_connection_rdma_write_done; 184*1249c01aSStefan Metzmacher msg->completion = &completion; 185*1249c01aSStefan Metzmacher 186*1249c01aSStefan Metzmacher msg->sgt.sgl = &msg->sg_list[0]; 187*1249c01aSStefan Metzmacher ret = sg_alloc_table_chained(&msg->sgt, 188*1249c01aSStefan Metzmacher page_count, 189*1249c01aSStefan Metzmacher msg->sg_list, 190*1249c01aSStefan Metzmacher SG_CHUNK_SIZE); 191*1249c01aSStefan Metzmacher if (ret) { 192*1249c01aSStefan Metzmacher ret = -ENOMEM; 193*1249c01aSStefan Metzmacher goto free_msg; 194*1249c01aSStefan Metzmacher } 195*1249c01aSStefan Metzmacher 196*1249c01aSStefan Metzmacher ret = smbdirect_connection_rdma_get_sg_list(desc_buf, 197*1249c01aSStefan Metzmacher desc_buf_len, 198*1249c01aSStefan Metzmacher msg->sgt.sgl, 199*1249c01aSStefan Metzmacher msg->sgt.orig_nents); 200*1249c01aSStefan Metzmacher if (ret < 0) 201*1249c01aSStefan Metzmacher goto free_table; 202*1249c01aSStefan Metzmacher 203*1249c01aSStefan Metzmacher ret = rdma_rw_ctx_init(&msg->rdma_ctx, 204*1249c01aSStefan Metzmacher sc->ib.qp, 205*1249c01aSStefan Metzmacher sc->ib.qp->port, 206*1249c01aSStefan Metzmacher msg->sgt.sgl, 207*1249c01aSStefan Metzmacher page_count, 208*1249c01aSStefan Metzmacher 0, 209*1249c01aSStefan Metzmacher le64_to_cpu(desc[i].offset), 210*1249c01aSStefan Metzmacher le32_to_cpu(desc[i].token), 211*1249c01aSStefan Metzmacher direction); 212*1249c01aSStefan Metzmacher if (ret < 0) { 213*1249c01aSStefan Metzmacher pr_err("failed to init rdma_rw_ctx: %d\n", ret); 214*1249c01aSStefan Metzmacher goto free_table; 215*1249c01aSStefan Metzmacher } 216*1249c01aSStefan Metzmacher 217*1249c01aSStefan Metzmacher list_add_tail(&msg->list, &msg_list); 218*1249c01aSStefan Metzmacher desc_buf += desc_buf_len; 219*1249c01aSStefan Metzmacher } 220*1249c01aSStefan Metzmacher 221*1249c01aSStefan Metzmacher /* concatenate work requests of rdma_rw_ctxs */ 222*1249c01aSStefan Metzmacher first_wr = NULL; 223*1249c01aSStefan Metzmacher list_for_each_entry_reverse(msg, &msg_list, list) { 224*1249c01aSStefan Metzmacher first_wr = rdma_rw_ctx_wrs(&msg->rdma_ctx, 225*1249c01aSStefan Metzmacher sc->ib.qp, 226*1249c01aSStefan Metzmacher sc->ib.qp->port, 227*1249c01aSStefan Metzmacher &msg->cqe, 228*1249c01aSStefan Metzmacher first_wr); 229*1249c01aSStefan Metzmacher } 230*1249c01aSStefan Metzmacher 231*1249c01aSStefan Metzmacher ret = ib_post_send(sc->ib.qp, first_wr, NULL); 232*1249c01aSStefan Metzmacher if (ret) { 233*1249c01aSStefan Metzmacher pr_err("failed to post send wr for RDMA R/W: %d\n", ret); 234*1249c01aSStefan Metzmacher goto out; 235*1249c01aSStefan Metzmacher } 236*1249c01aSStefan Metzmacher 237*1249c01aSStefan Metzmacher msg = list_last_entry(&msg_list, struct smbdirect_rw_io, list); 238*1249c01aSStefan Metzmacher wait_for_completion(&completion); 239*1249c01aSStefan Metzmacher ret = msg->error; 240*1249c01aSStefan Metzmacher out: 241*1249c01aSStefan Metzmacher list_for_each_entry_safe(msg, next_msg, &msg_list, list) { 242*1249c01aSStefan Metzmacher list_del(&msg->list); 243*1249c01aSStefan Metzmacher smbdirect_connection_rw_io_free(msg, direction); 244*1249c01aSStefan Metzmacher } 245*1249c01aSStefan Metzmacher atomic_add(credits_needed, &sc->rw_io.credits.count); 246*1249c01aSStefan Metzmacher wake_up(&sc->rw_io.credits.wait_queue); 247*1249c01aSStefan Metzmacher return ret; 248*1249c01aSStefan Metzmacher 249*1249c01aSStefan Metzmacher free_table: 250*1249c01aSStefan Metzmacher sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); 251*1249c01aSStefan Metzmacher free_msg: 252*1249c01aSStefan Metzmacher kfree(msg); 253*1249c01aSStefan Metzmacher goto out; 254*1249c01aSStefan Metzmacher } 255*1249c01aSStefan Metzmacher __SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_rdma_xmit); 256