[PATCH 07/14] powerpc/vas: Read and process fault CRBs
Haren Myneni
haren at linux.vnet.ibm.com
Wed Nov 27 12:09:16 AEDT 2019
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 at linux.vnet.ibm.com>
Signed-off-by: Haren Myneni <haren at us.ibm.com>
---
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
More information about the Linuxppc-dev
mailing list