Commit 7948efc2 authored by Michael Ellerman's avatar Michael Ellerman

Merge NX gzip support into next

As described by Haren:

Power9 processor supports Virtual Accelerator Switchboard (VAS) which
allows kernel and userspace to send compression requests to Nest
Accelerator (NX) directly. The NX unit comprises of 2 842 compression
engines and 1 GZIP engine. Linux kernel already has 842 compression
support on kernel. This patch series adds GZIP compression support
from user space. The GZIP Compression engine implements the ZLIB and
GZIP compression algorithms. No plans of adding NX-GZIP compression
support in kernel right now.

Applications can send requests to NX directly with COPY/PASTE
instructions. But kernel has to establish channel / window on NX-GZIP
device for the userspace. So userspace access to the GZIP engine is
provided through /dev/crypto/nx-gzip device with several operations.

An application must open the this device to obtain a file
descriptor (fd). Using the fd, application should issue the
VAS_TX_WIN_OPEN ioctl to establish a connection to the engine. Once
window is opened, should use mmap() system call to map the hardware
address of engine's request queue into the application's virtual
address space. Then user space forms the request as co-processor
Request Block (CRB) and paste this CRB on the mapped HW address using
COPY/PASTE instructions. Application can poll on status flags (part of
CRB) with timeout for request completion.

For VAS_TX_WIN_OPEN ioctl, if user space passes vas_id = -1 (struct
vas_tx_win_open_attr), kernel determines the VAS instance on the
corresponding chip based on the CPU on which the process is executing.
Otherwise, the specified VAS instance is used if application passes
the proper VAS instance (vas_id listed in
/proc/device-tree/vas@*/ibm,vas_id).

