diff mbox series

[08/10] crypto/NX: Add NX GZIP user space API

Message ID 1576415119.16318.4094.camel@hbabu-laptop (mailing list archive)
State Changes Requested
Delegated to: Herbert Xu
Headers show
Series crypto/nx: Enable GZIP engine and provide userpace API | expand

Commit Message

Haren Myneni Dec. 15, 2019, 1:05 p.m. UTC
On power9, userspace can send GZIP compression requests directly to NX
once kernel establishes NX channel / window. This patch provides GZIP
engine access to user space via /dev/crypto/nx-gzip device node with
open, VAS_TX_WIN_OPEN ioctl, mmap and close operations.

Each window corresponds to file descriptor and application can open
multiple windows. After the window is opened, mmap() system call to map
the hardware address of engine's request queue into the application's
virtual address space.

Then the application can then submit one or more requests to the the
engine by using the copy/paste instructions and pasting the CRBs to
the virtual address (aka paste_address) returned by mmap().

Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
Signed-off-by: Haren Myneni <haren@us.ibm.com>
---
 drivers/crypto/nx/Makefile            |   2 +-
 drivers/crypto/nx/nx-842-powernv.h    |   2 +
 drivers/crypto/nx/nx-commom-powernv.c |  21 ++-
 drivers/crypto/nx/nx-gzip-powernv.c   | 282 ++++++++++++++++++++++++++++++++++
 4 files changed, 304 insertions(+), 3 deletions(-)
 create mode 100644 drivers/crypto/nx/nx-gzip-powernv.c

Comments

Herbert Xu Dec. 17, 2019, 9:33 a.m. UTC | #1
On Sun, Dec 15, 2019 at 05:05:19AM -0800, Haren Myneni wrote:
> 
> On power9, userspace can send GZIP compression requests directly to NX
> once kernel establishes NX channel / window. This patch provides GZIP
> engine access to user space via /dev/crypto/nx-gzip device node with
> open, VAS_TX_WIN_OPEN ioctl, mmap and close operations.
> 
> Each window corresponds to file descriptor and application can open
> multiple windows. After the window is opened, mmap() system call to map
> the hardware address of engine's request queue into the application's
> virtual address space.
> 
> Then the application can then submit one or more requests to the the
> engine by using the copy/paste instructions and pasting the CRBs to
> the virtual address (aka paste_address) returned by mmap().
> 
> Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
> Signed-off-by: Haren Myneni <haren@us.ibm.com>
> ---
>  drivers/crypto/nx/Makefile            |   2 +-
>  drivers/crypto/nx/nx-842-powernv.h    |   2 +
>  drivers/crypto/nx/nx-commom-powernv.c |  21 ++-
>  drivers/crypto/nx/nx-gzip-powernv.c   | 282 ++++++++++++++++++++++++++++++++++
>  4 files changed, 304 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/crypto/nx/nx-gzip-powernv.c

We already have a kernel compress API which could be exposed
to user-space through af_alg.  If every driver created their
own user-space API it would be unmanageable.

Cheers,
Haren Myneni Dec. 19, 2019, 8:49 a.m. UTC | #2
On Tue, 2019-12-17 at 17:33 +0800, Herbert Xu wrote:
> On Sun, Dec 15, 2019 at 05:05:19AM -0800, Haren Myneni wrote:
> > 
> > On power9, userspace can send GZIP compression requests directly to NX
> > once kernel establishes NX channel / window. This patch provides GZIP
> > engine access to user space via /dev/crypto/nx-gzip device node with
> > open, VAS_TX_WIN_OPEN ioctl, mmap and close operations.
> > 
> > Each window corresponds to file descriptor and application can open
> > multiple windows. After the window is opened, mmap() system call to map
> > the hardware address of engine's request queue into the application's
> > virtual address space.
> > 
> > Then the application can then submit one or more requests to the the
> > engine by using the copy/paste instructions and pasting the CRBs to
> > the virtual address (aka paste_address) returned by mmap().
> > 
> > Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
> > Signed-off-by: Haren Myneni <haren@us.ibm.com>
> > ---
> >  drivers/crypto/nx/Makefile            |   2 +-
> >  drivers/crypto/nx/nx-842-powernv.h    |   2 +
> >  drivers/crypto/nx/nx-commom-powernv.c |  21 ++-
> >  drivers/crypto/nx/nx-gzip-powernv.c   | 282 ++++++++++++++++++++++++++++++++++
> >  4 files changed, 304 insertions(+), 3 deletions(-)
> >  create mode 100644 drivers/crypto/nx/nx-gzip-powernv.c
> 
> We already have a kernel compress API which could be exposed
> to user-space through af_alg.  If every driver created their
> own user-space API it would be unmanageable.

