From 673a6703b116f91098eb4a01ac77d27a9f286b5b Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 29 Jun 2026 10:59:55 -0700 Subject: [PATCH] Intel QAT: interleave instances across devices for multi-device utilization --- .wolfssl_known_macro_extras | 1 + wolfcrypt/src/port/intel/quickassist.c | 89 +++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 79cf7f6eaf..c780e72f7c 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -507,6 +507,7 @@ PRINT_SESSION_STATS PTHREAD_STACK_MIN QAT_ENABLE_HASH QAT_ENABLE_RNG +QAT_NO_DEV_INTERLEAVE QAT_USE_POLLING_CHECK RC_NO_RNG REDIRECTION_IN3_KEYELMID diff --git a/wolfcrypt/src/port/intel/quickassist.c b/wolfcrypt/src/port/intel/quickassist.c index 5c6960e019..05d7e05a13 100644 --- a/wolfcrypt/src/port/intel/quickassist.c +++ b/wolfcrypt/src/port/intel/quickassist.c @@ -323,13 +323,93 @@ void IntelQaHardwareStop(void) printf("IntelQA: Stop\n"); } -/* Returns nonzero when the QAT crypto service is running. Lets the memory - * layer fall back to regular memory when the service is not started. */ +/* Returns nonzero when the QAT crypto service is running. The memory layer + * uses this to decide whether a failed NUMA allocation should fall back to + * regular memory (service not started -> software mode) or remain NULL (real + * NUMA exhaustion while the device is in use). */ WOLFSSL_LOCAL int IntelQaIsStarted(void) { return (g_cyServiceStarted == CPA_TRUE) ? 1 : 0; } +#ifndef QAT_NO_DEV_INTERLEAVE +/* Reorder the instance handle list so instances interleave across QAT + * devices (packages). cpaCyGetInstances() returns them grouped by device + * (all of dev0, then dev1, ...). The per-thread round-robin assignment in + * IntelQaInit() (devId = g_instCounter % g_numInstances) would then pile a + * thread count lower than the instance count onto the first device(s), + * leaving later devices idle. Interleaving makes consecutive devIds land on + * different devices, so low/mid thread counts spread over every device. + * Order is unchanged when only one device is present, and at thread counts + * >= g_numInstances every instance is used either way. Non-fatal: on any + * failure the original order is kept. */ +static void IntelQaInterleaveInstances(CpaInstanceHandle* instances, + Cpa16U count) +{ + CpaInstanceHandle* sorted; + CpaInstanceInfo2* info; + Cpa16U* pkg; + CpaStatus status; + int i, p, idx, seen, round, maxPkg, out; + + if (instances == NULL || count <= 1) { + return; + } + + sorted = (CpaInstanceHandle*)XMALLOC(sizeof(CpaInstanceHandle) * count, + NULL, DYNAMIC_TYPE_ASYNC); + pkg = (Cpa16U*)XMALLOC(sizeof(Cpa16U) * count, NULL, DYNAMIC_TYPE_ASYNC); + info = (CpaInstanceInfo2*)XMALLOC(sizeof(CpaInstanceInfo2), NULL, + DYNAMIC_TYPE_ASYNC); + if (sorted == NULL || pkg == NULL || info == NULL) { + XFREE(sorted, NULL, DYNAMIC_TYPE_ASYNC); + XFREE(pkg, NULL, DYNAMIC_TYPE_ASYNC); + XFREE(info, NULL, DYNAMIC_TYPE_ASYNC); + return; + } + + /* record each instance's device (package) id */ + maxPkg = 0; + for (i = 0; i < (int)count; i++) { + status = cpaCyInstanceGetInfo2(instances[i], info); + pkg[i] = (status == CPA_STATUS_SUCCESS) ? + info->physInstId.packageId : 0; + if ((int)pkg[i] > maxPkg) { + maxPkg = (int)pkg[i]; + } + } + + /* emit one instance per device per round: dev0#0, dev1#0, dev2#0, + * dev0#1, dev1#1, ... */ + out = 0; + for (round = 0; round < (int)count && out < (int)count; round++) { + for (p = 0; p <= maxPkg && out < (int)count; p++) { + seen = 0; + for (idx = 0; idx < (int)count; idx++) { + if ((int)pkg[idx] == p) { + if (seen == round) { + sorted[out++] = instances[idx]; + break; + } + seen++; + } + } + } + } + + /* only apply if every instance was placed exactly once */ + if (out == (int)count) { + for (i = 0; i < (int)count; i++) { + instances[i] = sorted[i]; + } + } + + XFREE(sorted, NULL, DYNAMIC_TYPE_ASYNC); + XFREE(pkg, NULL, DYNAMIC_TYPE_ASYNC); + XFREE(info, NULL, DYNAMIC_TYPE_ASYNC); +} +#endif /* QAT_NO_DEV_INTERLEAVE */ + int IntelQaHardwareStart(const char* process_name, int limitDevAccess) { int ret = 0, i; @@ -414,6 +494,11 @@ int IntelQaHardwareStart(const char* process_name, int limitDevAccess) ret = INVALID_DEVID; goto error; } +#ifndef QAT_NO_DEV_INTERLEAVE + /* spread instances across devices for better multi-device utilization */ + IntelQaInterleaveInstances(g_cyInstances, g_numInstances); +#endif + /* start all instances */ g_cyServiceStarted = CPA_TRUE; for (i=0; i