Skip to content

Commit dda44eb

Browse files
hmynenimpe
authored andcommitted
powerpc/vas: Add VAS user space API
On power9, userspace can send GZIP compression requests directly to NX once kernel establishes NX channel / window with VAS. This patch provides user space API which allows user space to establish channel using 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, VAS_TX_WIN_OPEN icoctl to open a window on specific VAS instance, 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(). Only NX GZIP coprocessor type is supported right now and allow GZIP engine access via /dev/crypto/nx-gzip device node. Thanks to Michael Ellerman for his changes and suggestions to make the ioctl generic to support any coprocessor type. Signed-off-by: Sukadev Bhattiprolu <[email protected]> Signed-off-by: Haren Myneni <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/1587114121.2275.1109.camel@hbabu-laptop
1 parent 45f25a7 commit dda44eb

File tree

5 files changed

+296
-4
lines changed

5 files changed

+296
-4
lines changed

arch/powerpc/include/asm/vas.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,16 @@ int vas_copy_crb(void *crb, int offset);
163163
*/
164164
int vas_paste_crb(struct vas_window *win, int offset, bool re);
165165

166+
/*
167+
* Register / unregister coprocessor type to VAS API which will be exported
168+
* to user space. Applications can use this API to open / close window
169+
* which can be used to send / receive requests directly to cooprcessor.
170+
*
171+
* Only NX GZIP coprocessor type is supported now, but this API can be
172+
* used for others in future.
173+
*/
174+
int vas_register_coproc_api(struct module *mod, enum vas_cop_type cop_type,
175+
const char *name);
176+
void vas_unregister_coproc_api(void);
177+
166178
#endif /* __ASM_POWERPC_VAS_H */