Thanks. 

Virtual Accelerator Switchboard (VAS) can provide support different
accelerators, Right now only NX is used, but possible to extend to
others in future. Or different functionalities such as fast thread
wakeup (VAS feature) with VAS windows. 

So looking common VAS API for any its accelerators. Need open a window /
channel - open() and ioctl()) calls, and setup the communications with
mapping address to NX (mmap()) and close the window. Then user space
communicates to accelerator directly without kernel involvement.
Specific drivers should set window attributes such as how many requests
can be send at same time and etc. All other interfaces should be same
for any accelerator. 

Also, since user space sends requests directly, should restrict
malicious users to prevent overload NX (security issue). Allowing
sysadmin to restrict /dev/crypto/nx-gzip usage. 


As you suggested, SW crypto API (af_alg) can be used just for NX
compression like using API based on the accelerator functionalities. It
is socket based API with AF_ALG socket family. But is there a way for
sysadmin to restrict usage from user space? Need just few functions in
struct proto. 

static struct proto_ops {
	.family = PF_ALG,
	.ioctl = nxgzip_ioctl,
	.mmap = nxgzip_mmap,
	.release = nxgzip_release,
};

Thanks
Haren


> 
> Cheers,
Herbert Xu Dec. 20, 2019, 7:41 a.m. UTC | #3
On Thu, Dec 19, 2019 at 12:49:44AM -0800, Haren Myneni wrote:
> 
> Virtual Accelerator Switchboard (VAS) can provide support different
> accelerators, Right now only NX is used, but possible to extend to
> others in future. Or different functionalities such as fast thread
> wakeup (VAS feature) with VAS windows. 
> 
> So looking common VAS API for any its accelerators. Need open a window /
> channel - open() and ioctl()) calls, and setup the communications with
> mapping address to NX (mmap()) and close the window. Then user space
> communicates to accelerator directly without kernel involvement.
> Specific drivers should set window attributes such as how many requests
> can be send at same time and etc. All other interfaces should be same
> for any accelerator. 
> 
> Also, since user space sends requests directly, should restrict
> malicious users to prevent overload NX (security issue). Allowing
> sysadmin to restrict /dev/crypto/nx-gzip usage. 

If you are going to place your driver through the Crypto API then
it needs to use the Crypto API interface for user-space access.
That interface is af_alg.

If this is not a good fit then I suggest that you move your API
elsewhere, perhaps to the powerpc tree where the user-space API can
then be properly reviewed.

It is not feasible to review your driver's user-space API through
the crypto tree.

> As you suggested, SW crypto API (af_alg) can be used just for NX
> compression like using API based on the accelerator functionalities. It
> is socket based API with AF_ALG socket family. But is there a way for
> sysadmin to restrict usage from user space? Need just few functions in
> struct proto. 

The af_alg interface does not operate in the manner that you
describe.  It is an interface that maps onto the underlying kernel
Crypto API operations.  We currently don't have an af_alg module
for compression, but if we did we would be closely following the
current kernel compression interface.

One key feature of af_alg is that it normally is agnostic to the
underlying implementation.  That is, even when the hardware is
absent it would seamlessly switch over to a software implementation.

I say normally because there can be exceptions, e.g., with paes
and hardware keys.

Cheers,
diff mbox series

Patch

