xref: /freebsd/sys/dev/mlx5/mlx5_fpga/mlx5fpga_trans.c (revision a0409676120c1e558d0ade943019934e0f15118d)
1 /*-
2  * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  *
32  * $FreeBSD$
33  */
34 
35 #include <dev/mlx5/mlx5_fpga/trans.h>
36 #include <dev/mlx5/mlx5_fpga/conn.h>
37 
38 enum mlx5_fpga_transaction_state {
39 	TRANS_STATE_NONE,
40 	TRANS_STATE_SEND,
41 	TRANS_STATE_WAIT,
42 	TRANS_STATE_COMPLETE,
43 };
44 
45 struct mlx5_fpga_trans_priv {
46 	const struct mlx5_fpga_transaction *user_trans;
47 	u8 tid;
48 	enum mlx5_fpga_transaction_state state;
49 	u8 status;
50 	u32 header[MLX5_ST_SZ_DW(fpga_shell_qp_packet)];
51 	struct mlx5_fpga_dma_buf buf;
52 	struct list_head list_item;
53 };
54 
55 struct mlx5_fpga_trans_device_state {
56 	spinlock_t lock; /* Protects all members of this struct */
57 	struct list_head free_queue;
58 	struct mlx5_fpga_trans_priv transactions[MLX5_FPGA_TID_COUNT];
59 };
60 
61 static struct mlx5_fpga_trans_priv *find_tid(struct mlx5_fpga_device *fdev,
62 					     u8 tid)
63 {
64 	if (tid >= MLX5_FPGA_TID_COUNT) {
65 		mlx5_fpga_warn(fdev, "Unexpected transaction ID %u\n", tid);
66 		return NULL;
67 	}
68 	return &fdev->trans->transactions[tid];
69 }
70 
71 static struct mlx5_fpga_trans_priv *alloc_tid(struct mlx5_fpga_device *fdev)
72 {
73 	struct mlx5_fpga_trans_priv *ret;
74 	unsigned long flags;
75 
76 	spin_lock_irqsave(&fdev->trans->lock, flags);
77 
78 	if (list_empty(&fdev->trans->free_queue)) {
79 		mlx5_fpga_dbg(fdev, "No free transaction ID available\n");
80 		ret = NULL;
81 		goto out;
82 	}
83 
84 	ret = list_first_entry(&fdev->trans->free_queue,
85 			       struct mlx5_fpga_trans_priv, list_item);
86 	list_del(&ret->list_item);
87 
88 	ret->state = TRANS_STATE_NONE;
89 out:
90 	spin_unlock_irqrestore(&fdev->trans->lock, flags);
91 	return ret;
92 }
93 
94 static void free_tid(struct mlx5_fpga_device *fdev,
95 		     struct mlx5_fpga_trans_priv *trans_priv)
96 {
97 	unsigned long flags;
98 
99 	spin_lock_irqsave(&fdev->trans->lock, flags);
100 	list_add_tail(&trans_priv->list_item, &fdev->trans->free_queue);
101 	spin_unlock_irqrestore(&fdev->trans->lock, flags);
102 }
103 
104 static void trans_complete(struct mlx5_fpga_device *fdev,
105 			   struct mlx5_fpga_trans_priv *trans_priv, u8 status)
106 {
107 	const struct mlx5_fpga_transaction *user_trans;
108 	unsigned long flags;
109 
110 	mlx5_fpga_dbg(fdev, "Transaction %u is complete with status %u\n",
111 		      trans_priv->tid, status);
112 
113 	spin_lock_irqsave(&fdev->trans->lock, flags);
114 	trans_priv->state = TRANS_STATE_COMPLETE;
115 	trans_priv->status = status;
116 	spin_unlock_irqrestore(&fdev->trans->lock, flags);
117 
118 	user_trans = trans_priv->user_trans;
119 	free_tid(fdev, trans_priv);
120 
121 	if (user_trans->complete1)
122 		user_trans->complete1(user_trans, status);
123 }
124 
125 static void trans_send_complete(struct mlx5_fpga_conn *conn,
126 				struct mlx5_fpga_device *fdev,
127 				struct mlx5_fpga_dma_buf *buf, u8 status)
128 {
129 	unsigned long flags;
130 	struct mlx5_fpga_trans_priv *trans_priv;
131 
132 	trans_priv = container_of(buf, struct mlx5_fpga_trans_priv, buf);
133 	mlx5_fpga_dbg(fdev, "send complete tid %u. Status: %u\n",
134 		      trans_priv->tid, status);
135 	if (status) {
136 		trans_complete(fdev, trans_priv, status);
137 		return;
138 	}
139 
140 	spin_lock_irqsave(&fdev->trans->lock, flags);
141 	if (trans_priv->state == TRANS_STATE_SEND)
142 		trans_priv->state = TRANS_STATE_WAIT;
143 	spin_unlock_irqrestore(&fdev->trans->lock, flags);
144 }
145 
146 static int trans_validate(struct mlx5_fpga_device *fdev, u64 addr, size_t size)
147 {
148 	if (size > MLX5_FPGA_TRANSACTION_MAX_SIZE) {
149 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at once. Max is %u\n",
150 			       size, MLX5_FPGA_TRANSACTION_MAX_SIZE);
151 		return -EINVAL;
152 	}
153 	if (size & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
154 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Must be full dwords\n",
155 			       size);
156 		return -EINVAL;
157 	}
158 	if (size < 1) {
159 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Empty transaction not allowed\n",
160 			       size);
161 		return -EINVAL;
162 	}
163 	if (addr & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
164 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at unaligned address %jx\n",
165 			       size, (uintmax_t)addr);
166 		return -EINVAL;
167 	}
168 	if ((addr >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS) !=
169 	    ((addr + size - 1) >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS)) {
170 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at address %jx. Crosses page boundary\n",
171 			       size, (uintmax_t)addr);
172 		return -EINVAL;
173 	}
174 	if (addr < mlx5_fpga_ddr_base_get(fdev)) {
175 		if (size != sizeof(u32)) {
176 			mlx5_fpga_warn(fdev, "Cannot access %zu bytes at cr-space address %jx. Must access a single dword\n",
177 				       size, (uintmax_t)addr);
178 			return -EINVAL;
179 		}
180 	}
181 	return 0;
182 }
183 
184 int mlx5_fpga_trans_exec(const struct mlx5_fpga_transaction *trans)
185 {
186 	struct mlx5_fpga_conn *conn = trans->conn;
187 	struct mlx5_fpga_trans_priv *trans_priv;
188 	u32 *header;
189 	int err;
190 
191 	if (!trans->complete1) {
192 		mlx5_fpga_warn(conn->fdev, "Transaction must have a completion callback\n");
193 		err = -EINVAL;
194 		goto out;
195 	}
196 
197 	err = trans_validate(conn->fdev, trans->addr, trans->size);
198 	if (err)
199 		goto out;
200 
201 	trans_priv = alloc_tid(conn->fdev);
202 	if (!trans_priv) {
203 		err = -EBUSY;
204 		goto out;
205 	}
206 	trans_priv->user_trans = trans;
207 	header = trans_priv->header;
208 
209 	memset(header, 0, sizeof(trans_priv->header));
210 	memset(&trans_priv->buf, 0, sizeof(trans_priv->buf));
211 	MLX5_SET(fpga_shell_qp_packet, header, type,
212 		 (trans->direction == MLX5_FPGA_WRITE) ?
213 		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE :
214 		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ);
215 	MLX5_SET(fpga_shell_qp_packet, header, tid, trans_priv->tid);
216 	MLX5_SET(fpga_shell_qp_packet, header, len, trans->size);
217 	MLX5_SET64(fpga_shell_qp_packet, header, address, trans->addr);
218 
219 	trans_priv->buf.sg[0].data = header;
220 	trans_priv->buf.sg[0].size = sizeof(trans_priv->header);
221 	if (trans->direction == MLX5_FPGA_WRITE) {
222 		trans_priv->buf.sg[1].data = trans->data;
223 		trans_priv->buf.sg[1].size = trans->size;
224 	}
225 
226 	trans_priv->buf.complete = trans_send_complete;
227 	trans_priv->state = TRANS_STATE_SEND;
228 
229 #ifdef NOT_YET
230 	/* XXXKIB */
231 	err = mlx5_fpga_conn_send(conn->fdev->shell_conn, &trans_priv->buf);
232 #else
233 	err = 0;
234 #endif
235 	if (err)
236 		goto out_buf_tid;
237 	goto out;
238 
239 out_buf_tid:
240 	free_tid(conn->fdev, trans_priv);
241 out:
242 	return err;
243 }
244 
245 void mlx5_fpga_trans_recv(void *cb_arg, struct mlx5_fpga_dma_buf *buf)
246 {
247 	struct mlx5_fpga_device *fdev = cb_arg;
248 	struct mlx5_fpga_trans_priv *trans_priv;
249 	size_t payload_len;
250 	u8 status = 0;
251 	u8 tid, type;
252 
253 	mlx5_fpga_dbg(fdev, "Rx QP message on core conn; %u bytes\n",
254 		      buf->sg[0].size);
255 
256 	if (buf->sg[0].size < MLX5_ST_SZ_BYTES(fpga_shell_qp_packet)) {
257 		mlx5_fpga_warn(fdev, "Short message %u bytes from device\n",
258 			       buf->sg[0].size);
259 		goto out;
260 	}
261 	payload_len = buf->sg[0].size - MLX5_ST_SZ_BYTES(fpga_shell_qp_packet);
262 
263 	tid = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, tid);
264 	trans_priv = find_tid(fdev, tid);
265 	if (!trans_priv)
266 		goto out;
267 
268 	type = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, type);
269 	switch (type) {
270 	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ_RESPONSE:
271 		if (trans_priv->user_trans->direction != MLX5_FPGA_READ) {
272 			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
273 				       type, trans_priv->user_trans->direction);
274 			status = -EIO;
275 			goto complete;
276 		}
277 		if (payload_len != trans_priv->user_trans->size) {
278 			mlx5_fpga_warn(fdev, "Incorrect transaction payload length %zu expected %zu\n",
279 				       payload_len,
280 				       trans_priv->user_trans->size);
281 			goto complete;
282 		}
283 		memcpy(trans_priv->user_trans->data,
284 		       MLX5_ADDR_OF(fpga_shell_qp_packet, buf->sg[0].data,
285 				    data), payload_len);
286 		break;
287 	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE_RESPONSE:
288 		if (trans_priv->user_trans->direction != MLX5_FPGA_WRITE) {
289 			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
290 				       type, trans_priv->user_trans->direction);
291 			status = -EIO;
292 			goto complete;
293 		}
294 		break;
295 	default:
296 		mlx5_fpga_warn(fdev, "Unexpected message type %u len %u from device\n",
297 			       type, buf->sg[0].size);
298 		status = -EIO;
299 		goto complete;
300 	}
301 
302 complete:
303 	trans_complete(fdev, trans_priv, status);
304 out:
305 	return;
306 }
307 
308 int mlx5_fpga_trans_device_init(struct mlx5_fpga_device *fdev)
309 {
310 	int ret = 0;
311 	int tid;
312 
313 	fdev->trans = kzalloc(sizeof(*fdev->trans), GFP_KERNEL);
314 	if (!fdev->trans) {
315 		ret = -ENOMEM;
316 		goto out;
317 	}
318 
319 	INIT_LIST_HEAD(&fdev->trans->free_queue);
320 	for (tid = 0; tid < ARRAY_SIZE(fdev->trans->transactions); tid++) {
321 		fdev->trans->transactions[tid].tid = tid;
322 		list_add_tail(&fdev->trans->transactions[tid].list_item,
323 			      &fdev->trans->free_queue);
324 	}
325 
326 	spin_lock_init(&fdev->trans->lock);
327 
328 out:
329 	return ret;
330 }
331 
332 void mlx5_fpga_trans_device_cleanup(struct mlx5_fpga_device *fdev)
333 {
334 	kfree(fdev->trans);
335 }
336