| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright(c) 2021 Intel Corporation. All rights rsvd. */ |
| |
| #include <linux/init.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/pci.h> |
| #include <linux/device.h> |
| #include <linux/iommu.h> |
| #include <uapi/linux/idxd.h> |
| #include <linux/highmem.h> |
| #include <linux/sched/smt.h> |
| #include <crypto/internal/acompress.h> |
| |
| #include "idxd.h" |
| #include "iaa_crypto.h" |
| #include "iaa_crypto_stats.h" |
| |
| #ifdef pr_fmt |
| #undef pr_fmt |
| #endif |
| |
| #define pr_fmt(fmt) "idxd: " IDXD_SUBDRIVER_NAME ": " fmt |
| |
| #define IAA_ALG_PRIORITY 300 |
| |
| /* number of iaa instances probed */ |
| static unsigned int nr_iaa; |
| static unsigned int nr_cpus; |
| static unsigned int nr_nodes; |
| static unsigned int nr_cpus_per_node; |
| |
| /* Number of physical cpus sharing each iaa instance */ |
| static unsigned int cpus_per_iaa; |
| |
| static struct crypto_comp *deflate_generic_tfm; |
| |
| /* Per-cpu lookup table for balanced wqs */ |
| static struct wq_table_entry __percpu *wq_table; |
| |
| static struct idxd_wq *wq_table_next_wq(int cpu) |
| { |
| struct wq_table_entry *entry = per_cpu_ptr(wq_table, cpu); |
| |
| if (++entry->cur_wq >= entry->n_wqs) |
| entry->cur_wq = 0; |
| |
| if (!entry->wqs[entry->cur_wq]) |
| return NULL; |
| |
| pr_debug("%s: returning wq at idx %d (iaa wq %d.%d) from cpu %d\n", __func__, |
| entry->cur_wq, entry->wqs[entry->cur_wq]->idxd->id, |
| entry->wqs[entry->cur_wq]->id, cpu); |
| |
| return entry->wqs[entry->cur_wq]; |
| } |
| |
| static void wq_table_add(int cpu, struct idxd_wq *wq) |
| { |
| struct wq_table_entry *entry = per_cpu_ptr(wq_table, cpu); |
| |
| if (WARN_ON(entry->n_wqs == entry->max_wqs)) |
| return; |
| |
| entry->wqs[entry->n_wqs++] = wq; |
| |
| pr_debug("%s: added iaa wq %d.%d to idx %d of cpu %d\n", __func__, |
| entry->wqs[entry->n_wqs - 1]->idxd->id, |
| entry->wqs[entry->n_wqs - 1]->id, entry->n_wqs - 1, cpu); |
| } |
| |
| static void wq_table_free_entry(int cpu) |
| { |
| struct wq_table_entry *entry = per_cpu_ptr(wq_table, cpu); |
| |
| kfree(entry->wqs); |
| memset(entry, 0, sizeof(*entry)); |
| } |
| |
| static void wq_table_clear_entry(int cpu) |
| { |
| struct wq_table_entry *entry = per_cpu_ptr(wq_table, cpu); |
| |
| entry->n_wqs = 0; |
| entry->cur_wq = 0; |
| memset(entry->wqs, 0, entry->max_wqs * sizeof(struct idxd_wq *)); |
| } |
| |
| LIST_HEAD(iaa_devices); |
| DEFINE_MUTEX(iaa_devices_lock); |
| |
| /* If enabled, IAA hw crypto algos are registered, unavailable otherwise */ |
| static bool iaa_crypto_enabled; |
| static bool iaa_crypto_registered; |
| |
| /* Verify results of IAA compress or not */ |
| static bool iaa_verify_compress = true; |
| |
| static ssize_t verify_compress_show(struct device_driver *driver, char *buf) |
| { |
| return sprintf(buf, "%d\n", iaa_verify_compress); |
| } |
| |
| static ssize_t verify_compress_store(struct device_driver *driver, |
| const char *buf, size_t count) |
| { |
| int ret = -EBUSY; |
| |
| mutex_lock(&iaa_devices_lock); |
| |
| if (iaa_crypto_enabled) |
| goto out; |
| |
| ret = kstrtobool(buf, &iaa_verify_compress); |
| if (ret) |
| goto out; |
| |
| ret = count; |
| out: |
| mutex_unlock(&iaa_devices_lock); |
| |
| return ret; |
| } |
| static DRIVER_ATTR_RW(verify_compress); |
| |
| /* |
| * The iaa crypto driver supports three 'sync' methods determining how |
| * compressions and decompressions are performed: |
| * |
| * - sync: the compression or decompression completes before |
| * returning. This is the mode used by the async crypto |
| * interface when the sync mode is set to 'sync' and by |
| * the sync crypto interface regardless of setting. |
| * |
| * - async: the compression or decompression is submitted and returns |
| * immediately. Completion interrupts are not used so |
| * the caller is responsible for polling the descriptor |
| * for completion. This mode is applicable to only the |
| * async crypto interface and is ignored for anything |
| * else. |
| * |
| * - async_irq: the compression or decompression is submitted and |
| * returns immediately. Completion interrupts are |
| * enabled so the caller can wait for the completion and |
| * yield to other threads. When the compression or |
| * decompression completes, the completion is signaled |
| * and the caller awakened. This mode is applicable to |
| * only the async crypto interface and is ignored for |
| * anything else. |
| * |
| * These modes can be set using the iaa_crypto sync_mode driver |
| * attribute. |
| */ |
| |
| /* Use async mode */ |
| static bool async_mode; |
| /* Use interrupts */ |
| static bool use_irq; |
| |
| /** |
| * set_iaa_sync_mode - Set IAA sync mode |
| * @name: The name of the sync mode |
| * |
| * Make the IAA sync mode named @name the current sync mode used by |
| * compression/decompression. |
| */ |
| |
| static int set_iaa_sync_mode(const char *name) |
| { |
| int ret = 0; |
| |
| if (sysfs_streq(name, "sync")) { |
| async_mode = false; |
| use_irq = false; |
| } else if (sysfs_streq(name, "async")) { |
| async_mode = true; |
| use_irq = false; |
| } else if (sysfs_streq(name, "async_irq")) { |
| async_mode = true; |
| use_irq = true; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return ret; |
| } |
| |
| static ssize_t sync_mode_show(struct device_driver *driver, char *buf) |
| { |
| int ret = 0; |
| |
| if (!async_mode && !use_irq) |
| ret = sprintf(buf, "%s\n", "sync"); |
| else if (async_mode && !use_irq) |
| ret = sprintf(buf, "%s\n", "async"); |
| else if (async_mode && use_irq) |
| ret = sprintf(buf, "%s\n", "async_irq"); |
| |
| return ret; |
| } |
| |
| static ssize_t sync_mode_store(struct device_driver *driver, |
| const char *buf, size_t count) |
| { |
| int ret = -EBUSY; |
| |
| mutex_lock(&iaa_devices_lock); |
| |
| if (iaa_crypto_enabled) |
| goto out; |
| |
| ret = set_iaa_sync_mode(buf); |
| if (ret == 0) |
| ret = count; |
| out: |
| mutex_unlock(&iaa_devices_lock); |
| |
| return ret; |
| } |
| static DRIVER_ATTR_RW(sync_mode); |
| |
| static struct iaa_compression_mode *iaa_compression_modes[IAA_COMP_MODES_MAX]; |
| |
| static int find_empty_iaa_compression_mode(void) |
| { |
| int i = -EINVAL; |
| |
| for (i = 0; i < IAA_COMP_MODES_MAX; i++) { |
| if (iaa_compression_modes[i]) |
| continue; |
| break; |
| } |
| |
| return i; |
| } |
| |
| static struct iaa_compression_mode *find_iaa_compression_mode(const char *name, int *idx) |
| { |
| struct iaa_compression_mode *mode; |
| int i; |
| |
| for (i = 0; i < IAA_COMP_MODES_MAX; i++) { |
| mode = iaa_compression_modes[i]; |
| if (!mode) |
| continue; |
| |
| if (!strcmp(mode->name, name)) { |
| *idx = i; |
| return iaa_compression_modes[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static void free_iaa_compression_mode(struct iaa_compression_mode *mode) |
| { |
| kfree(mode->name); |
| kfree(mode->ll_table); |
| kfree(mode->d_table); |
| |
| kfree(mode); |
| } |
| |
| /* |
| * IAA Compression modes are defined by an ll_table and a d_table. |
| * These tables are typically generated and captured using statistics |
| * collected from running actual compress/decompress workloads. |
| * |
| * A module or other kernel code can add and remove compression modes |
| * with a given name using the exported @add_iaa_compression_mode() |
| * and @remove_iaa_compression_mode functions. |
| * |
| * When a new compression mode is added, the tables are saved in a |
| * global compression mode list. When IAA devices are added, a |
| * per-IAA device dma mapping is created for each IAA device, for each |
| * compression mode. These are the tables used to do the actual |
| * compression/deccompression and are unmapped if/when the devices are |
| * removed. Currently, compression modes must be added before any |
| * device is added, and removed after all devices have been removed. |
| */ |
| |
| /** |
| * remove_iaa_compression_mode - Remove an IAA compression mode |
| * @name: The name the compression mode will be known as |
| * |
| * Remove the IAA compression mode named @name. |
| */ |
| void remove_iaa_compression_mode(const char *name) |
| { |
| struct iaa_compression_mode *mode; |
| int idx; |
| |
| mutex_lock(&iaa_devices_lock); |
| |
| if (!list_empty(&iaa_devices)) |
| goto out; |
| |
| mode = find_iaa_compression_mode(name, &idx); |
| if (mode) { |
| free_iaa_compression_mode(mode); |
| iaa_compression_modes[idx] = NULL; |
| } |
| out: |
| mutex_unlock(&iaa_devices_lock); |
| } |
| EXPORT_SYMBOL_GPL(remove_iaa_compression_mode); |
| |
| /** |
| * add_iaa_compression_mode - Add an IAA compression mode |
| * @name: The name the compression mode will be known as |
| * @ll_table: The ll table |
| * @ll_table_size: The ll table size in bytes |
| * @d_table: The d table |
| * @d_table_size: The d table size in bytes |
| * @init: Optional callback function to init the compression mode data |
| * @free: Optional callback function to free the compression mode data |
| * |
| * Add a new IAA compression mode named @name. |
| * |
| * Returns 0 if successful, errcode otherwise. |
| */ |
| int add_iaa_compression_mode(const char *name, |
| const u32 *ll_table, |
| int ll_table_size, |
| const u32 *d_table, |
| int d_table_size, |
| iaa_dev_comp_init_fn_t init, |
| iaa_dev_comp_free_fn_t free) |
| { |
| struct iaa_compression_mode *mode; |
| int idx, ret = -ENOMEM; |
| |
| mutex_lock(&iaa_devices_lock); |
| |
| if (!list_empty(&iaa_devices)) { |
| ret = -EBUSY; |
| goto out; |
| } |
| |
| mode = kzalloc(sizeof(*mode), GFP_KERNEL); |
| if (!mode) |
| goto out; |
| |
| mode->name = kstrdup(name, GFP_KERNEL); |
| if (!mode->name) |
| goto free; |
| |
| if (ll_table) { |
| mode->ll_table = kzalloc(ll_table_size, GFP_KERNEL); |
| if (!mode->ll_table) |
| goto free; |
| memcpy(mode->ll_table, ll_table, ll_table_size); |
| mode->ll_table_size = ll_table_size; |
| } |
| |
| if (d_table) { |
| mode->d_table = kzalloc(d_table_size, GFP_KERNEL); |
| if (!mode->d_table) |
| goto free; |
| memcpy(mode->d_table, d_table, d_table_size); |
| mode->d_table_size = d_table_size; |
| } |
| |
| mode->init = init; |
| mode->free = free; |
| |
| idx = find_empty_iaa_compression_mode(); |
| if (idx < 0) |
| goto free; |
| |
| pr_debug("IAA compression mode %s added at idx %d\n", |
| mode->name, idx); |
| |
| iaa_compression_modes[idx] = mode; |
| |
| ret = 0; |
| out: |
| mutex_unlock(&iaa_devices_lock); |
| |
| return ret; |
| free: |
| free_iaa_compression_mode(mode); |
| goto out; |
| } |
| EXPORT_SYMBOL_GPL(add_iaa_compression_mode); |
| |
| static struct iaa_device_compression_mode * |
| get_iaa_device_compression_mode(struct iaa_device *iaa_device, int idx) |
| { |
| return iaa_device->compression_modes[idx]; |
| } |
| |
| static void free_device_compression_mode(struct iaa_device *iaa_device, |
| struct iaa_device_compression_mode *device_mode) |
| { |
| size_t size = sizeof(struct aecs_comp_table_record) + IAA_AECS_ALIGN; |
| struct device *dev = &iaa_device->idxd->pdev->dev; |
| |
| kfree(device_mode->name); |
| |
| if (device_mode->aecs_comp_table) |
| dma_free_coherent(dev, size, device_mode->aecs_comp_table, |
| device_mode->aecs_comp_table_dma_addr); |
| kfree(device_mode); |
| } |
| |
| #define IDXD_OP_FLAG_AECS_RW_TGLS 0x400000 |
| #define IAX_AECS_DEFAULT_FLAG (IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR | IDXD_OP_FLAG_CC) |
| #define IAX_AECS_COMPRESS_FLAG (IAX_AECS_DEFAULT_FLAG | IDXD_OP_FLAG_RD_SRC2_AECS) |
| #define IAX_AECS_DECOMPRESS_FLAG (IAX_AECS_DEFAULT_FLAG | IDXD_OP_FLAG_RD_SRC2_AECS) |
| #define IAX_AECS_GEN_FLAG (IAX_AECS_DEFAULT_FLAG | \ |
| IDXD_OP_FLAG_WR_SRC2_AECS_COMP | \ |
| IDXD_OP_FLAG_AECS_RW_TGLS) |
| |
| static int check_completion(struct device *dev, |
| struct iax_completion_record *comp, |
| bool compress, |
| bool only_once); |
| |
| static int init_device_compression_mode(struct iaa_device *iaa_device, |
| struct iaa_compression_mode *mode, |
| int idx, struct idxd_wq *wq) |
| { |
| size_t size = sizeof(struct aecs_comp_table_record) + IAA_AECS_ALIGN; |
| struct device *dev = &iaa_device->idxd->pdev->dev; |
| struct iaa_device_compression_mode *device_mode; |
| int ret = -ENOMEM; |
| |
| device_mode = kzalloc(sizeof(*device_mode), GFP_KERNEL); |
| if (!device_mode) |
| return -ENOMEM; |
| |
| device_mode->name = kstrdup(mode->name, GFP_KERNEL); |
| if (!device_mode->name) |
| goto free; |
| |
| device_mode->aecs_comp_table = dma_alloc_coherent(dev, size, |
| &device_mode->aecs_comp_table_dma_addr, GFP_KERNEL); |
| if (!device_mode->aecs_comp_table) |
| goto free; |
| |
| /* Add Huffman table to aecs */ |
| memset(device_mode->aecs_comp_table, 0, sizeof(*device_mode->aecs_comp_table)); |
| memcpy(device_mode->aecs_comp_table->ll_sym, mode->ll_table, mode->ll_table_size); |
| memcpy(device_mode->aecs_comp_table->d_sym, mode->d_table, mode->d_table_size); |
| |
| if (mode->init) { |
| ret = mode->init(device_mode); |
| if (ret) |
| goto free; |
| } |
| |
| /* mode index should match iaa_compression_modes idx */ |
| iaa_device->compression_modes[idx] = device_mode; |
| |
| pr_debug("IAA %s compression mode initialized for iaa device %d\n", |
| mode->name, iaa_device->idxd->id); |
| |
| ret = 0; |
| out: |
| return ret; |
| free: |
| pr_debug("IAA %s compression mode initialization failed for iaa device %d\n", |
| mode->name, iaa_device->idxd->id); |
| |
| free_device_compression_mode(iaa_device, device_mode); |
| goto out; |
| } |
| |
| static int init_device_compression_modes(struct iaa_device *iaa_device, |
| struct idxd_wq *wq) |
| { |
| struct iaa_compression_mode *mode; |
| int i, ret = 0; |
| |
| for (i = 0; i < IAA_COMP_MODES_MAX; i++) { |
| mode = iaa_compression_modes[i]; |
| if (!mode) |
| continue; |
| |
| ret = init_device_compression_mode(iaa_device, mode, i, wq); |
| if (ret) |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static void remove_device_compression_modes(struct iaa_device *iaa_device) |
| { |
| struct iaa_device_compression_mode *device_mode; |
| int i; |
| |
| for (i = 0; i < IAA_COMP_MODES_MAX; i++) { |
| device_mode = iaa_device->compression_modes[i]; |
| if (!device_mode) |
| continue; |
| |
| free_device_compression_mode(iaa_device, device_mode); |
| iaa_device->compression_modes[i] = NULL; |
| if (iaa_compression_modes[i]->free) |
| iaa_compression_modes[i]->free(device_mode); |
| } |
| } |
| |
| static struct iaa_device *iaa_device_alloc(void) |
| { |
| struct iaa_device *iaa_device; |
| |
| iaa_device = kzalloc(sizeof(*iaa_device), GFP_KERNEL); |
| if (!iaa_device) |
| return NULL; |
| |
| INIT_LIST_HEAD(&iaa_device->wqs); |
| |
| return iaa_device; |
| } |
| |
| static bool iaa_has_wq(struct iaa_device *iaa_device, struct idxd_wq *wq) |
| { |
| struct iaa_wq *iaa_wq; |
| |
| list_for_each_entry(iaa_wq, &iaa_device->wqs, list) { |
| if (iaa_wq->wq == wq) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static struct iaa_device *add_iaa_device(struct idxd_device *idxd) |
| { |
| struct iaa_device *iaa_device; |
| |
| iaa_device = iaa_device_alloc(); |
| if (!iaa_device) |
| return NULL; |
| |
| iaa_device->idxd = idxd; |
| |
| list_add_tail(&iaa_device->list, &iaa_devices); |
| |
| nr_iaa++; |
| |
| return iaa_device; |
| } |
| |
| static int init_iaa_device(struct iaa_device *iaa_device, struct iaa_wq *iaa_wq) |
| { |
| int ret = 0; |
| |
| ret = init_device_compression_modes(iaa_device, iaa_wq->wq); |
| if (ret) |
| return ret; |
| |
| return ret; |
| } |
| |
| static void del_iaa_device(struct iaa_device *iaa_device) |
| { |
| list_del(&iaa_device->list); |
| |
| nr_iaa--; |
| } |
| |
| static int add_iaa_wq(struct iaa_device *iaa_device, struct idxd_wq *wq, |
| struct iaa_wq **new_wq) |
| { |
| struct idxd_device *idxd = iaa_device->idxd; |
| struct pci_dev *pdev = idxd->pdev; |
| struct device *dev = &pdev->dev; |
| struct iaa_wq *iaa_wq; |
| |
| iaa_wq = kzalloc(sizeof(*iaa_wq), GFP_KERNEL); |
| if (!iaa_wq) |
| return -ENOMEM; |
| |
| iaa_wq->wq = wq; |
| iaa_wq->iaa_device = iaa_device; |
| idxd_wq_set_private(wq, iaa_wq); |
| |
| list_add_tail(&iaa_wq->list, &iaa_device->wqs); |
| |
| iaa_device->n_wq++; |
| |
| if (new_wq) |
| *new_wq = iaa_wq; |
| |
| dev_dbg(dev, "added wq %d to iaa device %d, n_wq %d\n", |
| wq->id, iaa_device->idxd->id, iaa_device->n_wq); |
| |
| return 0; |
| } |
| |
| static void del_iaa_wq(struct iaa_device *iaa_device, struct idxd_wq *wq) |
| { |
| struct idxd_device *idxd = iaa_device->idxd; |
| struct pci_dev *pdev = idxd->pdev; |
| struct device *dev = &pdev->dev; |
| struct iaa_wq *iaa_wq; |
| |
| list_for_each_entry(iaa_wq, &iaa_device->wqs, list) { |
| if (iaa_wq->wq == wq) { |
| list_del(&iaa_wq->list); |
| iaa_device->n_wq--; |
| |
| dev_dbg(dev, "removed wq %d from iaa_device %d, n_wq %d, nr_iaa %d\n", |
| wq->id, iaa_device->idxd->id, |
| iaa_device->n_wq, nr_iaa); |
| |
| if (iaa_device->n_wq == 0) |
| del_iaa_device(iaa_device); |
| break; |
| } |
| } |
| } |
| |
| static void clear_wq_table(void) |
| { |
| int cpu; |
| |
| for (cpu = 0; cpu < nr_cpus; cpu++) |
| wq_table_clear_entry(cpu); |
| |
| pr_debug("cleared wq table\n"); |
| } |
| |
| static void free_iaa_device(struct iaa_device *iaa_device) |
| { |
| if (!iaa_device) |
| return; |
| |
| remove_device_compression_modes(iaa_device); |
| kfree(iaa_device); |
| } |
| |
| static void __free_iaa_wq(struct iaa_wq *iaa_wq) |
| { |
| struct iaa_device *iaa_device; |
| |
| if (!iaa_wq) |
| return; |
| |
| iaa_device = iaa_wq->iaa_device; |
| if (iaa_device->n_wq == 0) |
| free_iaa_device(iaa_wq->iaa_device); |
| } |
| |
| static void free_iaa_wq(struct iaa_wq *iaa_wq) |
| { |
| struct idxd_wq *wq; |
| |
| __free_iaa_wq(iaa_wq); |
| |
| wq = iaa_wq->wq; |
| |
| kfree(iaa_wq); |
| idxd_wq_set_private(wq, NULL); |
| } |
| |
| static int iaa_wq_get(struct idxd_wq *wq) |
| { |
| struct idxd_device *idxd = wq->idxd; |
| struct iaa_wq *iaa_wq; |
| int ret = 0; |
| |
| spin_lock(&idxd->dev_lock); |
| iaa_wq = idxd_wq_get_private(wq); |
| if (iaa_wq && !iaa_wq->remove) { |
| iaa_wq->ref++; |
| idxd_wq_get(wq); |
| } else { |
| ret = -ENODEV; |
| } |
| spin_unlock(&idxd->dev_lock); |
| |
| return ret; |
| } |
| |
| static int iaa_wq_put(struct idxd_wq *wq) |
| { |
| struct idxd_device *idxd = wq->idxd; |
| struct iaa_wq *iaa_wq; |
| bool free = false; |
| int ret = 0; |
| |
| spin_lock(&idxd->dev_lock); |
| iaa_wq = idxd_wq_get_private(wq); |
| if (iaa_wq) { |
| iaa_wq->ref--; |
| if (iaa_wq->ref == 0 && iaa_wq->remove) { |
| idxd_wq_set_private(wq, NULL); |
| free = true; |
| } |
| idxd_wq_put(wq); |
| } else { |
| ret = -ENODEV; |
| } |
| spin_unlock(&idxd->dev_lock); |
| if (free) { |
| __free_iaa_wq(iaa_wq); |
| kfree(iaa_wq); |
| } |
| |
| return ret; |
| } |
| |
| static void free_wq_table(void) |
| { |
| int cpu; |
| |
| for (cpu = 0; cpu < nr_cpus; cpu++) |
| wq_table_free_entry(cpu); |
| |
| free_percpu(wq_table); |
| |
| pr_debug("freed wq table\n"); |
| } |
| |
| static int alloc_wq_table(int max_wqs) |
| { |
| struct wq_table_entry *entry; |
| int cpu; |
| |
| wq_table = alloc_percpu(struct wq_table_entry); |
| if (!wq_table) |
| return -ENOMEM; |
| |
| for (cpu = 0; cpu < nr_cpus; cpu++) { |
| entry = per_cpu_ptr(wq_table, cpu); |
| entry->wqs = kcalloc(max_wqs, sizeof(struct wq *), GFP_KERNEL); |
| if (!entry->wqs) { |
| free_wq_table(); |
| return -ENOMEM; |
| } |
| |
| entry->max_wqs = max_wqs; |
| } |
| |
| pr_debug("initialized wq table\n"); |
| |
| return 0; |
| } |
| |
| static int save_iaa_wq(struct idxd_wq *wq) |
| { |
| struct iaa_device *iaa_device, *found = NULL; |
| struct idxd_device *idxd; |
| struct pci_dev *pdev; |
| struct device *dev; |
| int ret = 0; |
| |
| list_for_each_entry(iaa_device, &iaa_devices, list) { |
| if (iaa_device->idxd == wq->idxd) { |
| idxd = iaa_device->idxd; |
| pdev = idxd->pdev; |
| dev = &pdev->dev; |
| /* |
| * Check to see that we don't already have this wq. |
| * Shouldn't happen but we don't control probing. |
| */ |
| if (iaa_has_wq(iaa_device, wq)) { |
| dev_dbg(dev, "same wq probed multiple times for iaa_device %p\n", |
| iaa_device); |
| goto out; |
| } |
| |
| found = iaa_device; |
| |
| ret = add_iaa_wq(iaa_device, wq, NULL); |
| if (ret) |
| goto out; |
| |
| break; |
| } |
| } |
| |
| if (!found) { |
| struct iaa_device *new_device; |
| struct iaa_wq *new_wq; |
| |
| new_device = add_iaa_device(wq->idxd); |
| if (!new_device) { |
| ret = -ENOMEM; |
| goto out; |
| } |
| |
| ret = add_iaa_wq(new_device, wq, &new_wq); |
| if (ret) { |
| del_iaa_device(new_device); |
| free_iaa_device(new_device); |
| goto out; |
| } |
| |
| ret = init_iaa_device(new_device, new_wq); |
| if (ret) { |
| del_iaa_wq(new_device, new_wq->wq); |
| del_iaa_device(new_device); |
| free_iaa_wq(new_wq); |
| goto out; |
| } |
| } |
| |
| if (WARN_ON(nr_iaa == 0)) |
| return -EINVAL; |
| |
| cpus_per_iaa = (nr_nodes * nr_cpus_per_node) / nr_iaa; |
| out: |
| return 0; |
| } |
| |
| static void remove_iaa_wq(struct idxd_wq *wq) |
| { |
| struct iaa_device *iaa_device; |
| |
| list_for_each_entry(iaa_device, &iaa_devices, list) { |
| if (iaa_has_wq(iaa_device, wq)) { |
| del_iaa_wq(iaa_device, wq); |
| break; |
| } |
| } |
| |
| if (nr_iaa) |
| cpus_per_iaa = (nr_nodes * nr_cpus_per_node) / nr_iaa; |
| else |
| cpus_per_iaa = 0; |
| } |
| |
| static int wq_table_add_wqs(int iaa, int cpu) |
| { |
| struct iaa_device *iaa_device, *found_device = NULL; |
| int ret = 0, cur_iaa = 0, n_wqs_added = 0; |
| struct idxd_device *idxd; |
| struct iaa_wq *iaa_wq; |
| struct pci_dev *pdev; |
| struct device *dev; |
| |
| list_for_each_entry(iaa_device, &iaa_devices, list) { |
| idxd = iaa_device->idxd; |
| pdev = idxd->pdev; |
| dev = &pdev->dev; |
| |
| if (cur_iaa != iaa) { |
| cur_iaa++; |
| continue; |
| } |
| |
| found_device = iaa_device; |
| dev_dbg(dev, "getting wq from iaa_device %d, cur_iaa %d\n", |
| found_device->idxd->id, cur_iaa); |
| break; |
| } |
| |
| if (!found_device) { |
| found_device = list_first_entry_or_null(&iaa_devices, |
| struct iaa_device, list); |
| if (!found_device) { |
| pr_debug("couldn't find any iaa devices with wqs!\n"); |
| ret = -EINVAL; |
| goto out; |
| } |
| cur_iaa = 0; |
| |
| idxd = found_device->idxd; |
| pdev = idxd->pdev; |
| dev = &pdev->dev; |
| dev_dbg(dev, "getting wq from only iaa_device %d, cur_iaa %d\n", |
| found_device->idxd->id, cur_iaa); |
| } |
| |
| list_for_each_entry(iaa_wq, &found_device->wqs, list) { |
| wq_table_add(cpu, iaa_wq->wq); |
| pr_debug("rebalance: added wq for cpu=%d: iaa wq %d.%d\n", |
| cpu, iaa_wq->wq->idxd->id, iaa_wq->wq->id); |
| n_wqs_added++; |
| } |
| |
| if (!n_wqs_added) { |
| pr_debug("couldn't find any iaa wqs!\n"); |
| ret = -EINVAL; |
| goto out; |
| } |
| out: |
| return ret; |
| } |
| |
| /* |
| * Rebalance the wq table so that given a cpu, it's easy to find the |
| * closest IAA instance. The idea is to try to choose the most |
| * appropriate IAA instance for a caller and spread available |
| * workqueues around to clients. |
| */ |
| static void rebalance_wq_table(void) |
| { |
| const struct cpumask *node_cpus; |
| int node, cpu, iaa = -1; |
| |
| if (nr_iaa == 0) |
| return; |
| |
| pr_debug("rebalance: nr_nodes=%d, nr_cpus %d, nr_iaa %d, cpus_per_iaa %d\n", |
| nr_nodes, nr_cpus, nr_iaa, cpus_per_iaa); |
| |
| clear_wq_table(); |
| |
| if (nr_iaa == 1) { |
| for (cpu = 0; cpu < nr_cpus; cpu++) { |
| if (WARN_ON(wq_table_add_wqs(0, cpu))) { |
| pr_debug("could not add any wqs for iaa 0 to cpu %d!\n", cpu); |
| return; |
| } |
| } |
| |
| return; |
| } |
| |
| for_each_node_with_cpus(node) { |
| node_cpus = cpumask_of_node(node); |
| |
| for (cpu = 0; cpu < nr_cpus_per_node; cpu++) { |
| int node_cpu = cpumask_nth(cpu, node_cpus); |
| |
| if (WARN_ON(node_cpu >= nr_cpu_ids)) { |
| pr_debug("node_cpu %d doesn't exist!\n", node_cpu); |
| return; |
| } |
| |
| if ((cpu % cpus_per_iaa) == 0) |
| iaa++; |
| |
| if (WARN_ON(wq_table_add_wqs(iaa, node_cpu))) { |
| pr_debug("could not add any wqs for iaa %d to cpu %d!\n", iaa, cpu); |
| return; |
| } |
| } |
| } |
| } |
| |
| static inline int check_completion(struct device *dev, |
| struct iax_completion_record *comp, |
| bool compress, |
| bool only_once) |
| { |
| char *op_str = compress ? "compress" : "decompress"; |
| int ret = 0; |
| |
| while (!comp->status) { |
| if (only_once) |
| return -EAGAIN; |
| cpu_relax(); |
| } |
| |
| if (comp->status != IAX_COMP_SUCCESS) { |
| if (comp->status == IAA_ERROR_WATCHDOG_EXPIRED) { |
| ret = -ETIMEDOUT; |
| dev_dbg(dev, "%s timed out, size=0x%x\n", |
| op_str, comp->output_size); |
| update_completion_timeout_errs(); |
| goto out; |
| } |
| |
| if (comp->status == IAA_ANALYTICS_ERROR && |
| comp->error_code == IAA_ERROR_COMP_BUF_OVERFLOW && compress) { |
| ret = -E2BIG; |
| dev_dbg(dev, "compressed > uncompressed size," |
| " not compressing, size=0x%x\n", |
| comp->output_size); |
| update_completion_comp_buf_overflow_errs(); |
| goto out; |
| } |
| |
| if (comp->status == IAA_ERROR_DECOMP_BUF_OVERFLOW) { |
| ret = -EOVERFLOW; |
| goto out; |
| } |
| |
| ret = -EINVAL; |
| dev_dbg(dev, "iaa %s status=0x%x, error=0x%x, size=0x%x\n", |
| op_str, comp->status, comp->error_code, comp->output_size); |
| print_hex_dump(KERN_INFO, "cmp-rec: ", DUMP_PREFIX_OFFSET, 8, 1, comp, 64, 0); |
| update_completion_einval_errs(); |
| |
| goto out; |
| } |
| out: |
| return ret; |
| } |
| |
| static int deflate_generic_decompress(struct acomp_req *req) |
| { |
| void *src, *dst; |
| int ret; |
| |
| src = kmap_local_page(sg_page(req->src)) + req->src->offset; |
| dst = kmap_local_page(sg_page(req->dst)) + req->dst->offset; |
| |
| ret = crypto_comp_decompress(deflate_generic_tfm, |
| src, req->slen, dst, &req->dlen); |
| |
| kunmap_local(src); |
| kunmap_local(dst); |
| |
| update_total_sw_decomp_calls(); |
| |
| return ret; |
| } |
| |
| static int iaa_remap_for_verify(struct device *dev, struct iaa_wq *iaa_wq, |
| struct acomp_req *req, |
| dma_addr_t *src_addr, dma_addr_t *dst_addr); |
| |
| static int iaa_compress_verify(struct crypto_tfm *tfm, struct acomp_req *req, |
| struct idxd_wq *wq, |
| dma_addr_t src_addr, unsigned int slen, |
| dma_addr_t dst_addr, unsigned int *dlen, |
| u32 compression_crc); |
| |
| static void iaa_desc_complete(struct idxd_desc *idxd_desc, |
| enum idxd_complete_type comp_type, |
| bool free_desc, void *__ctx, |
| u32 *status) |
| { |
| struct iaa_device_compression_mode *active_compression_mode; |
| struct iaa_compression_ctx *compression_ctx; |
| struct crypto_ctx *ctx = __ctx; |
| struct iaa_device *iaa_device; |
| struct idxd_device *idxd; |
| struct iaa_wq *iaa_wq; |
| struct pci_dev *pdev; |
| struct device *dev; |
| int ret, err = 0; |
| |
| compression_ctx = crypto_tfm_ctx(ctx->tfm); |
| |
| iaa_wq = idxd_wq_get_private(idxd_desc->wq); |
| iaa_device = iaa_wq->iaa_device; |
| idxd = iaa_device->idxd; |
| pdev = idxd->pdev; |
| dev = &pdev->dev; |
| |
| active_compression_mode = get_iaa_device_compression_mode(iaa_device, |
| compression_ctx->mode); |
| dev_dbg(dev, "%s: compression mode %s," |
| " ctx->src_addr %llx, ctx->dst_addr %llx\n", __func__, |
| active_compression_mode->name, |
| ctx->src_addr, ctx->dst_addr); |
| |
| ret = check_completion(dev, idxd_desc->iax_completion, |
| ctx->compress, false); |
| if (ret) { |
| dev_dbg(dev, "%s: check_completion failed ret=%d\n", __func__, ret); |
| if (!ctx->compress && |
| idxd_desc->iax_completion->status == IAA_ANALYTICS_ERROR) { |
| pr_warn("%s: falling back to deflate-generic decompress, " |
| "analytics error code %x\n", __func__, |
| idxd_desc->iax_completion->error_code); |
| ret = deflate_generic_decompress(ctx->req); |
| if (ret) { |
| dev_dbg(dev, "%s: deflate-generic failed ret=%d\n", |
| __func__, ret); |
| err = -EIO; |
| goto err; |
| } |
| } else { |
| err = -EIO; |
| goto err; |
| } |
| } else { |
| ctx->req->dlen = idxd_desc->iax_completion->output_size; |
| } |
| |
| /* Update stats */ |
| if (ctx->compress) { |
| update_total_comp_bytes_out(ctx->req->dlen); |
| update_wq_comp_bytes(iaa_wq->wq, ctx->req->dlen); |
| } else { |
| update_total_decomp_bytes_in(ctx->req->dlen); |
| update_wq_decomp_bytes(iaa_wq->wq, ctx->req->dlen); |
| } |
| |
| if (ctx->compress && compression_ctx->verify_compress) { |
| dma_addr_t src_addr, dst_addr; |
| u32 compression_crc; |
| |
| compression_crc = idxd_desc->iax_completion->crc; |
| |
| ret = iaa_remap_for_verify(dev, iaa_wq, ctx->req, &src_addr, &dst_addr); |
| if (ret) { |
| dev_dbg(dev, "%s: compress verify remap failed ret=%d\n", __func__, ret); |
| err = -EIO; |
| goto out; |
| } |
| |
| ret = iaa_compress_verify(ctx->tfm, ctx->req, iaa_wq->wq, src_addr, |
| ctx->req->slen, dst_addr, &ctx->req->dlen, |
| compression_crc); |
| if (ret) { |
| dev_dbg(dev, "%s: compress verify failed ret=%d\n", __func__, ret); |
| err = -EIO; |
| } |
| |
| dma_unmap_sg(dev, ctx->req->dst, sg_nents(ctx->req->dst), DMA_TO_DEVICE); |
| dma_unmap_sg(dev, ctx->req->src, sg_nents(ctx->req->src), DMA_FROM_DEVICE); |
| |
| goto out; |
| } |
| err: |
| dma_unmap_sg(dev, ctx->req->dst, sg_nents(ctx->req->dst), DMA_FROM_DEVICE); |
| dma_unmap_sg(dev, ctx->req->src, sg_nents(ctx->req->src), DMA_TO_DEVICE); |
| out: |
| if (ret != 0) |
| dev_dbg(dev, "asynchronous compress failed ret=%d\n", ret); |
| |
| if (ctx->req->base.complete) |
| acomp_request_complete(ctx->req, err); |
| |
| if (free_desc) |
| idxd_free_desc(idxd_desc->wq, idxd_desc); |
| iaa_wq_put(idxd_desc->wq); |
| } |
| |
| static int iaa_compress(struct crypto_tfm *tfm, struct acomp_req *req, |
| struct idxd_wq *wq, |
| dma_addr_t src_addr, unsigned int slen, |
| dma_addr_t dst_addr, unsigned int *dlen, |
| u32 *compression_crc, |
| bool disable_async) |
| { |
| struct iaa_device_compression_mode *active_compression_mode; |
| struct iaa_compression_ctx *ctx = crypto_tfm_ctx(tfm); |
| struct iaa_device *iaa_device; |
| struct idxd_desc *idxd_desc; |
| struct iax_hw_desc *desc; |
| struct idxd_device *idxd; |
| struct iaa_wq *iaa_wq; |
| struct pci_dev *pdev; |
| struct device *dev; |
| int ret = 0; |
| |
| iaa_wq = idxd_wq_get_private(wq); |
| iaa_device = iaa_wq->iaa_device; |
| idxd = iaa_device->idxd; |
| pdev = idxd->pdev; |
| dev = &pdev->dev; |
| |
| active_compression_mode = get_iaa_device_compression_mode(iaa_device, ctx->mode); |
| |
| idxd_desc = idxd_alloc_desc(wq, IDXD_OP_BLOCK); |
| if (IS_ERR(idxd_desc)) { |
| dev_dbg(dev, "idxd descriptor allocation failed\n"); |
| dev_dbg(dev, "iaa compress failed: ret=%ld\n", PTR_ERR(idxd_desc)); |
| return PTR_ERR(idxd_desc); |
| } |
| desc = idxd_desc->iax_hw; |
| |
| desc->flags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR | |
| IDXD_OP_FLAG_RD_SRC2_AECS | IDXD_OP_FLAG_CC; |
| desc->opcode = IAX_OPCODE_COMPRESS; |
| desc->compr_flags = IAA_COMP_FLAGS; |
| desc->priv = 0; |
| |
| desc->src1_addr = (u64)src_addr; |
| desc->src1_size = slen; |
| desc->dst_addr = (u64)dst_addr; |
| desc->max_dst_size = *dlen; |
| desc->src2_addr = active_compression_mode->aecs_comp_table_dma_addr; |
| desc->src2_size = sizeof(struct aecs_comp_table_record); |
| desc->completion_addr = idxd_desc->compl_dma; |
| |
| if (ctx->use_irq && !disable_async) { |
| desc->flags |= IDXD_OP_FLAG_RCI; |
| |
| idxd_desc->crypto.req = req; |
| idxd_desc->crypto.tfm = tfm; |
| idxd_desc->crypto.src_addr = src_addr; |
| idxd_desc->crypto.dst_addr = dst_addr; |
| idxd_desc->crypto.compress = true; |
| |
| dev_dbg(dev, "%s use_async_irq: compression mode %s," |
| " src_addr %llx, dst_addr %llx\n", __func__, |
| active_compression_mode->name, |
| src_addr, dst_addr); |
| } else if (ctx->async_mode && !disable_async) |
| req->base.data = idxd_desc; |
| |
| dev_dbg(dev, "%s: compression mode %s," |
| " desc->src1_addr %llx, desc->src1_size %d," |
| " desc->dst_addr %llx, desc->max_dst_size %d," |
| " desc->src2_addr %llx, desc->src2_size %d\n", __func__, |
| active_compression_mode->name, |
| desc->src1_addr, desc->src1_size, desc->dst_addr, |
| desc->max_dst_size, desc->src2_addr, desc->src2_size); |
| |
| ret = idxd_submit_desc(wq, idxd_desc); |
| if (ret) { |
| dev_dbg(dev, "submit_desc failed ret=%d\n", ret); |
| goto err; |
| } |
| |
| /* Update stats */ |
| update_total_comp_calls(); |
| update_wq_comp_calls(wq); |
| |
| if (ctx->async_mode && !disable_async) { |
| ret = -EINPROGRESS; |
| dev_dbg(dev, "%s: returning -EINPROGRESS\n", __func__); |
| goto out; |
| } |
| |
| ret = check_completion(dev, idxd_desc->iax_completion, true, false); |
| if (ret) { |
| dev_dbg(dev, "check_completion failed ret=%d\n", ret); |
| goto err; |
| } |
| |
| *dlen = idxd_desc->iax_completion->output_size; |
| |
| /* Update stats */ |
| update_total_comp_bytes_out(*dlen); |
| update_wq_comp_bytes(wq, *dlen); |
| |
| *compression_crc = idxd_desc->iax_completion->crc; |
| |
| if (!ctx->async_mode || disable_async) |
| idxd_free_desc(wq, idxd_desc); |
| out: |
| return ret; |
| err: |
| idxd_free_desc(wq, idxd_desc); |
| dev_dbg(dev, "iaa compress failed: ret=%d\n", ret); |
| |
| goto out; |
| } |
| |
| static int iaa_remap_for_verify(struct device *dev, struct iaa_wq *iaa_wq, |
| struct acomp_req *req, |
| dma_addr_t *src_addr, dma_addr_t *dst_addr) |
| { |
| int ret = 0; |
| int nr_sgs; |
| |
| dma_unmap_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); |
| dma_unmap_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); |
| |
| nr_sgs = dma_map_sg(dev, req->src, sg_nents(req->src), DMA_FROM_DEVICE); |
| if (nr_sgs <= 0 || nr_sgs > 1) { |
| dev_dbg(dev, "verify: couldn't map src sg for iaa device %d," |
| " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, |
| iaa_wq->wq->id, ret); |
| ret = -EIO; |
| goto out; |
| } |
| *src_addr = sg_dma_address(req->src); |
| dev_dbg(dev, "verify: dma_map_sg, src_addr %llx, nr_sgs %d, req->src %p," |
| " req->slen %d, sg_dma_len(sg) %d\n", *src_addr, nr_sgs, |
| req->src, req->slen, sg_dma_len(req->src)); |
| |
| nr_sgs = dma_map_sg(dev, req->dst, sg_nents(req->dst), DMA_TO_DEVICE); |
| if (nr_sgs <= 0 || nr_sgs > 1) { |
| dev_dbg(dev, "verify: couldn't map dst sg for iaa device %d," |
| " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, |
| iaa_wq->wq->id, ret); |
| ret = -EIO; |
| dma_unmap_sg(dev, req->src, sg_nents(req->src), DMA_FROM_DEVICE); |
| goto out; |
| } |
| *dst_addr = sg_dma_address(req->dst); |
| dev_dbg(dev, "verify: dma_map_sg, dst_addr %llx, nr_sgs %d, req->dst %p," |
| " req->dlen %d, sg_dma_len(sg) %d\n", *dst_addr, nr_sgs, |
| req->dst, req->dlen, sg_dma_len(req->dst)); |
| out: |
| return ret; |
| } |
| |
| static int iaa_compress_verify(struct crypto_tfm *tfm, struct acomp_req *req, |
| struct idxd_wq *wq, |
| dma_addr_t src_addr, unsigned int slen, |
| dma_addr_t dst_addr, unsigned int *dlen, |
| u32 compression_crc) |
| { |
| struct iaa_device_compression_mode *active_compression_mode; |
| struct iaa_compression_ctx *ctx = crypto_tfm_ctx(tfm); |
| struct iaa_device *iaa_device; |
| struct idxd_desc *idxd_desc; |
| struct iax_hw_desc *desc; |
| struct idxd_device *idxd; |
| struct iaa_wq *iaa_wq; |
| struct pci_dev *pdev; |
| struct device *dev; |
| int ret = 0; |
| |
| iaa_wq = idxd_wq_get_private(wq); |
| iaa_device = iaa_wq->iaa_device; |
| idxd = iaa_device->idxd; |
| pdev = idxd->pdev; |
| dev = &pdev->dev; |
| |
| active_compression_mode = get_iaa_device_compression_mode(iaa_device, ctx->mode); |
| |
| idxd_desc = idxd_alloc_desc(wq, IDXD_OP_BLOCK); |
| if (IS_ERR(idxd_desc)) { |
| dev_dbg(dev, "idxd descriptor allocation failed\n"); |
| dev_dbg(dev, "iaa compress failed: ret=%ld\n", |
| PTR_ERR(idxd_desc)); |
| return PTR_ERR(idxd_desc); |
| } |
| desc = idxd_desc->iax_hw; |
| |
| /* Verify (optional) - decompress and check crc, suppress dest write */ |
| |
| desc->flags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR | IDXD_OP_FLAG_CC; |
| desc->opcode = IAX_OPCODE_DECOMPRESS; |
| desc->decompr_flags = IAA_DECOMP_FLAGS | IAA_DECOMP_SUPPRESS_OUTPUT; |
| desc->priv = 0; |
| |
| desc->src1_addr = (u64)dst_addr; |
| desc->src1_size = *dlen; |
| desc->dst_addr = (u64)src_addr; |
| desc->max_dst_size = slen; |
| desc->completion_addr = idxd_desc->compl_dma; |
| |
| dev_dbg(dev, "(verify) compression mode %s," |
| " desc->src1_addr %llx, desc->src1_size %d," |
| " desc->dst_addr %llx, desc->max_dst_size %d," |
| " desc->src2_addr %llx, desc->src2_size %d\n", |
| active_compression_mode->name, |
| desc->src1_addr, desc->src1_size, desc->dst_addr, |
| desc->max_dst_size, desc->src2_addr, desc->src2_size); |
| |
| ret = idxd_submit_desc(wq, idxd_desc); |
| if (ret) { |
| dev_dbg(dev, "submit_desc (verify) failed ret=%d\n", ret); |
| goto err; |
| } |
| |
| ret = check_completion(dev, idxd_desc->iax_completion, false, false); |
| if (ret) { |
| dev_dbg(dev, "(verify) check_completion failed ret=%d\n", ret); |
| goto err; |
| } |
| |
| if (compression_crc != idxd_desc->iax_completion->crc) { |
| ret = -EINVAL; |
| dev_dbg(dev, "(verify) iaa comp/decomp crc mismatch:" |
| " comp=0x%x, decomp=0x%x\n", compression_crc, |
| idxd_desc->iax_completion->crc); |
| print_hex_dump(KERN_INFO, "cmp-rec: ", DUMP_PREFIX_OFFSET, |
| 8, 1, idxd_desc->iax_completion, 64, 0); |
| goto err; |
| } |
| |
| idxd_free_desc(wq, idxd_desc); |
| out: |
| return ret; |
| err: |
| idxd_free_desc(wq, idxd_desc); |
| dev_dbg(dev, "iaa compress failed: ret=%d\n", ret); |
| |
| goto out; |
| } |
| |
| static int iaa_decompress(struct crypto_tfm *tfm, struct acomp_req *req, |
| struct idxd_wq *wq, |
| dma_addr_t src_addr, unsigned int slen, |
| dma_addr_t dst_addr, unsigned int *dlen, |
| bool disable_async) |
| { |
| struct iaa_device_compression_mode *active_compression_mode; |
| struct iaa_compression_ctx *ctx = crypto_tfm_ctx(tfm); |
| struct iaa_device *iaa_device; |
| struct idxd_desc *idxd_desc; |
| struct iax_hw_desc *desc; |
| struct idxd_device *idxd; |
| struct iaa_wq *iaa_wq; |
| struct pci_dev *pdev; |
| struct device *dev; |
| int ret = 0; |
| |
| iaa_wq = idxd_wq_get_private(wq); |
| iaa_device = iaa_wq->iaa_device; |
| idxd = iaa_device->idxd; |
| pdev = idxd->pdev; |
| dev = &pdev->dev; |
| |
| active_compression_mode = get_iaa_device_compression_mode(iaa_device, ctx->mode); |
| |
| idxd_desc = idxd_alloc_desc(wq, IDXD_OP_BLOCK); |
| if (IS_ERR(idxd_desc)) { |
| dev_dbg(dev, "idxd descriptor allocation failed\n"); |
| dev_dbg(dev, "iaa decompress failed: ret=%ld\n", |
| PTR_ERR(idxd_desc)); |
| return PTR_ERR(idxd_desc); |
| } |
| desc = idxd_desc->iax_hw; |
| |
| desc->flags = IDXD_OP_FLAG_CRAV | IDXD_OP_FLAG_RCR | IDXD_OP_FLAG_CC; |
| desc->opcode = IAX_OPCODE_DECOMPRESS; |
| desc->max_dst_size = PAGE_SIZE; |
| desc->decompr_flags = IAA_DECOMP_FLAGS; |
| desc->priv = 0; |
| |
| desc->src1_addr = (u64)src_addr; |
| desc->dst_addr = (u64)dst_addr; |
| desc->max_dst_size = *dlen; |
| desc->src1_size = slen; |
| desc->completion_addr = idxd_desc->compl_dma; |
| |
| if (ctx->use_irq && !disable_async) { |
| desc->flags |= IDXD_OP_FLAG_RCI; |
| |
| idxd_desc->crypto.req = req; |
| idxd_desc->crypto.tfm = tfm; |
| idxd_desc->crypto.src_addr = src_addr; |
| idxd_desc->crypto.dst_addr = dst_addr; |
| idxd_desc->crypto.compress = false; |
| |
| dev_dbg(dev, "%s: use_async_irq compression mode %s," |
| " src_addr %llx, dst_addr %llx\n", __func__, |
| active_compression_mode->name, |
| src_addr, dst_addr); |
| } else if (ctx->async_mode && !disable_async) |
| req->base.data = idxd_desc; |
| |
| dev_dbg(dev, "%s: decompression mode %s," |
| " desc->src1_addr %llx, desc->src1_size %d," |
| " desc->dst_addr %llx, desc->max_dst_size %d," |
| " desc->src2_addr %llx, desc->src2_size %d\n", __func__, |
| active_compression_mode->name, |
| desc->src1_addr, desc->src1_size, desc->dst_addr, |
| desc->max_dst_size, desc->src2_addr, desc->src2_size); |
| |
| ret = idxd_submit_desc(wq, idxd_desc); |
| if (ret) { |
| dev_dbg(dev, "submit_desc failed ret=%d\n", ret); |
| goto err; |
| } |
| |
| /* Update stats */ |
| update_total_decomp_calls(); |
| update_wq_decomp_calls(wq); |
| |
| if (ctx->async_mode && !disable_async) { |
| ret = -EINPROGRESS; |
| dev_dbg(dev, "%s: returning -EINPROGRESS\n", __func__); |
| goto out; |
| } |
| |
| ret = check_completion(dev, idxd_desc->iax_completion, false, false); |
| if (ret) { |
| dev_dbg(dev, "%s: check_completion failed ret=%d\n", __func__, ret); |
| if (idxd_desc->iax_completion->status == IAA_ANALYTICS_ERROR) { |
| pr_warn("%s: falling back to deflate-generic decompress, " |
| "analytics error code %x\n", __func__, |
| idxd_desc->iax_completion->error_code); |
| ret = deflate_generic_decompress(req); |
| if (ret) { |
| dev_dbg(dev, "%s: deflate-generic failed ret=%d\n", |
| __func__, ret); |
| goto err; |
| } |
| } else { |
| goto err; |
| } |
| } else { |
| req->dlen = idxd_desc->iax_completion->output_size; |
| } |
| |
| *dlen = req->dlen; |
| |
| if (!ctx->async_mode || disable_async) |
| idxd_free_desc(wq, idxd_desc); |
| |
| /* Update stats */ |
| update_total_decomp_bytes_in(slen); |
| update_wq_decomp_bytes(wq, slen); |
| out: |
| return ret; |
| err: |
| idxd_free_desc(wq, idxd_desc); |
| dev_dbg(dev, "iaa decompress failed: ret=%d\n", ret); |
| |
| goto out; |
| } |
| |
| static int iaa_comp_acompress(struct acomp_req *req) |
| { |
| struct iaa_compression_ctx *compression_ctx; |
| struct crypto_tfm *tfm = req->base.tfm; |
| dma_addr_t src_addr, dst_addr; |
| bool disable_async = false; |
| int nr_sgs, cpu, ret = 0; |
| struct iaa_wq *iaa_wq; |
| u32 compression_crc; |
| struct idxd_wq *wq; |
| struct device *dev; |
| u64 start_time_ns; |
| int order = -1; |
| |
| compression_ctx = crypto_tfm_ctx(tfm); |
| |
| if (!iaa_crypto_enabled) { |
| pr_debug("iaa_crypto disabled, not compressing\n"); |
| return -ENODEV; |
| } |
| |
| if (!req->src || !req->slen) { |
| pr_debug("invalid src, not compressing\n"); |
| return -EINVAL; |
| } |
| |
| cpu = get_cpu(); |
| wq = wq_table_next_wq(cpu); |
| put_cpu(); |
| if (!wq) { |
| pr_debug("no wq configured for cpu=%d\n", cpu); |
| return -ENODEV; |
| } |
| |
| ret = iaa_wq_get(wq); |
| if (ret) { |
| pr_debug("no wq available for cpu=%d\n", cpu); |
| return -ENODEV; |
| } |
| |
| iaa_wq = idxd_wq_get_private(wq); |
| |
| if (!req->dst) { |
| gfp_t flags = req->flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL : GFP_ATOMIC; |
| |
| /* incompressible data will always be < 2 * slen */ |
| req->dlen = 2 * req->slen; |
| order = order_base_2(round_up(req->dlen, PAGE_SIZE) / PAGE_SIZE); |
| req->dst = sgl_alloc_order(req->dlen, order, false, flags, NULL); |
| if (!req->dst) { |
| ret = -ENOMEM; |
| order = -1; |
| goto out; |
| } |
| disable_async = true; |
| } |
| |
| dev = &wq->idxd->pdev->dev; |
| |
| nr_sgs = dma_map_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); |
| if (nr_sgs <= 0 || nr_sgs > 1) { |
| dev_dbg(dev, "couldn't map src sg for iaa device %d," |
| " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, |
| iaa_wq->wq->id, ret); |
| ret = -EIO; |
| goto out; |
| } |
| src_addr = sg_dma_address(req->src); |
| dev_dbg(dev, "dma_map_sg, src_addr %llx, nr_sgs %d, req->src %p," |
| " req->slen %d, sg_dma_len(sg) %d\n", src_addr, nr_sgs, |
| req->src, req->slen, sg_dma_len(req->src)); |
| |
| nr_sgs = dma_map_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); |
| if (nr_sgs <= 0 || nr_sgs > 1) { |
| dev_dbg(dev, "couldn't map dst sg for iaa device %d," |
| " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, |
| iaa_wq->wq->id, ret); |
| ret = -EIO; |
| goto err_map_dst; |
| } |
| dst_addr = sg_dma_address(req->dst); |
| dev_dbg(dev, "dma_map_sg, dst_addr %llx, nr_sgs %d, req->dst %p," |
| " req->dlen %d, sg_dma_len(sg) %d\n", dst_addr, nr_sgs, |
| req->dst, req->dlen, sg_dma_len(req->dst)); |
| |
| start_time_ns = iaa_get_ts(); |
| ret = iaa_compress(tfm, req, wq, src_addr, req->slen, dst_addr, |
| &req->dlen, &compression_crc, disable_async); |
| update_max_comp_delay_ns(start_time_ns); |
| if (ret == -EINPROGRESS) |
| return ret; |
| |
| if (!ret && compression_ctx->verify_compress) { |
| ret = iaa_remap_for_verify(dev, iaa_wq, req, &src_addr, &dst_addr); |
| if (ret) { |
| dev_dbg(dev, "%s: compress verify remap failed ret=%d\n", __func__, ret); |
| goto out; |
| } |
| |
| ret = iaa_compress_verify(tfm, req, wq, src_addr, req->slen, |
| dst_addr, &req->dlen, compression_crc); |
| if (ret) |
| dev_dbg(dev, "asynchronous compress verification failed ret=%d\n", ret); |
| |
| dma_unmap_sg(dev, req->dst, sg_nents(req->dst), DMA_TO_DEVICE); |
| dma_unmap_sg(dev, req->src, sg_nents(req->src), DMA_FROM_DEVICE); |
| |
| goto out; |
| } |
| |
| if (ret) |
| dev_dbg(dev, "asynchronous compress failed ret=%d\n", ret); |
| |
| dma_unmap_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); |
| err_map_dst: |
| dma_unmap_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); |
| out: |
| iaa_wq_put(wq); |
| |
| if (order >= 0) |
| sgl_free_order(req->dst, order); |
| |
| return ret; |
| } |
| |
| static int iaa_comp_adecompress_alloc_dest(struct acomp_req *req) |
| { |
| gfp_t flags = req->flags & CRYPTO_TFM_REQ_MAY_SLEEP ? |
| GFP_KERNEL : GFP_ATOMIC; |
| struct crypto_tfm *tfm = req->base.tfm; |
| dma_addr_t src_addr, dst_addr; |
| int nr_sgs, cpu, ret = 0; |
| struct iaa_wq *iaa_wq; |
| struct device *dev; |
| struct idxd_wq *wq; |
| u64 start_time_ns; |
| int order = -1; |
| |
| cpu = get_cpu(); |
| wq = wq_table_next_wq(cpu); |
| put_cpu(); |
| if (!wq) { |
| pr_debug("no wq configured for cpu=%d\n", cpu); |
| return -ENODEV; |
| } |
| |
| ret = iaa_wq_get(wq); |
| if (ret) { |
| pr_debug("no wq available for cpu=%d\n", cpu); |
| return -ENODEV; |
| } |
| |
| iaa_wq = idxd_wq_get_private(wq); |
| |
| dev = &wq->idxd->pdev->dev; |
| |
| nr_sgs = dma_map_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); |
| if (nr_sgs <= 0 || nr_sgs > 1) { |
| dev_dbg(dev, "couldn't map src sg for iaa device %d," |
| " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, |
| iaa_wq->wq->id, ret); |
| ret = -EIO; |
| goto out; |
| } |
| src_addr = sg_dma_address(req->src); |
| dev_dbg(dev, "dma_map_sg, src_addr %llx, nr_sgs %d, req->src %p," |
| " req->slen %d, sg_dma_len(sg) %d\n", src_addr, nr_sgs, |
| req->src, req->slen, sg_dma_len(req->src)); |
| |
| req->dlen = 4 * req->slen; /* start with ~avg comp rato */ |
| alloc_dest: |
| order = order_base_2(round_up(req->dlen, PAGE_SIZE) / PAGE_SIZE); |
| req->dst = sgl_alloc_order(req->dlen, order, false, flags, NULL); |
| if (!req->dst) { |
| ret = -ENOMEM; |
| order = -1; |
| goto out; |
| } |
| |
| nr_sgs = dma_map_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); |
| if (nr_sgs <= 0 || nr_sgs > 1) { |
| dev_dbg(dev, "couldn't map dst sg for iaa device %d," |
| " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, |
| iaa_wq->wq->id, ret); |
| ret = -EIO; |
| goto err_map_dst; |
| } |
| |
| dst_addr = sg_dma_address(req->dst); |
| dev_dbg(dev, "dma_map_sg, dst_addr %llx, nr_sgs %d, req->dst %p," |
| " req->dlen %d, sg_dma_len(sg) %d\n", dst_addr, nr_sgs, |
| req->dst, req->dlen, sg_dma_len(req->dst)); |
| start_time_ns = iaa_get_ts(); |
| ret = iaa_decompress(tfm, req, wq, src_addr, req->slen, |
| dst_addr, &req->dlen, true); |
| update_max_decomp_delay_ns(start_time_ns); |
| if (ret == -EOVERFLOW) { |
| dma_unmap_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); |
| req->dlen *= 2; |
| if (req->dlen > CRYPTO_ACOMP_DST_MAX) |
| goto err_map_dst; |
| goto alloc_dest; |
| } |
| |
| if (ret != 0) |
| dev_dbg(dev, "asynchronous decompress failed ret=%d\n", ret); |
| |
| dma_unmap_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); |
| err_map_dst: |
| dma_unmap_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); |
| out: |
| iaa_wq_put(wq); |
| |
| if (order >= 0) |
| sgl_free_order(req->dst, order); |
| |
| return ret; |
| } |
| |
| static int iaa_comp_adecompress(struct acomp_req *req) |
| { |
| struct crypto_tfm *tfm = req->base.tfm; |
| dma_addr_t src_addr, dst_addr; |
| int nr_sgs, cpu, ret = 0; |
| struct iaa_wq *iaa_wq; |
| struct device *dev; |
| u64 start_time_ns; |
| struct idxd_wq *wq; |
| |
| if (!iaa_crypto_enabled) { |
| pr_debug("iaa_crypto disabled, not decompressing\n"); |
| return -ENODEV; |
| } |
| |
| if (!req->src || !req->slen) { |
| pr_debug("invalid src, not decompressing\n"); |
| return -EINVAL; |
| } |
| |
| if (!req->dst) |
| return iaa_comp_adecompress_alloc_dest(req); |
| |
| cpu = get_cpu(); |
| wq = wq_table_next_wq(cpu); |
| put_cpu(); |
| if (!wq) { |
| pr_debug("no wq configured for cpu=%d\n", cpu); |
| return -ENODEV; |
| } |
| |
| ret = iaa_wq_get(wq); |
| if (ret) { |
| pr_debug("no wq available for cpu=%d\n", cpu); |
| return -ENODEV; |
| } |
| |
| iaa_wq = idxd_wq_get_private(wq); |
| |
| dev = &wq->idxd->pdev->dev; |
| |
| nr_sgs = dma_map_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); |
| if (nr_sgs <= 0 || nr_sgs > 1) { |
| dev_dbg(dev, "couldn't map src sg for iaa device %d," |
| " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, |
| iaa_wq->wq->id, ret); |
| ret = -EIO; |
| goto out; |
| } |
| src_addr = sg_dma_address(req->src); |
| dev_dbg(dev, "dma_map_sg, src_addr %llx, nr_sgs %d, req->src %p," |
| " req->slen %d, sg_dma_len(sg) %d\n", src_addr, nr_sgs, |
| req->src, req->slen, sg_dma_len(req->src)); |
| |
| nr_sgs = dma_map_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); |
| if (nr_sgs <= 0 || nr_sgs > 1) { |
| dev_dbg(dev, "couldn't map dst sg for iaa device %d," |
| " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, |
| iaa_wq->wq->id, ret); |
| ret = -EIO; |
| goto err_map_dst; |
| } |
| dst_addr = sg_dma_address(req->dst); |
| dev_dbg(dev, "dma_map_sg, dst_addr %llx, nr_sgs %d, req->dst %p," |
| " req->dlen %d, sg_dma_len(sg) %d\n", dst_addr, nr_sgs, |
| req->dst, req->dlen, sg_dma_len(req->dst)); |
| |
| start_time_ns = iaa_get_ts(); |
| ret = iaa_decompress(tfm, req, wq, src_addr, req->slen, |
| dst_addr, &req->dlen, false); |
| update_max_decomp_delay_ns(start_time_ns); |
| if (ret == -EINPROGRESS) |
| return ret; |
| |
| if (ret != 0) |
| dev_dbg(dev, "asynchronous decompress failed ret=%d\n", ret); |
| |
| dma_unmap_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); |
| err_map_dst: |
| dma_unmap_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); |
| out: |
| iaa_wq_put(wq); |
| |
| return ret; |
| } |
| |
| static void compression_ctx_init(struct iaa_compression_ctx *ctx) |
| { |
| ctx->verify_compress = iaa_verify_compress; |
| ctx->async_mode = async_mode; |
| ctx->use_irq = use_irq; |
| } |
| |
| static int iaa_comp_init_fixed(struct crypto_acomp *acomp_tfm) |
| { |
| struct crypto_tfm *tfm = crypto_acomp_tfm(acomp_tfm); |
| struct iaa_compression_ctx *ctx = crypto_tfm_ctx(tfm); |
| |
| compression_ctx_init(ctx); |
| |
| ctx->mode = IAA_MODE_FIXED; |
| |
| return 0; |
| } |
| |
| static void dst_free(struct scatterlist *sgl) |
| { |
| /* |
| * Called for req->dst = NULL cases but we free elsewhere |
| * using sgl_free_order(). |
| */ |
| } |
| |
| static struct acomp_alg iaa_acomp_fixed_deflate = { |
| .init = iaa_comp_init_fixed, |
| .compress = iaa_comp_acompress, |
| .decompress = iaa_comp_adecompress, |
| .dst_free = dst_free, |
| .base = { |
| .cra_name = "deflate", |
| .cra_driver_name = "deflate-iaa", |
| .cra_flags = CRYPTO_ALG_ASYNC, |
| .cra_ctxsize = sizeof(struct iaa_compression_ctx), |
| .cra_module = THIS_MODULE, |
| .cra_priority = IAA_ALG_PRIORITY, |
| } |
| }; |
| |
| static int iaa_register_compression_device(void) |
| { |
| int ret; |
| |
| ret = crypto_register_acomp(&iaa_acomp_fixed_deflate); |
| if (ret) { |
| pr_err("deflate algorithm acomp fixed registration failed (%d)\n", ret); |
| goto out; |
| } |
| |
| iaa_crypto_registered = true; |
| out: |
| return ret; |
| } |
| |
| static int iaa_unregister_compression_device(void) |
| { |
| if (iaa_crypto_registered) |
| crypto_unregister_acomp(&iaa_acomp_fixed_deflate); |
| |
| return 0; |
| } |
| |
| static int iaa_crypto_probe(struct idxd_dev *idxd_dev) |
| { |
| struct idxd_wq *wq = idxd_dev_to_wq(idxd_dev); |
| struct idxd_device *idxd = wq->idxd; |
| struct idxd_driver_data *data = idxd->data; |
| struct device *dev = &idxd_dev->conf_dev; |
| bool first_wq = false; |
| int ret = 0; |
| |
| if (idxd->state != IDXD_DEV_ENABLED) |
| return -ENXIO; |
| |
| if (data->type != IDXD_TYPE_IAX) |
| return -ENODEV; |
| |
| mutex_lock(&wq->wq_lock); |
| |
| if (idxd_wq_get_private(wq)) { |
| mutex_unlock(&wq->wq_lock); |
| return -EBUSY; |
| } |
| |
| if (!idxd_wq_driver_name_match(wq, dev)) { |
| dev_dbg(dev, "wq %d.%d driver_name match failed: wq driver_name %s, dev driver name %s\n", |
| idxd->id, wq->id, wq->driver_name, dev->driver->name); |
| idxd->cmd_status = IDXD_SCMD_WQ_NO_DRV_NAME; |
| ret = -ENODEV; |
| goto err; |
| } |
| |
| wq->type = IDXD_WQT_KERNEL; |
| |
| ret = idxd_drv_enable_wq(wq); |
| if (ret < 0) { |
| dev_dbg(dev, "enable wq %d.%d failed: %d\n", |
| idxd->id, wq->id, ret); |
| ret = -ENXIO; |
| goto err; |
| } |
| |
| mutex_lock(&iaa_devices_lock); |
| |
| if (list_empty(&iaa_devices)) { |
| ret = alloc_wq_table(wq->idxd->max_wqs); |
| if (ret) |
| goto err_alloc; |
| first_wq = true; |
| } |
| |
| ret = save_iaa_wq(wq); |
| if (ret) |
| goto err_save; |
| |
| rebalance_wq_table(); |
| |
| if (first_wq) { |
| iaa_crypto_enabled = true; |
| ret = iaa_register_compression_device(); |
| if (ret != 0) { |
| iaa_crypto_enabled = false; |
| dev_dbg(dev, "IAA compression device registration failed\n"); |
| goto err_register; |
| } |
| try_module_get(THIS_MODULE); |
| |
| pr_info("iaa_crypto now ENABLED\n"); |
| } |
| |
| mutex_unlock(&iaa_devices_lock); |
| out: |
| mutex_unlock(&wq->wq_lock); |
| |
| return ret; |
| |
| err_register: |
| remove_iaa_wq(wq); |
| free_iaa_wq(idxd_wq_get_private(wq)); |
| err_save: |
| if (first_wq) |
| free_wq_table(); |
| err_alloc: |
| mutex_unlock(&iaa_devices_lock); |
| idxd_drv_disable_wq(wq); |
| err: |
| wq->type = IDXD_WQT_NONE; |
| |
| goto out; |
| } |
| |
| static void iaa_crypto_remove(struct idxd_dev *idxd_dev) |
| { |
| struct idxd_wq *wq = idxd_dev_to_wq(idxd_dev); |
| struct idxd_device *idxd = wq->idxd; |
| struct iaa_wq *iaa_wq; |
| bool free = false; |
| |
| idxd_wq_quiesce(wq); |
| |
| mutex_lock(&wq->wq_lock); |
| mutex_lock(&iaa_devices_lock); |
| |
| remove_iaa_wq(wq); |
| |
| spin_lock(&idxd->dev_lock); |
| iaa_wq = idxd_wq_get_private(wq); |
| if (!iaa_wq) { |
| spin_unlock(&idxd->dev_lock); |
| pr_err("%s: no iaa_wq available to remove\n", __func__); |
| goto out; |
| } |
| |
| if (iaa_wq->ref) { |
| iaa_wq->remove = true; |
| } else { |
| wq = iaa_wq->wq; |
| idxd_wq_set_private(wq, NULL); |
| free = true; |
| } |
| spin_unlock(&idxd->dev_lock); |
| if (free) { |
| __free_iaa_wq(iaa_wq); |
| kfree(iaa_wq); |
| } |
| |
| idxd_drv_disable_wq(wq); |
| rebalance_wq_table(); |
| |
| if (nr_iaa == 0) { |
| iaa_crypto_enabled = false; |
| free_wq_table(); |
| module_put(THIS_MODULE); |
| |
| pr_info("iaa_crypto now DISABLED\n"); |
| } |
| out: |
| mutex_unlock(&iaa_devices_lock); |
| mutex_unlock(&wq->wq_lock); |
| } |
| |
| static enum idxd_dev_type dev_types[] = { |
| IDXD_DEV_WQ, |
| IDXD_DEV_NONE, |
| }; |
| |
| static struct idxd_device_driver iaa_crypto_driver = { |
| .probe = iaa_crypto_probe, |
| .remove = iaa_crypto_remove, |
| .name = IDXD_SUBDRIVER_NAME, |
| .type = dev_types, |
| .desc_complete = iaa_desc_complete, |
| }; |
| |
| static int __init iaa_crypto_init_module(void) |
| { |
| int ret = 0; |
| int node; |
| |
| nr_cpus = num_online_cpus(); |
| for_each_node_with_cpus(node) |
| nr_nodes++; |
| if (!nr_nodes) { |
| pr_err("IAA couldn't find any nodes with cpus\n"); |
| return -ENODEV; |
| } |
| nr_cpus_per_node = nr_cpus / nr_nodes; |
| |
| if (crypto_has_comp("deflate-generic", 0, 0)) |
| deflate_generic_tfm = crypto_alloc_comp("deflate-generic", 0, 0); |
| |
| if (IS_ERR_OR_NULL(deflate_generic_tfm)) { |
| pr_err("IAA could not alloc %s tfm: errcode = %ld\n", |
| "deflate-generic", PTR_ERR(deflate_generic_tfm)); |
| return -ENOMEM; |
| } |
| |
| ret = iaa_aecs_init_fixed(); |
| if (ret < 0) { |
| pr_debug("IAA fixed compression mode init failed\n"); |
| goto err_aecs_init; |
| } |
| |
| ret = idxd_driver_register(&iaa_crypto_driver); |
| if (ret) { |
| pr_debug("IAA wq sub-driver registration failed\n"); |
| goto err_driver_reg; |
| } |
| |
| ret = driver_create_file(&iaa_crypto_driver.drv, |
| &driver_attr_verify_compress); |
| if (ret) { |
| pr_debug("IAA verify_compress attr creation failed\n"); |
| goto err_verify_attr_create; |
| } |
| |
| ret = driver_create_file(&iaa_crypto_driver.drv, |
| &driver_attr_sync_mode); |
| if (ret) { |
| pr_debug("IAA sync mode attr creation failed\n"); |
| goto err_sync_attr_create; |
| } |
| |
| if (iaa_crypto_debugfs_init()) |
| pr_warn("debugfs init failed, stats not available\n"); |
| |
| pr_debug("initialized\n"); |
| out: |
| return ret; |
| |
| err_sync_attr_create: |
| driver_remove_file(&iaa_crypto_driver.drv, |
| &driver_attr_verify_compress); |
| err_verify_attr_create: |
| idxd_driver_unregister(&iaa_crypto_driver); |
| err_driver_reg: |
| iaa_aecs_cleanup_fixed(); |
| err_aecs_init: |
| crypto_free_comp(deflate_generic_tfm); |
| |
| goto out; |
| } |
| |
| static void __exit iaa_crypto_cleanup_module(void) |
| { |
| if (iaa_unregister_compression_device()) |
| pr_debug("IAA compression device unregister failed\n"); |
| |
| iaa_crypto_debugfs_cleanup(); |
| driver_remove_file(&iaa_crypto_driver.drv, |
| &driver_attr_sync_mode); |
| driver_remove_file(&iaa_crypto_driver.drv, |
| &driver_attr_verify_compress); |
| idxd_driver_unregister(&iaa_crypto_driver); |
| iaa_aecs_cleanup_fixed(); |
| crypto_free_comp(deflate_generic_tfm); |
| |
| pr_debug("cleaned up\n"); |
| } |
| |
| MODULE_IMPORT_NS(IDXD); |
| MODULE_LICENSE("GPL"); |
| MODULE_ALIAS_IDXD_DEVICE(0); |
| MODULE_AUTHOR("Intel Corporation"); |
| MODULE_DESCRIPTION("IAA Compression Accelerator Crypto Driver"); |
| |
| module_init(iaa_crypto_init_module); |
| module_exit(iaa_crypto_cleanup_module); |