NX pastes CRB in fault FIFO and generates interrupt whenever faults on CRB. OS reads CRBs from fault FIFO and process them by setting faulting address in fault_storge_addr in CRB and update CSB. When CSB status is changed, process sends NX request after touching the fault address. Signed-off-by: Sukadev Bhattiprolu <sukadev@xxxxxxxxxxxxxxxxxx> Signed-off-by: Haren Myneni <haren@xxxxxxxxxx> --- arch/powerpc/platforms/powernv/vas-fault.c | 81 +++++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/vas-window.c | 51 ++++++++++++++++++ arch/powerpc/platforms/powernv/vas.h | 3 ++ 3 files changed, 135 insertions(+) diff --git a/arch/powerpc/platforms/powernv/vas-fault.c b/arch/powerpc/platforms/powernv/vas-fault.c index c6c105c..7a8b2b5 100644 --- a/arch/powerpc/platforms/powernv/vas-fault.c +++ b/arch/powerpc/platforms/powernv/vas-fault.c @@ -12,6 +12,7 @@ #include <linux/uaccess.h> #include <linux/kthread.h> #include <linux/sched/signal.h> +#include <linux/mmu_context.h> #include <asm/icswx.h> #include "vas.h" @@ -36,6 +37,84 @@ void vas_wakeup_fault_handler(int virq, void *arg) } /* + * Process CRBs that we receive on the fault window. + */ +static void process_fault_crbs(struct vas_instance *vinst) +{ + void *fifo; + struct vas_window *window; + struct coprocessor_request_block buf; + struct coprocessor_request_block *crb; + u64 csb_addr; + + crb = &buf; + + /* + * VAS can interrupt with multiple page faults. So process all + * valid CRBs within fault FIFO until reaches invalid CRB. + * For valid CRBs, csb_addr should be valid address points to CSB + * section within CRB. After reading CRB entry, it is reset with + * 0's in fault FIFO. + * + * In case kernel receives another interrupt with different page + * fault and is processed by the previous handling, will be returned + * from this function when it sees invalid CRB (means 0's). + */ + do { + mutex_lock(&vinst->mutex); + + /* + * Advance the fault fifo pointer to next CRB. + * Use CRB_SIZE rather than sizeof(*crb) since the latter is + * aligned to CRB_ALIGN (256) but the CRB written to by VAS is + * only CRB_SIZE in len. + */ + fifo = vinst->fault_fifo + (vinst->fault_crbs * CRB_SIZE); + csb_addr = ((struct coprocessor_request_block *)fifo)->csb_addr; + + /* + * Return if reached invalid CRB. + */ + if (!csb_addr) { + mutex_unlock(&vinst->mutex); + return; + } + + vinst->fault_crbs++; + if (vinst->fault_crbs == vinst->fault_fifo_size/CRB_SIZE) + vinst->fault_crbs = 0; + + memcpy(crb, fifo, CRB_SIZE); + memset(fifo, 0, CRB_SIZE); + mutex_unlock(&vinst->mutex); + + pr_devel("VAS[%d] fault_fifo %p, fifo %p, fault_crbs %d pending %d\n", + vinst->vas_id, vinst->fault_fifo, fifo, + vinst->fault_crbs, + atomic_read(&vinst->pending_fault)); + + window = vas_pswid_to_window(vinst, crb_nx_pswid(crb)); + + if (IS_ERR(window)) { + /* + * What now? We got an interrupt about a specific send + * window but we can't find that window and we can't + * even clean it up (return credit). + * But we should not get here. + */ + pr_err("VAS[%d] fault_fifo %p, fifo %p, pswid 0x%x, fault_crbs %d, pending %d bad CRB?\n", + vinst->vas_id, vinst->fault_fifo, fifo, + crb_nx_pswid(crb), vinst->fault_crbs, + atomic_read(&vinst->pending_fault)); + + WARN_ON_ONCE(1); + return; + } + + } while (true); +} + +/* * Fault handler thread for each VAS instance and process fault CRBs. */ static int fault_handler_func(void *arg) @@ -54,6 +133,8 @@ static int fault_handler_func(void *arg) break; atomic_dec(&vinst->pending_fault); + process_fault_crbs(vinst); + } while (!kthread_should_stop()); return 0; diff --git a/arch/powerpc/platforms/powernv/vas-window.c b/arch/powerpc/platforms/powernv/vas-window.c index 5f1faeb..7fc1542 100644 --- a/arch/powerpc/platforms/powernv/vas-window.c +++ b/arch/powerpc/platforms/powernv/vas-window.c @@ -1294,3 +1294,54 @@ u32 vas_win_id(struct vas_window *win) return encode_pswid(win->vinst->vas_id, win->winid); } EXPORT_SYMBOL_GPL(vas_win_id); + +struct vas_window *vas_pswid_to_window(struct vas_instance *vinst, + uint32_t pswid) +{ + int winid; + struct vas_window *window; + + if (!pswid) { + pr_devel("%s: called for pswid 0!\n", __func__); + return ERR_PTR(-ESRCH); + } + + decode_pswid(pswid, NULL, &winid); + + if (winid >= VAS_WINDOWS_PER_CHIP) + return ERR_PTR(-ESRCH); + + /* + * If application closes the window before the hardware + * returns the fault CRB, we should wait in vas_win_close() + * for the pending requests. so the window must be active + * and the process alive. + * + * If its a kernel process, we should not get any faults and + * should not get here. + */ + window = vinst->windows[winid]; + + if (!window) { + pr_err("PSWID decode: Could not find window for winid %d pswid %d vinst 0x%p\n", + winid, pswid, vinst); + return NULL; + } + + /* + * Do some sanity checks on the decoded window. Window should be + * NX GZIP user send window. FTW windows should not incur faults + * since their CRBs are ignored (not queued on FIFO or processed + * by NX). + */ + if (!window->tx_win || !window->user_win || !window->nx_win || + window->cop == VAS_COP_TYPE_FAULT || + window->cop == VAS_COP_TYPE_FTW) { + pr_err("PSWID decode: id %d, tx %d, user %d, nx %d, cop %d\n", + winid, window->tx_win, window->user_win, + window->nx_win, window->cop); + WARN_ON(1); + } + + return window; +} diff --git a/arch/powerpc/platforms/powernv/vas.h b/arch/powerpc/platforms/powernv/vas.h index ee284b3..eb929c7 100644 --- a/arch/powerpc/platforms/powernv/vas.h +++ b/arch/powerpc/platforms/powernv/vas.h @@ -317,6 +317,7 @@ struct vas_instance { int virq; int fault_fifo_size; void *fault_fifo; + int fault_crbs; atomic_t pending_fault; wait_queue_head_t fault_wq; struct task_struct *fault_handler; @@ -420,6 +421,8 @@ struct vas_winctx { extern void vas_wakeup_fault_handler(int virq, void *arg); extern int vas_setup_fault_handler(struct vas_instance *vinst); extern void vas_cleanup_fault_handler(struct vas_instance *vinst); +extern struct vas_window *vas_pswid_to_window(struct vas_instance *vinst, + uint32_t pswid); static inline void vas_log_write(struct vas_window *win, char *name, void *regptr, u64 val) -- 1.8.3.1