arch/powerpc/platforms/powernv/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ obj-$(CONFIG_MEMORY_FAILURE) += opal-memory-errors.o
1717
obj-$(CONFIG_OPAL_PRD) += opal-prd.o
1818
obj-$(CONFIG_PERF_EVENTS) += opal-imc.o
1919
obj-$(CONFIG_PPC_MEMTRACE) += memtrace.o
20-
obj-$(CONFIG_PPC_VAS) += vas.o vas-window.o vas-debug.o vas-fault.o
20+
obj-$(CONFIG_PPC_VAS) += vas.o vas-window.o vas-debug.o vas-fault.o vas-api.o
2121
obj-$(CONFIG_OCXL_BASE) += ocxl.o
2222
obj-$(CONFIG_SCOM_DEBUGFS) += opal-xscom.o
2323
obj-$(CONFIG_PPC_SECURE_BOOT) += opal-secvar.o
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* VAS user space API for its accelerators (Only NX-GZIP is supported now)
4+
* Copyright (C) 2019 Haren Myneni, IBM Corp
5+
*/
6+
7+
#include <linux/kernel.h>
8+
#include <linux/device.h>
9+
#include <linux/cdev.h>
10+
#include <linux/fs.h>
11+
#include <linux/slab.h>
12+
#include <linux/uaccess.h>
13+
#include <asm/vas.h>
14+
#include <uapi/asm/vas-api.h>
15+
#include "vas.h"
16+
17+
/*
18+
* The driver creates the device node that can be used as follows:
19+
* For NX-GZIP
20+
*
21+
* fd = open("/dev/crypto/nx-gzip", O_RDWR);
22+
* rc = ioctl(fd, VAS_TX_WIN_OPEN, &attr);
23+
* paste_addr = mmap(NULL, PAGE_SIZE, prot, MAP_SHARED, fd, 0ULL).
24+
* vas_copy(&crb, 0, 1);
25+
* vas_paste(paste_addr, 0, 1);
26+
* close(fd) or exit process to close window.
27+
*
28+
* where "vas_copy" and "vas_paste" are defined in copy-paste.h.
29+
* copy/paste returns to the user space directly. So refer NX hardware
30+
* documententation for exact copy/paste usage and completion / error
31+
* conditions.
32+
*/
33+
34+
/*
35+
* Wrapper object for the nx-gzip device - there is just one instance of
36+
* this node for the whole system.
37+
*/
38+
static struct coproc_dev {
39+
struct cdev cdev;
40+
struct device *device;
41+
char *name;
42+
dev_t devt;
43+
struct class *class;
44+
enum vas_cop_type cop_type;
45+
} coproc_device;
46+
47+
struct coproc_instance {
48+
struct coproc_dev *coproc;
49+
struct vas_window *txwin;
50+
};
51+
52+
static char *coproc_devnode(struct device *dev, umode_t *mode)
53+
{
54+
return kasprintf(GFP_KERNEL, "crypto/%s", dev_name(dev));
55+
}
56+
57+
static int coproc_open(struct inode *inode, struct file *fp)
58+
{
59+
struct coproc_instance *cp_inst;
60+
61+
cp_inst = kzalloc(sizeof(*cp_inst), GFP_KERNEL);
62+
if (!cp_inst)
63+
return -ENOMEM;
64+
65+
cp_inst->coproc = container_of(inode->i_cdev, struct coproc_dev,
66+
cdev);
67+
fp->private_data = cp_inst;
68+
69+
return 0;
70+
}
71+
72+
static int coproc_ioc_tx_win_open(struct file *fp, unsigned long arg)
73+
{
74+
void __user *uptr = (void __user *)arg;
75+
struct vas_tx_win_attr txattr = {};
76+
struct vas_tx_win_open_attr uattr;
77+
struct coproc_instance *cp_inst;
78+
struct vas_window *txwin;
79+
int rc, vasid;
80+
81+
cp_inst = fp->private_data;
82+
83+
/*
84+
* One window for file descriptor
85+
*/
86+
if (cp_inst->txwin)
87+
return -EEXIST;
88+
89+
rc = copy_from_user(&uattr, uptr, sizeof(uattr));
90+
if (rc) {
91+
pr_err("%s(): copy_from_user() returns %d\n", __func__, rc);
92+
return -EFAULT;
93+
}
94+
95+
if (uattr.version != 1) {
96+
pr_err("Invalid version\n");
97+
return -EINVAL;
98+
}
99+
100+
vasid = uattr.vas_id;
101+
102+
vas_init_tx_win_attr(&txattr, cp_inst->coproc->cop_type);
103+
104+
txattr.lpid = mfspr(SPRN_LPID);
105+
txattr.pidr = mfspr(SPRN_PID);
106+
txattr.user_win = true;
107+
txattr.rsvd_txbuf_count = false;
108+
txattr.pswid = false;
109+
110+
pr_devel("Pid %d: Opening txwin, PIDR %ld\n", txattr.pidr,
111+
mfspr(SPRN_PID));
112+
113+
txwin = vas_tx_win_open(vasid, cp_inst->coproc->cop_type, &txattr);
114+
if (IS_ERR(txwin)) {
115+
pr_err("%s() vas_tx_win_open() failed, %ld\n", __func__,
116+
PTR_ERR(txwin));
117+
return PTR_ERR(txwin);
118+
}
119+
120+
cp_inst->txwin = txwin;
121+
122+
return 0;
123+
}
124+
125+
static int coproc_release(struct inode *inode, struct file *fp)
126+
{
127+
struct coproc_instance *cp_inst = fp->private_data;
128+
129+
if (cp_inst->txwin) {
130+
vas_win_close(cp_inst->txwin);
131+
cp_inst->txwin = NULL;
132+
}
133+
134+
kfree(cp_inst);
135+
fp->private_data = NULL;
136+
137+
/*
138+
* We don't know here if user has other receive windows
139+
* open, so we can't really call clear_thread_tidr().
140+
* So, once the process calls set_thread_tidr(), the
141+
* TIDR value sticks around until process exits, resulting
142+
* in an extra copy in restore_sprs().
143+
*/
144+
145+
return 0;
146+
}
147+
148+
static int coproc_mmap(struct file *fp, struct vm_area_struct *vma)
149+
{
150+
struct coproc_instance *cp_inst = fp->private_data;
151+
struct vas_window *txwin;
152+
unsigned long pfn;
153+
u64 paste_addr;
154+
pgprot_t prot;
155+
int rc;
156+
157+
txwin = cp_inst->txwin;
158+
159+
if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) {
160+
pr_debug("%s(): size 0x%zx, PAGE_SIZE 0x%zx\n", __func__,
161+
(vma->vm_end - vma->vm_start), PAGE_SIZE);
162+
return -EINVAL;
163+
}
164+
165+
/* Ensure instance has an open send window */
166+
if (!txwin) {
167+
pr_err("%s(): No send window open?\n", __func__);
168+
return -EINVAL;
169+
}
170+
171+
vas_win_paste_addr(txwin, &paste_addr, NULL);
172+
pfn = paste_addr >> PAGE_SHIFT;
173+
174+
/* flags, page_prot from cxl_mmap(), except we want cachable */
175+
vma->vm_flags |= VM_IO | VM_PFNMAP;
176+
vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
177+
178+
prot = __pgprot(pgprot_val(vma->vm_page_prot) | _PAGE_DIRTY);
179+
180+
rc = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
181+
vma->vm_end - vma->vm_start, prot);
182+
183+
pr_devel("%s(): paste addr %llx at %lx, rc %d\n", __func__,
184+
paste_addr, vma->vm_start, rc);
185+
186+
return rc;
187+
}
188+
189+
static long coproc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
190+
{
191+
switch (cmd) {
192+
case VAS_TX_WIN_OPEN:
193+
return coproc_ioc_tx_win_open(fp, arg);
194+
default:
195+
return -EINVAL;
196+
}
197+
}
198+
199+
static struct file_operations coproc_fops = {
200+
.open = coproc_open,
201+
.release = coproc_release,
202+
.mmap = coproc_mmap,
203+
.unlocked_ioctl = coproc_ioctl,
204+
};
205+
206+
/*
207+
* Supporting only nx-gzip coprocessor type now, but this API code
208+
* extended to other coprocessor types later.
209+
*/
210+
int vas_register_coproc_api(struct module *mod, enum vas_cop_type cop_type,
211+
const char *name)
212+
{
213+
int rc = -EINVAL;
214+
dev_t devno;
215+
216+
rc = alloc_chrdev_region(&coproc_device.devt, 1, 1, name);
217+
if (rc) {
218+
pr_err("Unable to allocate coproc major number: %i\n", rc);
219+
return rc;
220+
}
221+
222+
pr_devel("%s device allocated, dev [%i,%i]\n", name,
223+
MAJOR(coproc_device.devt), MINOR(coproc_device.devt));
224+
225+
coproc_device.class = class_create(mod, name);
226+
if (IS_ERR(coproc_device.class)) {
227+
rc = PTR_ERR(coproc_device.class);
228+
pr_err("Unable to create %s class %d\n", name, rc);
229+
goto err_class;
230+
}
231+
coproc_device.class->devnode = coproc_devnode;
232+
coproc_device.cop_type = cop_type;
233+
234+
coproc_fops.owner = mod;
235+
cdev_init(&coproc_device.cdev, &coproc_fops);
236+
237+
devno = MKDEV(MAJOR(coproc_device.devt), 0);
238+
rc = cdev_add(&coproc_device.cdev, devno, 1);
239+
if (rc) {
240+
pr_err("cdev_add() failed %d\n", rc);
241+
goto err_cdev;
242+
}
243+
244+
coproc_device.device = device_create(coproc_device.class, NULL,
245+
devno, NULL, name, MINOR(devno));
246+
if (IS_ERR(coproc_device.device)) {
247+
rc = PTR_ERR(coproc_device.device);
248+
pr_err("Unable to create coproc-%d %d\n", MINOR(devno), rc);
249+
goto err;
250+
}
251+
252+
pr_devel("%s: Added dev [%d,%d]\n", __func__, MAJOR(devno),
253+
MINOR(devno));
254+
255+
return 0;
256+
257+
err:
258+
cdev_del(&coproc_device.cdev);
259+
err_cdev:
260+
class_destroy(coproc_device.class);
261+
err_class:
262+
unregister_chrdev_region(coproc_device.devt, 1);
263+
return rc;
264+
}
265+
EXPORT_SYMBOL_GPL(vas_register_coproc_api);
266+
267+
void vas_unregister_coproc_api(void)
268+
{
269+
dev_t devno;
270+
271+
cdev_del(&coproc_device.cdev);
272+
devno = MKDEV(MAJOR(coproc_device.devt), 0);
273+
device_destroy(coproc_device.class, devno);
274+
275+
class_destroy(coproc_device.class);
276+
unregister_chrdev_region(coproc_device.devt, 1);
277+
}
278+
EXPORT_SYMBOL_GPL(vas_unregister_coproc_api);