diff --git a/drivers/crypto/nx/Makefile b/drivers/crypto/nx/Makefile
index 1949449..0394a62 100644
--- a/drivers/crypto/nx/Makefile
+++ b/drivers/crypto/nx/Makefile
@@ -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-commom-powernv.o nx-842-powernv.o
+nx-compress-powernv-objs := nx-commom-powernv.o nx-842-powernv.o nx-gzip-powernv.o
diff --git a/drivers/crypto/nx/nx-842-powernv.h b/drivers/crypto/nx/nx-842-powernv.h
index b754023..8aca108 100644
--- a/drivers/crypto/nx/nx-842-powernv.h
+++ b/drivers/crypto/nx/nx-842-powernv.h
@@ -25,5 +25,7 @@  extern int nx842_exec_vas(const unsigned char *in, unsigned int inlen,
 extern int (*nx842_powernv_exec)(const unsigned char *in, unsigned int inlen,
 				unsigned char *out, unsigned int *outlenp,
 				void *workmem, int fc);
+extern int nxgzip_device_create(void);
+extern void nxgzip_device_delete(void);
 
 #endif /* __NX_COMPRESS_POWERNV_H__ */
diff --git a/drivers/crypto/nx/nx-commom-powernv.c b/drivers/crypto/nx/nx-commom-powernv.c
index 7cc736f..2b89677 100644
--- a/drivers/crypto/nx/nx-commom-powernv.c
+++ b/drivers/crypto/nx/nx-commom-powernv.c
@@ -427,10 +427,19 @@  static __init int nx_compress_powernv_init(void)
 		nx842_powernv_exec = nx842_exec_icswx;
 	} else {
 		/*
+		 * Creat nx-gxip char device for user space API.
+		 * /dev/crypto/nx-gzip.
+		 * 842 compression is supported only in kernel.
+		 */
+		ret = nxgzip_device_create();
+
+		/*
 		 * GZIP is not supported in kernel right now.
 		 * So open tx windows only for 842.
 		 */
-		ret = nx_open_percpu_txwins();
+		if (!ret)
+			ret = nx_open_percpu_txwins();
+
 		if (ret) {
 			nx_delete_coprocs();
 			return ret;
@@ -440,6 +449,7 @@  static __init int nx_compress_powernv_init(void)
 	}
 
 	ret = crypto_register_alg(&nx842_powernv_alg);
+
 	if (ret) {
 		nx_delete_coprocs();
 		return ret;
@@ -451,8 +461,15 @@  static __init int nx_compress_powernv_init(void)
 
 static void __exit nx_compress_powernv_exit(void)
 {
-	crypto_unregister_alg(&nx842_powernv_alg);
+	/*
+	 * GZIP engine is supported only power9 or later.
+	 * nx842_ct is used on power8 (icswx) and nx-gzip device is
+	 * created on power9 during init.
+	 */
+	if (!nx842_ct)
+		nxgzip_device_delete();
 
+	crypto_unregister_alg(&nx842_powernv_alg);
 	nx_delete_coprocs();
 }
 module_exit(nx_compress_powernv_exit);
diff --git a/drivers/crypto/nx/nx-gzip-powernv.c b/drivers/crypto/nx/nx-gzip-powernv.c
new file mode 100644
index 0000000..cfacfea
--- /dev/null
+++ b/drivers/crypto/nx/nx-gzip-powernv.c
@@ -0,0 +1,282 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * IBM PowerNV NX gzip compression user space API
+ * Copyright (C) 2019 Haren Myneni, IBM Corp
+ */
+
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <asm/vas.h>
+#include <uapi/asm/vas-api.h>
+#include "nx-842-powernv.h"
+
+/*
+ * The driver creates the device /dev/crypto/nx-gzip that can be
+ * used as follows:
+ *
+ *	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 excat copy/paste usage and completion / error
+ * conditions.
+ */
+
+static char	*nxgzip_dev_name = "nx-gzip";
+static atomic_t	nxgzip_instid = ATOMIC_INIT(0);
+
+/*
+ * Wrapper object for the nx-gzip device - there is just one instance of
+ * this node for the whole system.
+ */
+struct nxgzip_dev {
+	struct cdev cdev;
+	struct device *device;
+	char *name;
+	dev_t devt;
+	struct class *class;
+} nxgzip_device;
+
+/*
+ * One instance per open of a nx-gzip device. Each nxgzip_instance is
+ * associated with a VAS window after the caller issues
+ * VAS_GZIP_TX_WIN_OPEN ioctl.
+ */
+struct nxgzip_instance {
+	int id;
+	struct vas_window *txwin;
+};
+
+static char *nxgzip_devnode(struct device *dev, umode_t *mode)
+{
+	return kasprintf(GFP_KERNEL, "crypto/%s", dev_name(dev));
+}
+
+static int nxgzip_open(struct inode *inode, struct file *fp)
+{
+	struct nxgzip_instance *instance;
+
+	instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+	if (!instance)
+		return -ENOMEM;
+
+	instance->id = atomic_inc_return(&nxgzip_instid);
+
+	fp->private_data = instance;
+	return 0;
+}
+
+static int nx_ioc_gzip_tx_win_open(struct file *fp, unsigned long arg)
+{
+	int rc, vasid;
+	struct vas_tx_win_attr txattr;
+	struct vas_tx_win_open_attr uattr;
+	void __user *uptr = (void *)arg;
+	struct vas_window *txwin;
+	struct nxgzip_instance *nxti = fp->private_data;
+
+	if (!nxti)
+		return -EINVAL;
+
+	/*
+	 * One window for file descriptor
+	 */
+	if (nxti->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;
+
+	memset(&txattr, 0, sizeof(struct vas_tx_win_attr));
+	vas_init_tx_win_attr(&txattr, VAS_COP_TYPE_GZIP);
+
+	txattr.lpid = mfspr(SPRN_LPID);
+	txattr.pidr = mfspr(SPRN_PID);
+	txattr.user_win = true;
+	txattr.rsvd_txbuf_count = false;
+	txattr.pswid = false;
+	/*
+	 * txattr.wcreds_max is set to VAS_WCREDS_DEFAULT (1024) in
+	 * vas-window.c, but can be changed specific to GZIP depends
+	 * on user space need.
+	 * If needed to set txattr.wcreds_max here.
+	 */
+
+	pr_devel("Pid %d: Opening txwin, PIDR %ld\n", txattr.pidr,
+				mfspr(SPRN_PID));
+
+	txwin = vas_tx_win_open(vasid, VAS_COP_TYPE_GZIP, &txattr);
+	if (IS_ERR(txwin)) {
+		pr_err("%s() vas_tx_win_open() failed, %ld\n", __func__,
+					PTR_ERR(txwin));
+		return PTR_ERR(txwin);
+	}
+
+	nxti->txwin = txwin;
+
+	return 0;
+}
+
+static int nxgzip_release(struct inode *inode, struct file *fp)
+{
+	struct nxgzip_instance *instance;
+
+	instance = fp->private_data;
+
+	if (instance && instance->txwin) {
+		vas_win_close(instance->txwin);
+		instance->txwin = 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().
+	 */
+
+	kfree(instance);
+	fp->private_data = NULL;
+	atomic_dec(&nxgzip_instid);
+
+	return 0;
+}
+
+static int nxgzip_mmap(struct file *fp, struct vm_area_struct *vma)
+{
+	int rc;
+	pgprot_t prot;
+	u64 paste_addr;
+	unsigned long pfn;
+	struct nxgzip_instance *instance = fp->private_data;
+
+	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 (!instance->txwin) {
+		pr_err("%s(): No send window open?\n", __func__);
+		return -EINVAL;
+	}
+
+	paste_addr = vas_win_paste_addr(instance->txwin);
+	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 nxgzip_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case VAS_TX_WIN_OPEN:
+		return nx_ioc_gzip_tx_win_open(fp, arg);
+	default:
+		return -EINVAL;
+	}
+}
+
+const struct file_operations nxgzip_fops = {
+	.owner = THIS_MODULE,
+	.open = nxgzip_open,
+	.release = nxgzip_release,
+	.mmap = nxgzip_mmap,
+	.unlocked_ioctl = nxgzip_ioctl,
+};
+
+int nxgzip_device_create(void)
+{
+	int rc = -EINVAL;
+	dev_t devno;
+
+	rc = alloc_chrdev_region(&nxgzip_device.devt, 1, 1, "nx-gzip");
+	if (rc) {
+		pr_err("Unable to allocate nxgzip major number: %i\n", rc);
+		return rc;
+	}
+
+	pr_devel("NX-GZIP device allocated, dev [%i,%i]\n",
+			MAJOR(nxgzip_device.devt), MINOR(nxgzip_device.devt));
+
+	nxgzip_device.class = class_create(THIS_MODULE, "nxgzip");
+	if (IS_ERR(nxgzip_device.class)) {
+		rc = PTR_ERR(nxgzip_device.class);
+		pr_err("Unable to create NX-GZIP class %d\n", rc);
+		goto err_class;
+	}
+	nxgzip_device.class->devnode = nxgzip_devnode;
+
+	cdev_init(&nxgzip_device.cdev, &nxgzip_fops);
+
+	devno = MKDEV(MAJOR(nxgzip_device.devt), 0);
+	rc = cdev_add(&nxgzip_device.cdev, devno, 1);
+	if (rc) {
+		pr_err("cdev_add() failed %d\n", rc);
+		goto err_cdev;
+	}
+
+	nxgzip_device.device = device_create(nxgzip_device.class, NULL,
+			devno, NULL, nxgzip_dev_name, MINOR(devno));
+	if (IS_ERR(nxgzip_device.device)) {
+		rc = PTR_ERR(nxgzip_device.device);
+		pr_err("Unable to create nxgzip-%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(&nxgzip_device.cdev);
+err_cdev:
+	class_destroy(nxgzip_device.class);
+err_class:
+	unregister_chrdev_region(nxgzip_device.devt, 1);
+	return rc;
+}
+
+void nxgzip_device_delete(void)
+{
+	dev_t devno;
+
+	cdev_del(&nxgzip_device.cdev);
+	devno = MKDEV(MAJOR(nxgzip_device.devt), 0);
+	device_destroy(nxgzip_device.class, devno);
+
+	class_destroy(nxgzip_device.class);
+	unregister_chrdev_region(nxgzip_device.devt, 1);
+}