xref: /linux/fs/smb/smbdirect/rw.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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