arch/powerpc/platforms/powernv/vas-window.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* Compute the paste address region for the window @window using the
2727
* ->paste_base_addr and ->paste_win_id_shift we got from device tree.
2828
*/
29-
static void compute_paste_address(struct vas_window *window, u64 *addr, int *len)
29+
void vas_win_paste_addr(struct vas_window *window, u64 *addr, int *len)
3030
{
3131
int winid;
3232
u64 base, shift;
@@ -80,7 +80,7 @@ static void *map_paste_region(struct vas_window *txwin)
8080
goto free_name;
8181

8282
txwin->paste_addr_name = name;
83-
compute_paste_address(txwin, &start, &len);
83+
vas_win_paste_addr(txwin, &start, &len);
8484

8585
if (!request_mem_region(start, len, name)) {
8686
pr_devel("%s(): request_mem_region(0x%llx, %d) failed\n",
@@ -138,7 +138,7 @@ static void unmap_paste_region(struct vas_window *window)
138138
u64 busaddr_start;
139139

140140
if (window->paste_kaddr) {
141-
compute_paste_address(window, &busaddr_start, &len);
141+
vas_win_paste_addr(window, &busaddr_start, &len);
142142
unmap_region(window->paste_kaddr, busaddr_start, len);
143143
window->paste_kaddr = NULL;
144144
kfree(window->paste_addr_name);

arch/powerpc/platforms/powernv/vas.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,8 @@ extern irqreturn_t vas_fault_handler(int irq, void *dev_id);
437437
extern void vas_return_credit(struct vas_window *window, bool tx);
438438
extern struct vas_window *vas_pswid_to_window(struct vas_instance *vinst,
439439
uint32_t pswid);
440+
extern void vas_win_paste_addr(struct vas_window *window, u64 *addr,
441+
int *len);
440442

441443
static inline int vas_window_pid(struct vas_window *window)
442444
{

0 commit comments

Comments
 (0)