1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2022 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "accel_dsa.h" 7 8 #include "spdk/stdinc.h" 9 10 #include "spdk/accel_module.h" 11 #include "spdk/log.h" 12 #include "spdk_internal/idxd.h" 13 14 #include "spdk/env.h" 15 #include "spdk/event.h" 16 #include "spdk/likely.h" 17 #include "spdk/thread.h" 18 #include "spdk/idxd.h" 19 #include "spdk/util.h" 20 #include "spdk/json.h" 21 #include "spdk/trace.h" 22 #include "spdk_internal/trace_defs.h" 23 24 static bool g_dsa_enable = false; 25 static bool g_kernel_mode = false; 26 27 enum channel_state { 28 IDXD_CHANNEL_ACTIVE, 29 IDXD_CHANNEL_ERROR, 30 }; 31 32 static bool g_dsa_initialized = false; 33 34 struct idxd_device { 35 struct spdk_idxd_device *dsa; 36 TAILQ_ENTRY(idxd_device) tailq; 37 }; 38 static TAILQ_HEAD(, idxd_device) g_dsa_devices = TAILQ_HEAD_INITIALIZER(g_dsa_devices); 39 static struct idxd_device *g_next_dev = NULL; 40 static uint32_t g_num_devices = 0; 41 static pthread_mutex_t g_dev_lock = PTHREAD_MUTEX_INITIALIZER; 42 43 struct idxd_task { 44 struct spdk_accel_task task; 45 struct idxd_io_channel *chan; 46 }; 47 48 struct idxd_io_channel { 49 struct spdk_idxd_io_channel *chan; 50 struct idxd_device *dev; 51 enum channel_state state; 52 struct spdk_poller *poller; 53 uint32_t num_outstanding; 54 TAILQ_HEAD(, spdk_accel_task) queued_tasks; 55 }; 56 57 static struct spdk_io_channel *dsa_get_io_channel(void); 58 59 static struct idxd_device * 60 idxd_select_device(struct idxd_io_channel *chan) 61 { 62 uint32_t count = 0; 63 struct idxd_device *dev; 64 uint32_t socket_id = spdk_env_get_socket_id(spdk_env_get_current_core()); 65 66 /* 67 * We allow channels to share underlying devices, 68 * selection is round-robin based with a limitation 69 * on how many channel can share one device. 70 */ 71 do { 72 /* select next device */ 73 pthread_mutex_lock(&g_dev_lock); 74 g_next_dev = TAILQ_NEXT(g_next_dev, tailq); 75 if (g_next_dev == NULL) { 76 g_next_dev = TAILQ_FIRST(&g_dsa_devices); 77 } 78 dev = g_next_dev; 79 pthread_mutex_unlock(&g_dev_lock); 80 81 if (socket_id != spdk_idxd_get_socket(dev->dsa)) { 82 continue; 83 } 84 85 /* 86 * Now see if a channel is available on this one. We only 87 * allow a specific number of channels to share a device 88 * to limit outstanding IO for flow control purposes. 89 */ 90 chan->chan = spdk_idxd_get_channel(dev->dsa); 91 if (chan->chan != NULL) { 92 SPDK_DEBUGLOG(accel_dsa, "On socket %d using device on socket %d\n", 93 socket_id, spdk_idxd_get_socket(dev->dsa)); 94 return dev; 95 } 96 } while (count++ < g_num_devices); 97 98 /* We are out of available channels and/or devices for the local socket. We fix the number 99 * of channels that we allocate per device and only allocate devices on the same socket 100 * that the current thread is on. If on a 2 socket system it may be possible to avoid 101 * this situation by spreading threads across the sockets. 102 */ 103 SPDK_ERRLOG("No more DSA devices available on the local socket.\n"); 104 return NULL; 105 } 106 107 static void 108 dsa_done(void *cb_arg, int status) 109 { 110 struct idxd_task *idxd_task = cb_arg; 111 struct idxd_io_channel *chan; 112 113 chan = idxd_task->chan; 114 115 assert(chan->num_outstanding > 0); 116 spdk_trace_record(TRACE_ACCEL_DSA_OP_COMPLETE, 0, 0, 0, chan->num_outstanding - 1); 117 chan->num_outstanding--; 118 119 spdk_accel_task_complete(&idxd_task->task, status); 120 } 121 122 static int 123 idxd_submit_dualcast(struct idxd_io_channel *ch, struct idxd_task *idxd_task, int flags) 124 { 125 struct spdk_accel_task *task = &idxd_task->task; 126 127 if (spdk_unlikely(task->d.iovcnt != 1 || task->d2.iovcnt != 1 || task->s.iovcnt != 1)) { 128 return -EINVAL; 129 } 130 131 if (spdk_unlikely(task->d.iovs[0].iov_len != task->s.iovs[0].iov_len || 132 task->d.iovs[0].iov_len != task->d2.iovs[0].iov_len)) { 133 return -EINVAL; 134 } 135 136 return spdk_idxd_submit_dualcast(ch->chan, task->d.iovs[0].iov_base, 137 task->d2.iovs[0].iov_base, task->s.iovs[0].iov_base, 138 task->d.iovs[0].iov_len, flags, dsa_done, idxd_task); 139 } 140 141 static int 142 _process_single_task(struct spdk_io_channel *ch, struct spdk_accel_task *task) 143 { 144 struct idxd_io_channel *chan = spdk_io_channel_get_ctx(ch); 145 struct idxd_task *idxd_task; 146 int rc = 0, flags = 0; 147 148 idxd_task = SPDK_CONTAINEROF(task, struct idxd_task, task); 149 idxd_task->chan = chan; 150 151 switch (task->op_code) { 152 case ACCEL_OPC_COPY: 153 rc = spdk_idxd_submit_copy(chan->chan, task->d.iovs, task->d.iovcnt, 154 task->s.iovs, task->s.iovcnt, flags, dsa_done, idxd_task); 155 break; 156 case ACCEL_OPC_DUALCAST: 157 rc = idxd_submit_dualcast(chan, idxd_task, flags); 158 break; 159 case ACCEL_OPC_COMPARE: 160 rc = spdk_idxd_submit_compare(chan->chan, task->s.iovs, task->s.iovcnt, 161 task->s2.iovs, task->s2.iovcnt, flags, 162 dsa_done, idxd_task); 163 break; 164 case ACCEL_OPC_FILL: 165 rc = spdk_idxd_submit_fill(chan->chan, task->d.iovs, task->d.iovcnt, 166 task->fill_pattern, flags, dsa_done, idxd_task); 167 break; 168 case ACCEL_OPC_CRC32C: 169 rc = spdk_idxd_submit_crc32c(chan->chan, task->s.iovs, task->s.iovcnt, task->seed, 170 task->crc_dst, flags, dsa_done, idxd_task); 171 break; 172 case ACCEL_OPC_COPY_CRC32C: 173 rc = spdk_idxd_submit_copy_crc32c(chan->chan, task->d.iovs, task->d.iovcnt, 174 task->s.iovs, task->s.iovcnt, 175 task->seed, task->crc_dst, flags, 176 dsa_done, idxd_task); 177 break; 178 default: 179 assert(false); 180 rc = -EINVAL; 181 break; 182 } 183 184 if (rc == 0) { 185 chan->num_outstanding++; 186 spdk_trace_record(TRACE_ACCEL_DSA_OP_SUBMIT, 0, 0, 0, chan->num_outstanding); 187 } 188 189 return rc; 190 } 191 192 static int 193 dsa_submit_tasks(struct spdk_io_channel *ch, struct spdk_accel_task *first_task) 194 { 195 struct idxd_io_channel *chan = spdk_io_channel_get_ctx(ch); 196 struct spdk_accel_task *task, *tmp; 197 int rc = 0; 198 199 task = first_task; 200 201 if (chan->state == IDXD_CHANNEL_ERROR) { 202 while (task) { 203 tmp = TAILQ_NEXT(task, link); 204 spdk_accel_task_complete(task, -EINVAL); 205 task = tmp; 206 } 207 return 0; 208 } 209 210 if (!TAILQ_EMPTY(&chan->queued_tasks)) { 211 goto queue_tasks; 212 } 213 214 /* The caller will either submit a single task or a group of tasks that are 215 * linked together but they cannot be on a list. For example, see idxd_poll() 216 * where a list of queued tasks is being resubmitted, the list they are on 217 * is initialized after saving off the first task from the list which is then 218 * passed in here. Similar thing is done in the accel framework. 219 */ 220 while (task) { 221 tmp = TAILQ_NEXT(task, link); 222 rc = _process_single_task(ch, task); 223 224 if (rc == -EBUSY) { 225 goto queue_tasks; 226 } else if (rc) { 227 spdk_accel_task_complete(task, rc); 228 } 229 task = tmp; 230 } 231 232 return 0; 233 234 queue_tasks: 235 while (task != NULL) { 236 tmp = TAILQ_NEXT(task, link); 237 TAILQ_INSERT_TAIL(&chan->queued_tasks, task, link); 238 task = tmp; 239 } 240 return 0; 241 } 242 243 static int 244 idxd_poll(void *arg) 245 { 246 struct idxd_io_channel *chan = arg; 247 struct spdk_accel_task *task = NULL; 248 struct idxd_task *idxd_task; 249 int count; 250 251 count = spdk_idxd_process_events(chan->chan); 252 253 /* Check if there are any pending ops to process if the channel is active */ 254 if (chan->state == IDXD_CHANNEL_ACTIVE) { 255 /* Submit queued tasks */ 256 if (!TAILQ_EMPTY(&chan->queued_tasks)) { 257 task = TAILQ_FIRST(&chan->queued_tasks); 258 idxd_task = SPDK_CONTAINEROF(task, struct idxd_task, task); 259 260 TAILQ_INIT(&chan->queued_tasks); 261 262 dsa_submit_tasks(spdk_io_channel_from_ctx(idxd_task->chan), task); 263 } 264 } 265 266 return count > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE; 267 } 268 269 static size_t 270 accel_dsa_get_ctx_size(void) 271 { 272 return sizeof(struct idxd_task); 273 } 274 275 static bool 276 dsa_supports_opcode(enum accel_opcode opc) 277 { 278 if (!g_dsa_initialized) { 279 return false; 280 } 281 282 switch (opc) { 283 case ACCEL_OPC_COPY: 284 case ACCEL_OPC_FILL: 285 case ACCEL_OPC_DUALCAST: 286 case ACCEL_OPC_COMPARE: 287 case ACCEL_OPC_CRC32C: 288 case ACCEL_OPC_COPY_CRC32C: 289 return true; 290 default: 291 return false; 292 } 293 } 294 295 static int accel_dsa_init(void); 296 static void accel_dsa_exit(void *ctx); 297 static void accel_dsa_write_config_json(struct spdk_json_write_ctx *w); 298 299 static struct spdk_accel_module_if g_dsa_module = { 300 .module_init = accel_dsa_init, 301 .module_fini = accel_dsa_exit, 302 .write_config_json = accel_dsa_write_config_json, 303 .get_ctx_size = accel_dsa_get_ctx_size, 304 .name = "dsa", 305 .supports_opcode = dsa_supports_opcode, 306 .get_io_channel = dsa_get_io_channel, 307 .submit_tasks = dsa_submit_tasks 308 }; 309 310 SPDK_ACCEL_MODULE_REGISTER(dsa, &g_dsa_module) 311 312 static int 313 dsa_create_cb(void *io_device, void *ctx_buf) 314 { 315 struct idxd_io_channel *chan = ctx_buf; 316 struct idxd_device *dsa; 317 318 dsa = idxd_select_device(chan); 319 if (dsa == NULL) { 320 SPDK_ERRLOG("Failed to get an idxd channel\n"); 321 return -EINVAL; 322 } 323 324 chan->dev = dsa; 325 chan->poller = SPDK_POLLER_REGISTER(idxd_poll, chan, 0); 326 TAILQ_INIT(&chan->queued_tasks); 327 chan->num_outstanding = 0; 328 chan->state = IDXD_CHANNEL_ACTIVE; 329 330 return 0; 331 } 332 333 static void 334 dsa_destroy_cb(void *io_device, void *ctx_buf) 335 { 336 struct idxd_io_channel *chan = ctx_buf; 337 338 spdk_poller_unregister(&chan->poller); 339 spdk_idxd_put_channel(chan->chan); 340 } 341 342 static struct spdk_io_channel * 343 dsa_get_io_channel(void) 344 { 345 return spdk_get_io_channel(&g_dsa_module); 346 } 347 348 static void 349 attach_cb(void *cb_ctx, struct spdk_idxd_device *idxd) 350 { 351 struct idxd_device *dev; 352 353 dev = calloc(1, sizeof(*dev)); 354 if (dev == NULL) { 355 SPDK_ERRLOG("Failed to allocate device struct\n"); 356 return; 357 } 358 359 dev->dsa = idxd; 360 if (g_next_dev == NULL) { 361 g_next_dev = dev; 362 } 363 364 TAILQ_INSERT_TAIL(&g_dsa_devices, dev, tailq); 365 g_num_devices++; 366 } 367 368 void 369 accel_dsa_enable_probe(bool kernel_mode) 370 { 371 g_kernel_mode = kernel_mode; 372 g_dsa_enable = true; 373 spdk_idxd_set_config(g_kernel_mode); 374 } 375 376 static bool 377 probe_cb(void *cb_ctx, struct spdk_pci_device *dev) 378 { 379 if (dev->id.device_id == PCI_DEVICE_ID_INTEL_DSA) { 380 return true; 381 } 382 383 return false; 384 } 385 386 static int 387 accel_dsa_init(void) 388 { 389 if (!g_dsa_enable) { 390 return -EINVAL; 391 } 392 393 if (spdk_idxd_probe(NULL, attach_cb, probe_cb) != 0) { 394 SPDK_ERRLOG("spdk_idxd_probe() failed\n"); 395 return -EINVAL; 396 } 397 398 if (TAILQ_EMPTY(&g_dsa_devices)) { 399 SPDK_NOTICELOG("no available dsa devices\n"); 400 return -EINVAL; 401 } 402 403 g_dsa_initialized = true; 404 SPDK_NOTICELOG("Accel framework DSA module initialized.\n"); 405 spdk_io_device_register(&g_dsa_module, dsa_create_cb, dsa_destroy_cb, 406 sizeof(struct idxd_io_channel), "dsa_accel_module"); 407 return 0; 408 } 409 410 static void 411 accel_dsa_exit(void *ctx) 412 { 413 struct idxd_device *dev; 414 415 if (g_dsa_initialized) { 416 spdk_io_device_unregister(&g_dsa_module, NULL); 417 g_dsa_initialized = false; 418 } 419 420 while (!TAILQ_EMPTY(&g_dsa_devices)) { 421 dev = TAILQ_FIRST(&g_dsa_devices); 422 TAILQ_REMOVE(&g_dsa_devices, dev, tailq); 423 spdk_idxd_detach(dev->dsa); 424 free(dev); 425 } 426 427 spdk_accel_module_finish(); 428 } 429 430 static void 431 accel_dsa_write_config_json(struct spdk_json_write_ctx *w) 432 { 433 if (g_dsa_enable) { 434 spdk_json_write_object_begin(w); 435 spdk_json_write_named_string(w, "method", "dsa_scan_accel_module"); 436 spdk_json_write_named_object_begin(w, "params"); 437 spdk_json_write_named_bool(w, "config_kernel_mode", g_kernel_mode); 438 spdk_json_write_object_end(w); 439 spdk_json_write_object_end(w); 440 } 441 } 442 443 SPDK_TRACE_REGISTER_FN(dsa_trace, "dsa", TRACE_GROUP_ACCEL_DSA) 444 { 445 spdk_trace_register_description("DSA_OP_SUBMIT", TRACE_ACCEL_DSA_OP_SUBMIT, OWNER_NONE, OBJECT_NONE, 446 0, 447 SPDK_TRACE_ARG_TYPE_INT, "count"); 448 spdk_trace_register_description("DSA_OP_COMPLETE", TRACE_ACCEL_DSA_OP_COMPLETE, OWNER_NONE, 449 OBJECT_NONE, 450 0, SPDK_TRACE_ARG_TYPE_INT, "count"); 451 } 452 453 SPDK_LOG_REGISTER_COMPONENT(accel_dsa) 454