diff --git a/arch/powerpc/include/asm/vas.h b/arch/powerpc/include/asm/vas.h
index 57573d9c1e09..43cea69d1af1 100644
--- a/arch/powerpc/include/asm/vas.h
+++ b/arch/powerpc/include/asm/vas.h
@@ -29,6 +29,18 @@
#define VAS_THRESH_FIFO_GT_QTR_FULL 2
#define VAS_THRESH_FIFO_GT_EIGHTH_FULL 3
+/*
+ * VAS window status
+ */
+#define VAS_WIN_ACTIVE 0x0 /* Used in platform independent */
+ /* vas mmap() */
+#define VAS_WIN_CLOSED 0x1
+#define VAS_WIN_INACTIVE 0x2 /* Inactive due to HW failure */
+#define VAS_WIN_MOD_IN_PROCESS 0x3 /* Process of being modified, */
+ /* deallocated, or quiesced */
+#define VAS_WIN_NO_CRED_CLOSE 0x4 /* Linux specific status when */
+ /* window is closed due to lost */
+ /* credit */
/*
* Get/Set bit fields
*/
@@ -59,6 +71,8 @@ struct vas_user_win_ref {
struct pid *pid; /* PID of owner */
struct pid *tgid; /* Thread group ID of owner */
struct mm_struct *mm; /* Linux process mm_struct */
+ struct mutex mmap_mutex; /* protects paste address mmap() */
+ /* with DLPAR close/open windows */
};
/*@@ -67,6 +81,7 @@ struct vas_user_win_ref {
struct vas_window {
u32 winid;
u32 wcreds_max; /* Window credits */
+ u32 status;
enum vas_cop_type cop;
struct vas_user_win_ref task_ref;
char *dbgname;diff --git a/arch/powerpc/platforms/book3s/vas-api.c b/arch/powerpc/platforms/book3s/vas-api.c
index 4d82c92ddd52..2b0ced611f32 100644
--- a/arch/powerpc/platforms/book3s/vas-api.c
+++ b/arch/powerpc/platforms/book3s/vas-api.c
@@ -316,6 +316,7 @@ static int coproc_ioc_tx_win_open(struct file *fp, unsigned long arg)
return PTR_ERR(txwin);
}
+ mutex_init(&txwin->task_ref.mmap_mutex);
cp_inst->txwin = txwin;
return 0;
diff --git a/arch/powerpc/platforms/pseries/vas.c b/arch/powerpc/platforms/pseries/vas.c
index 6b35f67d5175..ace8ee7a99e6 100644
--- a/arch/powerpc/platforms/pseries/vas.c
+++ b/arch/powerpc/platforms/pseries/vas.c
@@ -493,6 +493,7 @@ static int get_vas_capabilities(u8 feat, enum vas_cop_feat_type type,
memset(vcaps, 0, sizeof(*vcaps));
INIT_LIST_HEAD(&vcaps->list);
+ vcaps->feat = feat;
caps = &vcaps->caps;
rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, feat,
@@ -531,6 +532,149 @@ static int get_vas_capabilities(u8 feat, enum vas_cop_feat_type type,
return 0;
}
+/*
+ * VAS windows can be closed due to lost credits when the core is
+ * removed. So reopen them if credits are available due to DLPAR
+ * core add and set the window active status. When NX sees the page
+ * fault on the unmapped paste address, the kernel handles the fault
+ * by setting the remapping to new paste address if the window is
+ * active.
+ */
+static int reconfig_open_windows(struct vas_caps *vcaps, int creds)
+{
+ long domain[PLPAR_HCALL9_BUFSIZE] = {VAS_DEFAULT_DOMAIN_ID};
+ struct vas_cop_feat_caps *caps = &vcaps->caps;
+ struct pseries_vas_window *win = NULL;
+ int rc, mv_ents = 0;
+
+ /*
+ * Nothing to do if there are no closed windows.
+ */
+ if (!vcaps->close_wins)
+ return 0;
+
+ /*
+ * For the core removal, the hypervisor reduces the credits
+ * assigned to the LPAR and the kernel closes VAS windows
+ * in the hypervisor depends on reduced credits. The kernel
+ * uses LIFO (the last windows that are opened will be closed
+ * first) and expects to open in the same order when credits
+ * are available.
+ * For example, 40 windows are closed when the LPAR lost 2 cores
+ * (dedicated). If 1 core is added, this LPAR can have 20 more
+ * credits. It means the kernel can reopen 20 windows. So move
+ * 20 entries in the VAS windows lost and reopen next 20 windows.
+ */
+ if (vcaps->close_wins > creds)
+ mv_ents = vcaps->close_wins - creds;
+
+ list_for_each_entry(win, &vcaps->list, win_list) {
+ if (!mv_ents)
+ break;
+
+ mv_ents--;
+ }
+
+ while (vcaps->close_wins && win) {
+ /*
+ * Nothing to do on this window if it is active.
+ */
+ if (win->vas_win.status != VAS_WIN_NO_CRED_CLOSE) {
+ win = list_next_entry(win, win_list);
+ continue;
+ }
+
+ rc = allocate_setup_window(win, (u64 *)&domain[0],
+ caps->win_type);
+ if (rc)
+ return rc;
+
+ rc = h_modify_vas_window(win);
+ if (rc)
+ goto out;
+
+ mutex_lock(&win->vas_win.task_ref.mmap_mutex);
+
+ /*
+ * Set window status to active
+ */
+ win->vas_win.status = VAS_WIN_ACTIVE;
+ win->win_type = caps->win_type;
+ mutex_unlock(&win->vas_win.task_ref.mmap_mutex);
+ atomic_inc(&caps->used_creds);
+ win = list_next_entry(win, win_list);
+ vcaps->close_wins--;
+ }
+
+ return 0;
+out:
+ /*
+ * Window modify HCALL failed. So close the window to the
+ * hypervisor and return.
+ */
+ free_irq_setup(win);
+ h_deallocate_vas_window(win->vas_win.winid);
+ return rc;
+}
+
+/*
+ * Get new VAS capabilities when the core add/removal configuration
+ * changes. Reconfig window configurations based on the credits
+ * availability from this new capabilities.
+ */
+static int vas_reconfig_capabilties(u8 type)
+{
+ int lpar_creds, avail_creds;
+ struct hv_vas_cop_feat_caps *hv_caps;
+ struct vas_cop_feat_caps *caps;
+ struct vas_caps *vcaps;
+ int rc = 0;
+
+ if (type >= VAS_MAX_FEAT_TYPE) {
+ pr_err("Invalid credit type %d\n", type);
+ return -EINVAL;
+ }
+
+ vcaps = &vascaps[type];
+ caps = &vcaps->caps;
+
+ hv_caps = kmalloc(sizeof(*hv_caps), GFP_KERNEL);
+ if (!hv_caps)
+ return -ENOMEM;
+
+ mutex_lock(&vas_pseries_mutex);
+ rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, vcaps->feat,
+ (u64)virt_to_phys(hv_caps));
+ if (rc)
+ goto out;
+
+ lpar_creds = be16_to_cpu(hv_caps->target_lpar_creds);
+
+ atomic_set(&caps->target_creds, lpar_creds);
+
+ /*
+ * The total number of available credits are decreased due to
+ * DLPAR core removal. Means some windows have to be closed.
+ * Hold the vas_pseries_mutex before closing the required windows
+ * so that the user space can not open new windows.
+ */
+ if (atomic_read(&caps->used_creds) < lpar_creds) {
+ /*
+ * If the existing used credits is less than the new
+ * available credits, reopen windows if they are
+ * closed due to the previous DLPAR (core removal).
+ */
+ avail_creds = lpar_creds -
+ atomic_read(&caps->used_creds);
+ rc = reconfig_open_windows(vcaps, avail_creds);
+ }
+
+out:
+ mutex_unlock(&vas_pseries_mutex);
+ kfree(hv_caps);
+ return rc;
+}
+
/*
* Total number of default credits available (target_credits)
* in LPAR depends on number of cores configured. It varies based on@@ -554,6 +698,10 @@ static int pseries_vas_notifier(struct notifier_block *nb,
if (!intserv)
return NOTIFY_OK;
+ rc = vas_reconfig_capabilties(VAS_GZIP_DEF_FEAT_TYPE);
+ if (rc)
+ pr_err("Failed reconfig VAS capabilities with DLPAR\n");
+
return rc;
}
diff --git a/arch/powerpc/platforms/pseries/vas.h b/arch/powerpc/platforms/pseries/vas.h
index 0538760d13be..1fbb49a34d48 100644
--- a/arch/powerpc/platforms/pseries/vas.h
+++ b/arch/powerpc/platforms/pseries/vas.h
@@ -84,6 +84,8 @@ struct vas_cop_feat_caps {
struct vas_caps {
struct vas_cop_feat_caps caps;
struct list_head list; /* List of open windows */
+ int close_wins; /* closed windows in the hypervisor for DLPAR */
+ u8 feat; /* Feature type */
};
/*--
2.27.0