Process can open multiple windows with different FDs or can send
several requests to NX on the same window at the same time.
parents b96ea616 c12e38b1
......@@ -30,6 +30,7 @@ powerpc
syscall64-abi
transactional_memory
ultravisor
vas-api
.. only:: subproject and html
......
This diff is collapsed.
......@@ -286,6 +286,7 @@ Code Seq# Include File Comments
'v' 00-1F linux/fs.h conflict!
'v' 00-0F linux/sonypi.h conflict!
'v' 00-0F media/v4l2-subdev.h conflict!
'v' 20-27 arch/powerpc/include/uapi/asm/vas-api.h VAS API
'v' C0-FF linux/meye.h conflict!
'w' all CERN SCI driver
'y' 00-1F packet based user level communications
......
......@@ -86,7 +86,6 @@ struct vas_tx_win_attr {
int wcreds_max;
int lpid;
int pidr; /* hardware PID (from SPRN_PID) */
int pid; /* linux process id */
int pswid;
int rsvd_txbuf_count;
int tc_mode;
......@@ -163,4 +162,16 @@ int vas_copy_crb(void *crb, int offset);
*/
int vas_paste_crb(struct vas_window *win, int offset, bool re);
/*
* Register / unregister coprocessor type to VAS API which will be exported
* to user space. Applications can use this API to open / close window
* which can be used to send / receive requests directly to cooprcessor.
*
* Only NX GZIP coprocessor type is supported now, but this API can be
* used for others in future.
*/
int vas_register_coproc_api(struct module *mod, enum vas_cop_type cop_type,
const char *name);
void vas_unregister_coproc_api(void);
#endif /* __ASM_POWERPC_VAS_H */
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
/*
* Copyright 2019 IBM Corp.
*/
#ifndef _UAPI_MISC_VAS_H
#define _UAPI_MISC_VAS_H
#include <asm/ioctl.h>
#define VAS_MAGIC 'v'
#define VAS_TX_WIN_OPEN _IOW(VAS_MAGIC, 0x20, struct vas_tx_win_open_attr)
struct vas_tx_win_open_attr {
__u32 version;
__s16 vas_id; /* specific instance of vas or -1 for default */
__u16 reserved1;
__u64 flags; /* Future use */
__u64 reserved2[6];
};
#endif /* _UAPI_MISC_VAS_H */
......@@ -17,7 +17,7 @@ obj-$(CONFIG_MEMORY_FAILURE) += opal-memory-errors.o
obj-$(CONFIG_OPAL_PRD) += opal-prd.o
obj-$(CONFIG_PERF_EVENTS) += opal-imc.o
obj-$(CONFIG_PPC_MEMTRACE) += memtrace.o
obj-$(CONFIG_PPC_VAS) += vas.o vas-window.o vas-debug.o vas-fault.o
obj-$(CONFIG_PPC_VAS) += vas.o vas-window.o vas-debug.o vas-fault.o vas-api.o
obj-$(CONFIG_OCXL_BASE) += ocxl.o
obj-$(CONFIG_SCOM_DEBUGFS) += opal-xscom.o
obj-$(CONFIG_PPC_SECURE_BOOT) += opal-secvar.o
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* VAS user space API for its accelerators (Only NX-GZIP is supported now)
* Copyright (C) 2019 Haren Myneni, IBM Corp
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <asm/vas.h>
#include <uapi/asm/vas-api.h>
#include "vas.h"
/*
* The driver creates the device node that can be used as follows:
* For NX-GZIP
*
* fd = open("/dev/crypto/nx-gzip", O_RDWR);
* rc = ioctl(fd, VAS_TX_WIN_OPEN, &attr);
* paste_addr = mmap(NULL, PAGE_SIZE, prot, MAP_SHARED, fd, 0ULL).
* vas_copy(&crb, 0, 1);
* vas_paste(paste_addr, 0, 1);
* close(fd) or exit process to close window.
*
* where "vas_copy" and "vas_paste" are defined in copy-paste.h.
* copy/paste returns to the user space directly. So refer NX hardware
* documententation for exact copy/paste usage and completion / error
* conditions.
*/
/*
* Wrapper object for the nx-gzip device - there is just one instance of
* this node for the whole system.
*/
static struct coproc_dev {
struct cdev cdev;
struct device *device;
char *name;
dev_t devt;
struct class *class;
enum vas_cop_type cop_type;
} coproc_device;
struct coproc_instance {
struct coproc_dev *coproc;
struct vas_window *txwin;
};
static char *coproc_devnode(struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "crypto/%s", dev_name(dev));
}
static int coproc_open(struct inode *inode, struct file *fp)
{
struct coproc_instance *cp_inst;
cp_inst = kzalloc(sizeof(*cp_inst), GFP_KERNEL);
if (!cp_inst)
return -ENOMEM;
cp_inst->coproc = container_of(inode->i_cdev, struct coproc_dev,
cdev);
fp->private_data = cp_inst;
return 0;
}
static int coproc_ioc_tx_win_open(struct file *fp, unsigned long arg)
{
void __user *uptr = (void __user *)arg;
struct vas_tx_win_attr txattr = {};
struct vas_tx_win_open_attr uattr;
struct coproc_instance *cp_inst;
struct vas_window *txwin;
int rc, vasid;
cp_inst = fp->private_data;
/*
* One window for file descriptor
*/
if (cp_inst->txwin)
return -EEXIST;
rc = copy_from_user(&uattr, uptr, sizeof(uattr));
if (rc) {
pr_err("%s(): copy_from_user() returns %d\n", __func__, rc);
return -EFAULT;
}
if (uattr.version != 1) {
pr_err("Invalid version\n");
return -EINVAL;
}
vasid = uattr.vas_id;
vas_init_tx_win_attr(&txattr, cp_inst->coproc->cop_type);
txattr.lpid = mfspr(SPRN_LPID);
txattr.pidr = mfspr(SPRN_PID);
txattr.user_win = true;
txattr.rsvd_txbuf_count = false;
txattr.pswid = false;
pr_devel("Pid %d: Opening txwin, PIDR %ld\n", txattr.pidr,
mfspr(SPRN_PID));
txwin = vas_tx_win_open(vasid, cp_inst->coproc->cop_type, &txattr);
if (IS_ERR(txwin)) {
pr_err("%s() vas_tx_win_open() failed, %ld\n", __func__,
PTR_ERR(txwin));
return PTR_ERR(txwin);
}
cp_inst->txwin = txwin;
return 0;
}
static int coproc_release(struct inode *inode, struct file *fp)
{
struct coproc_instance *cp_inst = fp->private_data;
if (cp_inst->txwin) {
vas_win_close(cp_inst->txwin);
cp_inst->txwin = NULL;
}
kfree(cp_inst);
fp->private_data = NULL;
/*
* We don't know here if user has other receive windows
* open, so we can't really call clear_thread_tidr().
* So, once the process calls set_thread_tidr(), the
* TIDR value sticks around until process exits, resulting
* in an extra copy in restore_sprs().
*/
return 0;
}
static int coproc_mmap(struct file *fp, struct vm_area_struct *vma)
{
struct coproc_instance *cp_inst = fp->private_data;
struct vas_window *txwin;
unsigned long pfn;
u64 paste_addr;
pgprot_t prot;
int rc;
txwin = cp_inst->txwin;
if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) {
pr_debug("%s(): size 0x%zx, PAGE_SIZE 0x%zx\n", __func__,
(vma->vm_end - vma->vm_start), PAGE_SIZE);
return -EINVAL;
}
/* Ensure instance has an open send window */
if (!txwin) {
pr_err("%s(): No send window open?\n", __func__);
return -EINVAL;
}
vas_win_paste_addr(txwin, &paste_addr, NULL);
pfn = paste_addr >> PAGE_SHIFT;
/* flags, page_prot from cxl_mmap(), except we want cachable */
vma->vm_flags |= VM_IO | VM_PFNMAP;
vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
prot = __pgprot(pgprot_val(vma->vm_page_prot) | _PAGE_DIRTY);
rc = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
vma->vm_end - vma->vm_start, prot);
pr_devel("%s(): paste addr %llx at %lx, rc %d\n", __func__,
paste_addr, vma->vm_start, rc);
return rc;
}
static long coproc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case VAS_TX_WIN_OPEN:
return coproc_ioc_tx_win_open(fp, arg);
default:
return -EINVAL;
}
}
static struct file_operations coproc_fops = {
.open = coproc_open,
.release = coproc_release,
.mmap = coproc_mmap,
.unlocked_ioctl = coproc_ioctl,
};
/*
* Supporting only nx-gzip coprocessor type now, but this API code
* extended to other coprocessor types later.
*/
int vas_register_coproc_api(struct module *mod, enum vas_cop_type cop_type,
const char *name)
{
int rc = -EINVAL;
dev_t devno;
rc = alloc_chrdev_region(&coproc_device.devt, 1, 1, name);
if (rc) {
pr_err("Unable to allocate coproc major number: %i\n", rc);
return rc;
}
pr_devel("%s device allocated, dev [%i,%i]\n", name,
MAJOR(coproc_device.devt), MINOR(coproc_device.devt));
coproc_device.class = class_create(mod, name);
if (IS_ERR(coproc_device.class)) {
rc = PTR_ERR(coproc_device.class);
pr_err("Unable to create %s class %d\n", name, rc);
goto err_class;
}
coproc_device.class->devnode = coproc_devnode;
coproc_device.cop_type = cop_type;
coproc_fops.owner = mod;
cdev_init(&coproc_device.cdev, &coproc_fops);
devno = MKDEV(MAJOR(coproc_device.devt), 0);
rc = cdev_add(&coproc_device.cdev, devno, 1);
if (rc) {
pr_err("cdev_add() failed %d\n", rc);
goto err_cdev;
}
coproc_device.device = device_create(coproc_device.class, NULL,
devno, NULL, name, MINOR(devno));
if (IS_ERR(coproc_device.device)) {
rc = PTR_ERR(coproc_device.device);
pr_err("Unable to create coproc-%d %d\n", MINOR(devno), rc);
goto err;
}
pr_devel("%s: Added dev [%d,%d]\n", __func__, MAJOR(devno),
MINOR(devno));
return 0;
err:
cdev_del(&coproc_device.cdev);
err_cdev:
class_destroy(coproc_device.class);
err_class:
unregister_chrdev_region(coproc_device.devt, 1);
return rc;
}
EXPORT_SYMBOL_GPL(vas_register_coproc_api);
void vas_unregister_coproc_api(void)
{
dev_t devno;
cdev_del(&coproc_device.cdev);
devno = MKDEV(MAJOR(coproc_device.devt), 0);
device_destroy(coproc_device.class, devno);
class_destroy(coproc_device.class);
unregister_chrdev_region(coproc_device.devt, 1);
}
EXPORT_SYMBOL_GPL(vas_unregister_coproc_api);
......@@ -26,7 +26,7 @@
* Compute the paste address region for the window @window using the
* ->paste_base_addr and ->paste_win_id_shift we got from device tree.
*/
static void compute_paste_address(struct vas_window *window, u64 *addr, int *len)
void vas_win_paste_addr(struct vas_window *window, u64 *addr, int *len)
{
int winid;
u64 base, shift;
......@@ -80,7 +80,7 @@ static void *map_paste_region(struct vas_window *txwin)
goto free_name;
txwin->paste_addr_name = name;
compute_paste_address(txwin, &start, &len);
vas_win_paste_addr(txwin, &start, &len);
if (!request_mem_region(start, len, name)) {
pr_devel("%s(): request_mem_region(0x%llx, %d) failed\n",
......@@ -138,7 +138,7 @@ static void unmap_paste_region(struct vas_window *window)
u64 busaddr_start;
if (window->paste_kaddr) {
compute_paste_address(window, &busaddr_start, &len);
vas_win_paste_addr(window, &busaddr_start, &len);
unmap_region(window->paste_kaddr, busaddr_start, len);
window->paste_kaddr = NULL;
kfree(window->paste_addr_name);
......@@ -817,7 +817,8 @@ void vas_init_rx_win_attr(struct vas_rx_win_attr *rxattr, enum vas_cop_type cop)
{
memset(rxattr, 0, sizeof(*rxattr));
if (cop == VAS_COP_TYPE_842 || cop == VAS_COP_TYPE_842_HIPRI) {
if (cop == VAS_COP_TYPE_842 || cop == VAS_COP_TYPE_842_HIPRI ||
cop == VAS_COP_TYPE_GZIP || cop == VAS_COP_TYPE_GZIP_HIPRI) {
rxattr->pin_win = true;
rxattr->nx_win = true;
rxattr->fault_win = false;
......@@ -892,7 +893,8 @@ void vas_init_tx_win_attr(struct vas_tx_win_attr *txattr, enum vas_cop_type cop)
{
memset(txattr, 0, sizeof(*txattr));
if (cop == VAS_COP_TYPE_842 || cop == VAS_COP_TYPE_842_HIPRI) {
if (cop == VAS_COP_TYPE_842 || cop == VAS_COP_TYPE_842_HIPRI ||
cop == VAS_COP_TYPE_GZIP || cop == VAS_COP_TYPE_GZIP_HIPRI) {
txattr->rej_no_credit = false;
txattr->rx_wcred_mode = true;
txattr->tx_wcred_mode = true;
......@@ -976,10 +978,15 @@ static bool tx_win_args_valid(enum vas_cop_type cop,
if (attr->wcreds_max > VAS_TX_WCREDS_MAX)
return false;
if (attr->user_win &&
(cop != VAS_COP_TYPE_FTW || attr->rsvd_txbuf_count))
if (attr->user_win) {
if (attr->rsvd_txbuf_count)
return false;
if (cop != VAS_COP_TYPE_FTW && cop != VAS_COP_TYPE_GZIP &&
cop != VAS_COP_TYPE_GZIP_HIPRI)
return false;
}
return true;
}
......
......@@ -437,6 +437,8 @@ extern irqreturn_t vas_fault_handler(int irq, void *dev_id);
extern void vas_return_credit(struct vas_window *window, bool tx);
extern struct vas_window *vas_pswid_to_window(struct vas_instance *vinst,
uint32_t pswid);
extern void vas_win_paste_addr(struct vas_window *window, u64 *addr,
int *len);
static inline int vas_window_pid(struct vas_window *window)
{
......
......@@ -15,4 +15,4 @@ obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_PSERIES) += nx-compress-pseries.o nx-compres
obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_POWERNV) += nx-compress-powernv.o nx-compress.o
nx-compress-objs := nx-842.o
nx-compress-pseries-objs := nx-842-pseries.o
nx-compress-powernv-objs := nx-842-powernv.o
nx-compress-powernv-objs := nx-common-powernv.o
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment