xref: /freebsd/sys/dev/mlx5/mlx5_fpga/mlx5fpga_trans.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
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 
33 #include <dev/mlx5/mlx5_fpga/trans.h>
34 #include <dev/mlx5/mlx5_fpga/conn.h>
35 
36 enum mlx5_fpga_transaction_state {
37 	TRANS_STATE_NONE,
38 	TRANS_STATE_SEND,
39 	TRANS_STATE_WAIT,
40 	TRANS_STATE_COMPLETE,
41 };
42 
43 struct mlx5_fpga_trans_priv {
44 	const struct mlx5_fpga_transaction *user_trans;
45 	u8 tid;
46 	enum mlx5_fpga_transaction_state state;
47 	u8 status;
48 	u32 header[MLX5_ST_SZ_DW(fpga_shell_qp_packet)];
49 	struct mlx5_fpga_dma_buf buf;
50 	struct list_head list_item;
51 };
52 
53 struct mlx5_fpga_trans_device_state {
54 	spinlock_t lock; /* Protects all members of this struct */
55 	struct list_head free_queue;
56 	struct mlx5_fpga_trans_priv transactions[MLX5_FPGA_TID_COUNT];
57 };
58 
find_tid(struct mlx5_fpga_device * fdev,u8 tid)59 static struct mlx5_fpga_trans_priv *find_tid(struct mlx5_fpga_device *fdev,
60 					     u8 tid)
61 {
62 	if (tid >= MLX5_FPGA_TID_COUNT) {
63 		mlx5_fpga_warn(fdev, "Unexpected transaction ID %u\n", tid);
64 		return NULL;
65 	}
66 	return &fdev->trans->transactions[tid];
67 }
68 
alloc_tid(struct mlx5_fpga_device * fdev)69 static struct mlx5_fpga_trans_priv *alloc_tid(struct mlx5_fpga_device *fdev)
70 {
71 	struct mlx5_fpga_trans_priv *ret;
72 	unsigned long flags;
73 
74 	spin_lock_irqsave(&fdev->trans->lock, flags);
75 
76 	if (list_empty(&fdev->trans->free_queue)) {
77 		mlx5_fpga_dbg(fdev, "No free transaction ID available\n");
78 		ret = NULL;
79 		goto out;
80 	}
81 
82 	ret = list_first_entry(&fdev->trans->free_queue,
83 			       struct mlx5_fpga_trans_priv, list_item);
84 	list_del(&ret->list_item);
85 
86 	ret->state = TRANS_STATE_NONE;
87 out:
88 	spin_unlock_irqrestore(&fdev->trans->lock, flags);
89 	return ret;
90 }
91 
free_tid(struct mlx5_fpga_device * fdev,struct mlx5_fpga_trans_priv * trans_priv)92 static void free_tid(struct mlx5_fpga_device *fdev,
93 		     struct mlx5_fpga_trans_priv *trans_priv)
94 {
95 	unsigned long flags;
96 
97 	spin_lock_irqsave(&fdev->trans->lock, flags);
98 	list_add_tail(&trans_priv->list_item, &fdev->trans->free_queue);
99 	spin_unlock_irqrestore(&fdev->trans->lock, flags);
100 }
101 
trans_complete(struct mlx5_fpga_device * fdev,struct mlx5_fpga_trans_priv * trans_priv,u8 status)102 static void trans_complete(struct mlx5_fpga_device *fdev,
103 			   struct mlx5_fpga_trans_priv *trans_priv, u8 status)
104 {
105 	const struct mlx5_fpga_transaction *user_trans;
106 	unsigned long flags;
107 
108 	mlx5_fpga_dbg(fdev, "Transaction %u is complete with status %u\n",
109 		      trans_priv->tid, status);
110 
111 	spin_lock_irqsave(&fdev->trans->lock, flags);
112 	trans_priv->state = TRANS_STATE_COMPLETE;
113 	trans_priv->status = status;
114 	spin_unlock_irqrestore(&fdev->trans->lock, flags);
115 
116 	user_trans = trans_priv->user_trans;
117 	free_tid(fdev, trans_priv);
118 
119 	if (user_trans->complete1)
120 		user_trans->complete1(user_trans, status);
121 }
122 
trans_send_complete(struct mlx5_fpga_conn * conn,struct mlx5_fpga_device * fdev,struct mlx5_fpga_dma_buf * buf,u8 status)123 static void trans_send_complete(struct mlx5_fpga_conn *conn,
124 				struct mlx5_fpga_device *fdev,
125 				struct mlx5_fpga_dma_buf *buf, u8 status)
126 {
127 	unsigned long flags;
128 	struct mlx5_fpga_trans_priv *trans_priv;
129 
130 	trans_priv = container_of(buf, struct mlx5_fpga_trans_priv, buf);
131 	mlx5_fpga_dbg(fdev, "send complete tid %u. Status: %u\n",
132 		      trans_priv->tid, status);
133 	if (status) {
134 		trans_complete(fdev, trans_priv, status);
135 		return;
136 	}
137 
138 	spin_lock_irqsave(&fdev->trans->lock, flags);
139 	if (trans_priv->state == TRANS_STATE_SEND)
140 		trans_priv->state = TRANS_STATE_WAIT;
141 	spin_unlock_irqrestore(&fdev->trans->lock, flags);
142 }
143 
trans_validate(struct mlx5_fpga_device * fdev,u64 addr,size_t size)144 static int trans_validate(struct mlx5_fpga_device *fdev, u64 addr, size_t size)
145 {
146 	if (size > MLX5_FPGA_TRANSACTION_MAX_SIZE) {
147 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at once. Max is %u\n",
148 			       size, MLX5_FPGA_TRANSACTION_MAX_SIZE);
149 		return -EINVAL;
150 	}
151 	if (size & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
152 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Must be full dwords\n",
153 			       size);
154 		return -EINVAL;
155 	}
156 	if (size < 1) {
157 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Empty transaction not allowed\n",
158 			       size);
159 		return -EINVAL;
160 	}
161 	if (addr & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
162 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at unaligned address %jx\n",
163 			       size, (uintmax_t)addr);
164 		return -EINVAL;
165 	}
166 	if ((addr >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS) !=
167 	    ((addr + size - 1) >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS)) {
168 		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at address %jx. Crosses page boundary\n",
169 			       size, (uintmax_t)addr);
170 		return -EINVAL;
171 	}
172 	if (addr < mlx5_fpga_ddr_base_get(fdev)) {
173 		if (size != sizeof(u32)) {
174 			mlx5_fpga_warn(fdev, "Cannot access %zu bytes at cr-space address %jx. Must access a single dword\n",
175 				       size, (uintmax_t)addr);
176 			return -EINVAL;
177 		}
178 	}
179 	return 0;
180 }
181 
mlx5_fpga_trans_exec(const struct mlx5_fpga_transaction * trans)182 int mlx5_fpga_trans_exec(const struct mlx5_fpga_transaction *trans)
183 {
184 	struct mlx5_fpga_conn *conn = trans->conn;
185 	struct mlx5_fpga_trans_priv *trans_priv;
186 	u32 *header;
187 	int err;
188 
189 	if (!trans->complete1) {
190 		mlx5_fpga_warn(conn->fdev, "Transaction must have a completion callback\n");
191 		err = -EINVAL;
192 		goto out;
193 	}
194 
195 	err = trans_validate(conn->fdev, trans->addr, trans->size);
196 	if (err)
197 		goto out;
198 
199 	trans_priv = alloc_tid(conn->fdev);
200 	if (!trans_priv) {
201 		err = -EBUSY;
202 		goto out;
203 	}
204 	trans_priv->user_trans = trans;
205 	header = trans_priv->header;
206 
207 	memset(header, 0, sizeof(trans_priv->header));
208 	memset(&trans_priv->buf, 0, sizeof(trans_priv->buf));
209 	MLX5_SET(fpga_shell_qp_packet, header, type,
210 		 (trans->direction == MLX5_FPGA_WRITE) ?
211 		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE :
212 		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ);
213 	MLX5_SET(fpga_shell_qp_packet, header, tid, trans_priv->tid);
214 	MLX5_SET(fpga_shell_qp_packet, header, len, trans->size);
215 	MLX5_SET64(fpga_shell_qp_packet, header, address, trans->addr);
216 
217 	trans_priv->buf.sg[0].data = header;
218 	trans_priv->buf.sg[0].size = sizeof(trans_priv->header);
219 	if (trans->direction == MLX5_FPGA_WRITE) {
220 		trans_priv->buf.sg[1].data = trans->data;
221 		trans_priv->buf.sg[1].size = trans->size;
222 	}
223 
224 	trans_priv->buf.complete = trans_send_complete;
225 	trans_priv->state = TRANS_STATE_SEND;
226 
227 #ifdef NOT_YET
228 	/* XXXKIB */
229 	err = mlx5_fpga_conn_send(conn->fdev->shell_conn, &trans_priv->buf);
230 #else
231 	err = 0;
232 #endif
233 	if (err)
234 		goto out_buf_tid;
235 	goto out;
236 
237 out_buf_tid:
238 	free_tid(conn->fdev, trans_priv);
239 out:
240 	return err;
241 }
242 
mlx5_fpga_trans_recv(void * cb_arg,struct mlx5_fpga_dma_buf * buf)243 void mlx5_fpga_trans_recv(void *cb_arg, struct mlx5_fpga_dma_buf *buf)
244 {
245 	struct mlx5_fpga_device *fdev = cb_arg;
246 	struct mlx5_fpga_trans_priv *trans_priv;
247 	size_t payload_len;
248 	u8 status = 0;
249 	u8 tid, type;
250 
251 	mlx5_fpga_dbg(fdev, "Rx QP message on core conn; %u bytes\n",
252 		      buf->sg[0].size);
253 
254 	if (buf->sg[0].size < MLX5_ST_SZ_BYTES(fpga_shell_qp_packet)) {
255 		mlx5_fpga_warn(fdev, "Short message %u bytes from device\n",
256 			       buf->sg[0].size);
257 		goto out;
258 	}
259 	payload_len = buf->sg[0].size - MLX5_ST_SZ_BYTES(fpga_shell_qp_packet);
260 
261 	tid = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, tid);
262 	trans_priv = find_tid(fdev, tid);
263 	if (!trans_priv)
264 		goto out;
265 
266 	type = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, type);
267 	switch (type) {
268 	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ_RESPONSE:
269 		if (trans_priv->user_trans->direction != MLX5_FPGA_READ) {
270 			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
271 				       type, trans_priv->user_trans->direction);
272 			status = -EIO;
273 			goto complete;
274 		}
275 		if (payload_len != trans_priv->user_trans->size) {
276 			mlx5_fpga_warn(fdev, "Incorrect transaction payload length %zu expected %zu\n",
277 				       payload_len,
278 				       trans_priv->user_trans->size);
279 			goto complete;
280 		}
281 		memcpy(trans_priv->user_trans->data,
282 		       MLX5_ADDR_OF(fpga_shell_qp_packet, buf->sg[0].data,
283 				    data), payload_len);
284 		break;
285 	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE_RESPONSE:
286 		if (trans_priv->user_trans->direction != MLX5_FPGA_WRITE) {
287 			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
288 				       type, trans_priv->user_trans->direction);
289 			status = -EIO;
290 			goto complete;
291 		}
292 		break;
293 	default:
294 		mlx5_fpga_warn(fdev, "Unexpected message type %u len %u from device\n",
295 			       type, buf->sg[0].size);
296 		status = -EIO;
297 		goto complete;
298 	}
299 
300 complete:
301 	trans_complete(fdev, trans_priv, status);
302 out:
303 	return;
304 }
305 
mlx5_fpga_trans_device_init(struct mlx5_fpga_device * fdev)306 int mlx5_fpga_trans_device_init(struct mlx5_fpga_device *fdev)
307 {
308 	int ret = 0;
309 	int tid;
310 
311 	fdev->trans = kzalloc(sizeof(*fdev->trans), GFP_KERNEL);
312 	if (!fdev->trans) {
313 		ret = -ENOMEM;
314 		goto out;
315 	}
316 
317 	INIT_LIST_HEAD(&fdev->trans->free_queue);
318 	for (tid = 0; tid < ARRAY_SIZE(fdev->trans->transactions); tid++) {
319 		fdev->trans->transactions[tid].tid = tid;
320 		list_add_tail(&fdev->trans->transactions[tid].list_item,
321 			      &fdev->trans->free_queue);
322 	}
323 
324 	spin_lock_init(&fdev->trans->lock);
325 
326 out:
327 	return ret;
328 }
329 
mlx5_fpga_trans_device_cleanup(struct mlx5_fpga_device * fdev)330 void mlx5_fpga_trans_device_cleanup(struct mlx5_fpga_device *fdev)
331 {
332 	kfree(fdev->trans);
333 }
334