xref: /spdk/lib/idxd/idxd_kernel.c (revision 510f4c134a21b45ff3a5add9ebc6c6cf7e49aeab)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (c) Intel Corporation.
3  *   All rights reserved.
4  *
5  */
6 
7 #include "spdk/stdinc.h"
8 
9 #include <accel-config/libaccel_config.h>
10 
11 #include "spdk/env.h"
12 #include "spdk/util.h"
13 #include "spdk/memory.h"
14 #include "spdk/likely.h"
15 
16 #include "spdk/log.h"
17 #include "spdk_internal/idxd.h"
18 
19 #include "idxd_internal.h"
20 
21 struct spdk_kernel_idxd_device {
22 	struct spdk_idxd_device	idxd;
23 	struct accfg_ctx	*ctx;
24 
25 	unsigned int		max_batch_size;
26 	unsigned int		max_xfer_size;
27 	unsigned int		max_xfer_bits;
28 
29 	/* We only use a single WQ */
30 	struct accfg_wq		*wq;
31 	int			fd;
32 	void			*portal;
33 };
34 
35 #define __kernel_idxd(idxd) SPDK_CONTAINEROF(idxd, struct spdk_kernel_idxd_device, idxd)
36 
37 static void
38 kernel_idxd_device_destruct(struct spdk_idxd_device *idxd)
39 {
40 	struct spdk_kernel_idxd_device *kernel_idxd = __kernel_idxd(idxd);
41 
42 	if (kernel_idxd->portal != NULL) {
43 		munmap(kernel_idxd->portal, 0x1000);
44 	}
45 
46 	if (kernel_idxd->fd >= 0) {
47 		close(kernel_idxd->fd);
48 	}
49 
50 	accfg_unref(kernel_idxd->ctx);
51 	free(kernel_idxd);
52 }
53 
54 static struct spdk_idxd_impl g_kernel_idxd_impl;
55 
56 static int
57 kernel_idxd_probe(void *cb_ctx, spdk_idxd_attach_cb attach_cb, spdk_idxd_probe_cb probe_cb)
58 {
59 	int rc;
60 	struct accfg_ctx *ctx;
61 	struct accfg_device *device;
62 
63 	rc = accfg_new(&ctx);
64 	if (rc < 0) {
65 		SPDK_ERRLOG("Unable to allocate accel-config context\n");
66 		return rc;
67 	}
68 
69 	/* Loop over each IDXD device */
70 	accfg_device_foreach(ctx, device) {
71 		enum accfg_device_state dstate;
72 		struct spdk_kernel_idxd_device *kernel_idxd;
73 		struct accfg_wq *wq;
74 
75 		/* Make sure that the device is enabled */
76 		dstate = accfg_device_get_state(device);
77 		if (dstate != ACCFG_DEVICE_ENABLED) {
78 			continue;
79 		}
80 
81 		kernel_idxd = calloc(1, sizeof(struct spdk_kernel_idxd_device));
82 		if (kernel_idxd == NULL) {
83 			SPDK_ERRLOG("Failed to allocate memory for kernel_idxd device.\n");
84 			/* TODO: Goto error cleanup */
85 			return -ENOMEM;
86 		}
87 
88 		kernel_idxd->max_batch_size = accfg_device_get_max_batch_size(device);
89 		kernel_idxd->max_xfer_size = accfg_device_get_max_transfer_size(device);
90 		kernel_idxd->idxd.socket_id = accfg_device_get_numa_node(device);
91 		kernel_idxd->idxd.impl = &g_kernel_idxd_impl;
92 		kernel_idxd->fd = -1;
93 		kernel_idxd->idxd.version = accfg_device_get_version(device);
94 
95 		accfg_wq_foreach(device, wq) {
96 			enum accfg_wq_state wstate;
97 			enum accfg_wq_mode mode;
98 			enum accfg_wq_type type;
99 			int major, minor;
100 			char path[1024];
101 
102 			wstate = accfg_wq_get_state(wq);
103 			if (wstate != ACCFG_WQ_ENABLED) {
104 				continue;
105 			}
106 
107 			type = accfg_wq_get_type(wq);
108 			if (type != ACCFG_WQT_USER) {
109 				continue;
110 			}
111 
112 			/* TODO: For now, only support dedicated WQ */
113 			mode = accfg_wq_get_mode(wq);
114 			if (mode != ACCFG_WQ_DEDICATED) {
115 				continue;
116 			}
117 
118 			major = accfg_device_get_cdev_major(device);
119 			if (major < 0) {
120 				continue;
121 			}
122 
123 			minor = accfg_wq_get_cdev_minor(wq);
124 			if (minor < 0) {
125 				continue;
126 			}
127 
128 			/* Map the portal */
129 			snprintf(path, sizeof(path), "/dev/char/%u:%u", major, minor);
130 			kernel_idxd->fd = open(path, O_RDWR);
131 			if (kernel_idxd->fd < 0) {
132 				SPDK_ERRLOG("Can not open the WQ file descriptor on path=%s\n",
133 					    path);
134 				continue;
135 			}
136 
137 			kernel_idxd->portal = mmap(NULL, 0x1000, PROT_WRITE,
138 						   MAP_SHARED | MAP_POPULATE, kernel_idxd->fd, 0);
139 			if (kernel_idxd->portal == MAP_FAILED) {
140 				perror("mmap");
141 				continue;
142 			}
143 
144 			kernel_idxd->wq = wq;
145 
146 			/* Since we only use a single WQ, the total size is the size of this WQ */
147 			kernel_idxd->idxd.total_wq_size = accfg_wq_get_size(wq);
148 			kernel_idxd->idxd.chan_per_device = (kernel_idxd->idxd.total_wq_size >= 128) ? 8 : 4;
149 			/* TODO: Handle BOF when we add support for shared WQ */
150 			/* wq_ctx->bof = accfg_wq_get_block_on_fault(wq); */
151 
152 			/* We only use a single WQ, so once we've found one we can stop looking. */
153 			break;
154 		}
155 
156 		if (kernel_idxd->idxd.total_wq_size > 0) {
157 			/* This device has at least 1 WQ available, so ask the user if they want to use it. */
158 			attach_cb(cb_ctx, &kernel_idxd->idxd);
159 		} else {
160 			kernel_idxd_device_destruct(&kernel_idxd->idxd);
161 		}
162 	}
163 
164 	return 0;
165 }
166 
167 static void
168 kernel_idxd_dump_sw_error(struct spdk_idxd_device *idxd, void *portal)
169 {
170 	/* Need to be enhanced later */
171 }
172 
173 static char *
174 kernel_idxd_portal_get_addr(struct spdk_idxd_device *idxd)
175 {
176 	struct spdk_kernel_idxd_device *kernel_idxd = __kernel_idxd(idxd);
177 
178 	return kernel_idxd->portal;
179 }
180 
181 static struct spdk_idxd_impl g_kernel_idxd_impl = {
182 	.name			= "kernel",
183 	.probe			= kernel_idxd_probe,
184 	.destruct		= kernel_idxd_device_destruct,
185 	.dump_sw_error		= kernel_idxd_dump_sw_error,
186 	.portal_get_addr	= kernel_idxd_portal_get_addr,
187 };
188 
189 SPDK_IDXD_IMPL_REGISTER(kernel, &g_kernel_idxd_impl);
190