162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2020-21 IBM Corp. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) "vas: " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/irqdomain.h> 1662306a36Sopenharmony_ci#include <asm/machdep.h> 1762306a36Sopenharmony_ci#include <asm/hvcall.h> 1862306a36Sopenharmony_ci#include <asm/plpar_wrappers.h> 1962306a36Sopenharmony_ci#include <asm/firmware.h> 2062306a36Sopenharmony_ci#include <asm/vphn.h> 2162306a36Sopenharmony_ci#include <asm/vas.h> 2262306a36Sopenharmony_ci#include "vas.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define VAS_INVALID_WIN_ADDRESS 0xFFFFFFFFFFFFFFFFul 2562306a36Sopenharmony_ci#define VAS_DEFAULT_DOMAIN_ID 0xFFFFFFFFFFFFFFFFul 2662306a36Sopenharmony_ci/* The hypervisor allows one credit per window right now */ 2762306a36Sopenharmony_ci#define DEF_WIN_CREDS 1 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic struct vas_all_caps caps_all; 3062306a36Sopenharmony_cistatic bool copypaste_feat; 3162306a36Sopenharmony_cistatic struct hv_vas_cop_feat_caps hv_cop_caps; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct vas_caps vascaps[VAS_MAX_FEAT_TYPE]; 3462306a36Sopenharmony_cistatic DEFINE_MUTEX(vas_pseries_mutex); 3562306a36Sopenharmony_cistatic bool migration_in_progress; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic long hcall_return_busy_check(long rc) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci /* Check if we are stalled for some time */ 4062306a36Sopenharmony_ci if (H_IS_LONG_BUSY(rc)) { 4162306a36Sopenharmony_ci msleep(get_longbusy_msecs(rc)); 4262306a36Sopenharmony_ci rc = H_BUSY; 4362306a36Sopenharmony_ci } else if (rc == H_BUSY) { 4462306a36Sopenharmony_ci cond_resched(); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return rc; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * Allocate VAS window hcall 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistatic int h_allocate_vas_window(struct pseries_vas_window *win, u64 *domain, 5462306a36Sopenharmony_ci u8 wintype, u16 credits) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci long retbuf[PLPAR_HCALL9_BUFSIZE] = {0}; 5762306a36Sopenharmony_ci long rc; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci do { 6062306a36Sopenharmony_ci rc = plpar_hcall9(H_ALLOCATE_VAS_WINDOW, retbuf, wintype, 6162306a36Sopenharmony_ci credits, domain[0], domain[1], domain[2], 6262306a36Sopenharmony_ci domain[3], domain[4], domain[5]); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci rc = hcall_return_busy_check(rc); 6562306a36Sopenharmony_ci } while (rc == H_BUSY); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (rc == H_SUCCESS) { 6862306a36Sopenharmony_ci if (win->win_addr == VAS_INVALID_WIN_ADDRESS) { 6962306a36Sopenharmony_ci pr_err("H_ALLOCATE_VAS_WINDOW: COPY/PASTE is not supported\n"); 7062306a36Sopenharmony_ci return -ENOTSUPP; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci win->vas_win.winid = retbuf[0]; 7362306a36Sopenharmony_ci win->win_addr = retbuf[1]; 7462306a36Sopenharmony_ci win->complete_irq = retbuf[2]; 7562306a36Sopenharmony_ci win->fault_irq = retbuf[3]; 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci pr_err("H_ALLOCATE_VAS_WINDOW error: %ld, wintype: %u, credits: %u\n", 8062306a36Sopenharmony_ci rc, wintype, credits); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return -EIO; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * Deallocate VAS window hcall. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic int h_deallocate_vas_window(u64 winid) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci long rc; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci do { 9362306a36Sopenharmony_ci rc = plpar_hcall_norets(H_DEALLOCATE_VAS_WINDOW, winid); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci rc = hcall_return_busy_check(rc); 9662306a36Sopenharmony_ci } while (rc == H_BUSY); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (rc == H_SUCCESS) 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci pr_err("H_DEALLOCATE_VAS_WINDOW error: %ld, winid: %llu\n", 10262306a36Sopenharmony_ci rc, winid); 10362306a36Sopenharmony_ci return -EIO; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * Modify VAS window. 10862306a36Sopenharmony_ci * After the window is opened with allocate window hcall, configure it 10962306a36Sopenharmony_ci * with flags and LPAR PID before using. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistatic int h_modify_vas_window(struct pseries_vas_window *win) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci long rc; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * AMR value is not supported in Linux VAS implementation. 11762306a36Sopenharmony_ci * The hypervisor ignores it if 0 is passed. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci do { 12062306a36Sopenharmony_ci rc = plpar_hcall_norets(H_MODIFY_VAS_WINDOW, 12162306a36Sopenharmony_ci win->vas_win.winid, win->pid, 0, 12262306a36Sopenharmony_ci VAS_MOD_WIN_FLAGS, 0); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci rc = hcall_return_busy_check(rc); 12562306a36Sopenharmony_ci } while (rc == H_BUSY); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (rc == H_SUCCESS) 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci pr_err("H_MODIFY_VAS_WINDOW error: %ld, winid %u pid %u\n", 13162306a36Sopenharmony_ci rc, win->vas_win.winid, win->pid); 13262306a36Sopenharmony_ci return -EIO; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * This hcall is used to determine the capabilities from the hypervisor. 13762306a36Sopenharmony_ci * @hcall: H_QUERY_VAS_CAPABILITIES or H_QUERY_NX_CAPABILITIES 13862306a36Sopenharmony_ci * @query_type: If 0 is passed, the hypervisor returns the overall 13962306a36Sopenharmony_ci * capabilities which provides all feature(s) that are 14062306a36Sopenharmony_ci * available. Then query the hypervisor to get the 14162306a36Sopenharmony_ci * corresponding capabilities for the specific feature. 14262306a36Sopenharmony_ci * Example: H_QUERY_VAS_CAPABILITIES provides VAS GZIP QoS 14362306a36Sopenharmony_ci * and VAS GZIP Default capabilities. 14462306a36Sopenharmony_ci * H_QUERY_NX_CAPABILITIES provides NX GZIP 14562306a36Sopenharmony_ci * capabilities. 14662306a36Sopenharmony_ci * @result: Return buffer to save capabilities. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ciint h_query_vas_capabilities(const u64 hcall, u8 query_type, u64 result) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci long rc; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci rc = plpar_hcall_norets(hcall, query_type, result); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (rc == H_SUCCESS) 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* H_FUNCTION means HV does not support VAS so don't print an error */ 15862306a36Sopenharmony_ci if (rc != H_FUNCTION) { 15962306a36Sopenharmony_ci pr_err("%s error %ld, query_type %u, result buffer 0x%llx\n", 16062306a36Sopenharmony_ci (hcall == H_QUERY_VAS_CAPABILITIES) ? 16162306a36Sopenharmony_ci "H_QUERY_VAS_CAPABILITIES" : 16262306a36Sopenharmony_ci "H_QUERY_NX_CAPABILITIES", 16362306a36Sopenharmony_ci rc, query_type, result); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return -EIO; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(h_query_vas_capabilities); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * hcall to get fault CRB from the hypervisor. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic int h_get_nx_fault(u32 winid, u64 buffer) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci long rc; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci rc = plpar_hcall_norets(H_GET_NX_FAULT, winid, buffer); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (rc == H_SUCCESS) 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci pr_err("H_GET_NX_FAULT error: %ld, winid %u, buffer 0x%llx\n", 18362306a36Sopenharmony_ci rc, winid, buffer); 18462306a36Sopenharmony_ci return -EIO; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* 18962306a36Sopenharmony_ci * Handle the fault interrupt. 19062306a36Sopenharmony_ci * When the fault interrupt is received for each window, query the 19162306a36Sopenharmony_ci * hypervisor to get the fault CRB on the specific fault. Then 19262306a36Sopenharmony_ci * process the CRB by updating CSB or send signal if the user space 19362306a36Sopenharmony_ci * CSB is invalid. 19462306a36Sopenharmony_ci * Note: The hypervisor forwards an interrupt for each fault request. 19562306a36Sopenharmony_ci * So one fault CRB to process for each H_GET_NX_FAULT hcall. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic irqreturn_t pseries_vas_fault_thread_fn(int irq, void *data) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct pseries_vas_window *txwin = data; 20062306a36Sopenharmony_ci struct coprocessor_request_block crb; 20162306a36Sopenharmony_ci struct vas_user_win_ref *tsk_ref; 20262306a36Sopenharmony_ci int rc; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci while (atomic_read(&txwin->pending_faults)) { 20562306a36Sopenharmony_ci rc = h_get_nx_fault(txwin->vas_win.winid, (u64)virt_to_phys(&crb)); 20662306a36Sopenharmony_ci if (!rc) { 20762306a36Sopenharmony_ci tsk_ref = &txwin->vas_win.task_ref; 20862306a36Sopenharmony_ci vas_dump_crb(&crb); 20962306a36Sopenharmony_ci vas_update_csb(&crb, tsk_ref); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci atomic_dec(&txwin->pending_faults); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return IRQ_HANDLED; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* 21862306a36Sopenharmony_ci * irq_default_primary_handler() can be used only with IRQF_ONESHOT 21962306a36Sopenharmony_ci * which disables IRQ before executing the thread handler and enables 22062306a36Sopenharmony_ci * it after. But this disabling interrupt sets the VAS IRQ OFF 22162306a36Sopenharmony_ci * state in the hypervisor. If the NX generates fault interrupt 22262306a36Sopenharmony_ci * during this window, the hypervisor will not deliver this 22362306a36Sopenharmony_ci * interrupt to the LPAR. So use VAS specific IRQ handler instead 22462306a36Sopenharmony_ci * of calling the default primary handler. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_cistatic irqreturn_t pseries_vas_irq_handler(int irq, void *data) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct pseries_vas_window *txwin = data; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* 23162306a36Sopenharmony_ci * The thread hanlder will process this interrupt if it is 23262306a36Sopenharmony_ci * already running. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci atomic_inc(&txwin->pending_faults); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* 24062306a36Sopenharmony_ci * Allocate window and setup IRQ mapping. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistatic int allocate_setup_window(struct pseries_vas_window *txwin, 24362306a36Sopenharmony_ci u64 *domain, u8 wintype) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int rc; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci rc = h_allocate_vas_window(txwin, domain, wintype, DEF_WIN_CREDS); 24862306a36Sopenharmony_ci if (rc) 24962306a36Sopenharmony_ci return rc; 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * On PowerVM, the hypervisor setup and forwards the fault 25262306a36Sopenharmony_ci * interrupt per window. So the IRQ setup and fault handling 25362306a36Sopenharmony_ci * will be done for each open window separately. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci txwin->fault_virq = irq_create_mapping(NULL, txwin->fault_irq); 25662306a36Sopenharmony_ci if (!txwin->fault_virq) { 25762306a36Sopenharmony_ci pr_err("Failed irq mapping %d\n", txwin->fault_irq); 25862306a36Sopenharmony_ci rc = -EINVAL; 25962306a36Sopenharmony_ci goto out_win; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci txwin->name = kasprintf(GFP_KERNEL, "vas-win-%d", 26362306a36Sopenharmony_ci txwin->vas_win.winid); 26462306a36Sopenharmony_ci if (!txwin->name) { 26562306a36Sopenharmony_ci rc = -ENOMEM; 26662306a36Sopenharmony_ci goto out_irq; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci rc = request_threaded_irq(txwin->fault_virq, 27062306a36Sopenharmony_ci pseries_vas_irq_handler, 27162306a36Sopenharmony_ci pseries_vas_fault_thread_fn, 0, 27262306a36Sopenharmony_ci txwin->name, txwin); 27362306a36Sopenharmony_ci if (rc) { 27462306a36Sopenharmony_ci pr_err("VAS-Window[%d]: Request IRQ(%u) failed with %d\n", 27562306a36Sopenharmony_ci txwin->vas_win.winid, txwin->fault_virq, rc); 27662306a36Sopenharmony_ci goto out_free; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci txwin->vas_win.wcreds_max = DEF_WIN_CREDS; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ciout_free: 28362306a36Sopenharmony_ci kfree(txwin->name); 28462306a36Sopenharmony_ciout_irq: 28562306a36Sopenharmony_ci irq_dispose_mapping(txwin->fault_virq); 28662306a36Sopenharmony_ciout_win: 28762306a36Sopenharmony_ci h_deallocate_vas_window(txwin->vas_win.winid); 28862306a36Sopenharmony_ci return rc; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic inline void free_irq_setup(struct pseries_vas_window *txwin) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci free_irq(txwin->fault_virq, txwin); 29462306a36Sopenharmony_ci kfree(txwin->name); 29562306a36Sopenharmony_ci irq_dispose_mapping(txwin->fault_virq); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct vas_window *vas_allocate_window(int vas_id, u64 flags, 29962306a36Sopenharmony_ci enum vas_cop_type cop_type) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci long domain[PLPAR_HCALL9_BUFSIZE] = {VAS_DEFAULT_DOMAIN_ID}; 30262306a36Sopenharmony_ci struct vas_cop_feat_caps *cop_feat_caps; 30362306a36Sopenharmony_ci struct vas_caps *caps; 30462306a36Sopenharmony_ci struct pseries_vas_window *txwin; 30562306a36Sopenharmony_ci int rc; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci txwin = kzalloc(sizeof(*txwin), GFP_KERNEL); 30862306a36Sopenharmony_ci if (!txwin) 30962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* 31262306a36Sopenharmony_ci * A VAS window can have many credits which means that many 31362306a36Sopenharmony_ci * requests can be issued simultaneously. But the hypervisor 31462306a36Sopenharmony_ci * restricts one credit per window. 31562306a36Sopenharmony_ci * The hypervisor introduces 2 different types of credits: 31662306a36Sopenharmony_ci * Default credit type (Uses normal priority FIFO): 31762306a36Sopenharmony_ci * A limited number of credits are assigned to partitions 31862306a36Sopenharmony_ci * based on processor entitlement. But these credits may be 31962306a36Sopenharmony_ci * over-committed on a system depends on whether the CPUs 32062306a36Sopenharmony_ci * are in shared or dedicated modes - that is, more requests 32162306a36Sopenharmony_ci * may be issued across the system than NX can service at 32262306a36Sopenharmony_ci * once which can result in paste command failure (RMA_busy). 32362306a36Sopenharmony_ci * Then the process has to resend requests or fall-back to 32462306a36Sopenharmony_ci * SW compression. 32562306a36Sopenharmony_ci * Quality of Service (QoS) credit type (Uses high priority FIFO): 32662306a36Sopenharmony_ci * To avoid NX HW contention, the system admins can assign 32762306a36Sopenharmony_ci * QoS credits for each LPAR so that this partition is 32862306a36Sopenharmony_ci * guaranteed access to NX resources. These credits are 32962306a36Sopenharmony_ci * assigned to partitions via the HMC. 33062306a36Sopenharmony_ci * Refer PAPR for more information. 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * Allocate window with QoS credits if user requested. Otherwise 33362306a36Sopenharmony_ci * default credits are used. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci if (flags & VAS_TX_WIN_FLAG_QOS_CREDIT) 33662306a36Sopenharmony_ci caps = &vascaps[VAS_GZIP_QOS_FEAT_TYPE]; 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci caps = &vascaps[VAS_GZIP_DEF_FEAT_TYPE]; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci cop_feat_caps = &caps->caps; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (atomic_inc_return(&cop_feat_caps->nr_used_credits) > 34362306a36Sopenharmony_ci atomic_read(&cop_feat_caps->nr_total_credits)) { 34462306a36Sopenharmony_ci pr_err_ratelimited("Credits are not available to allocate window\n"); 34562306a36Sopenharmony_ci rc = -EINVAL; 34662306a36Sopenharmony_ci goto out; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (vas_id == -1) { 35062306a36Sopenharmony_ci /* 35162306a36Sopenharmony_ci * The user space is requesting to allocate a window on 35262306a36Sopenharmony_ci * a VAS instance where the process is executing. 35362306a36Sopenharmony_ci * On PowerVM, domain values are passed to the hypervisor 35462306a36Sopenharmony_ci * to select VAS instance. Useful if the process is 35562306a36Sopenharmony_ci * affinity to NUMA node. 35662306a36Sopenharmony_ci * The hypervisor selects VAS instance if 35762306a36Sopenharmony_ci * VAS_DEFAULT_DOMAIN_ID (-1) is passed for domain values. 35862306a36Sopenharmony_ci * The h_allocate_vas_window hcall is defined to take a 35962306a36Sopenharmony_ci * domain values as specified by h_home_node_associativity, 36062306a36Sopenharmony_ci * So no unpacking needs to be done. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci rc = plpar_hcall9(H_HOME_NODE_ASSOCIATIVITY, domain, 36362306a36Sopenharmony_ci VPHN_FLAG_VCPU, hard_smp_processor_id()); 36462306a36Sopenharmony_ci if (rc != H_SUCCESS) { 36562306a36Sopenharmony_ci pr_err("H_HOME_NODE_ASSOCIATIVITY error: %d\n", rc); 36662306a36Sopenharmony_ci goto out; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci txwin->pid = mfspr(SPRN_PID); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * Allocate / Deallocate window hcalls and setup / free IRQs 37462306a36Sopenharmony_ci * have to be protected with mutex. 37562306a36Sopenharmony_ci * Open VAS window: Allocate window hcall and setup IRQ 37662306a36Sopenharmony_ci * Close VAS window: Deallocate window hcall and free IRQ 37762306a36Sopenharmony_ci * The hypervisor waits until all NX requests are 37862306a36Sopenharmony_ci * completed before closing the window. So expects OS 37962306a36Sopenharmony_ci * to handle NX faults, means IRQ can be freed only 38062306a36Sopenharmony_ci * after the deallocate window hcall is returned. 38162306a36Sopenharmony_ci * So once the window is closed with deallocate hcall before 38262306a36Sopenharmony_ci * the IRQ is freed, it can be assigned to new allocate 38362306a36Sopenharmony_ci * hcall with the same fault IRQ by the hypervisor. It can 38462306a36Sopenharmony_ci * result in setup IRQ fail for the new window since the 38562306a36Sopenharmony_ci * same fault IRQ is not freed by the OS before. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci mutex_lock(&vas_pseries_mutex); 38862306a36Sopenharmony_ci if (migration_in_progress) { 38962306a36Sopenharmony_ci rc = -EBUSY; 39062306a36Sopenharmony_ci } else { 39162306a36Sopenharmony_ci rc = allocate_setup_window(txwin, (u64 *)&domain[0], 39262306a36Sopenharmony_ci cop_feat_caps->win_type); 39362306a36Sopenharmony_ci if (!rc) 39462306a36Sopenharmony_ci caps->nr_open_wins_progress++; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 39862306a36Sopenharmony_ci if (rc) 39962306a36Sopenharmony_ci goto out; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* 40262306a36Sopenharmony_ci * Modify window and it is ready to use. 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_ci rc = h_modify_vas_window(txwin); 40562306a36Sopenharmony_ci if (!rc) 40662306a36Sopenharmony_ci rc = get_vas_user_win_ref(&txwin->vas_win.task_ref); 40762306a36Sopenharmony_ci if (rc) 40862306a36Sopenharmony_ci goto out_free; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci txwin->win_type = cop_feat_caps->win_type; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * The migration SUSPEND thread sets migration_in_progress and 41462306a36Sopenharmony_ci * closes all open windows from the list. But the window is 41562306a36Sopenharmony_ci * added to the list after open and modify HCALLs. So possible 41662306a36Sopenharmony_ci * that migration_in_progress is set before modify HCALL which 41762306a36Sopenharmony_ci * may cause some windows are still open when the hypervisor 41862306a36Sopenharmony_ci * initiates the migration. 41962306a36Sopenharmony_ci * So checks the migration_in_progress flag again and close all 42062306a36Sopenharmony_ci * open windows. 42162306a36Sopenharmony_ci * 42262306a36Sopenharmony_ci * Possible to lose the acquired credit with DLPAR core 42362306a36Sopenharmony_ci * removal after the window is opened. So if there are any 42462306a36Sopenharmony_ci * closed windows (means with lost credits), do not give new 42562306a36Sopenharmony_ci * window to user space. New windows will be opened only 42662306a36Sopenharmony_ci * after the existing windows are reopened when credits are 42762306a36Sopenharmony_ci * available. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci mutex_lock(&vas_pseries_mutex); 43062306a36Sopenharmony_ci if (!caps->nr_close_wins && !migration_in_progress) { 43162306a36Sopenharmony_ci list_add(&txwin->win_list, &caps->list); 43262306a36Sopenharmony_ci caps->nr_open_windows++; 43362306a36Sopenharmony_ci caps->nr_open_wins_progress--; 43462306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 43562306a36Sopenharmony_ci vas_user_win_add_mm_context(&txwin->vas_win.task_ref); 43662306a36Sopenharmony_ci return &txwin->vas_win; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci put_vas_user_win_ref(&txwin->vas_win.task_ref); 44162306a36Sopenharmony_ci rc = -EBUSY; 44262306a36Sopenharmony_ci pr_err_ratelimited("No credit is available to allocate window\n"); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ciout_free: 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Window is not operational. Free IRQ before closing 44762306a36Sopenharmony_ci * window so that do not have to hold mutex. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci free_irq_setup(txwin); 45062306a36Sopenharmony_ci h_deallocate_vas_window(txwin->vas_win.winid); 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * Hold mutex and reduce nr_open_wins_progress counter. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci mutex_lock(&vas_pseries_mutex); 45562306a36Sopenharmony_ci caps->nr_open_wins_progress--; 45662306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 45762306a36Sopenharmony_ciout: 45862306a36Sopenharmony_ci atomic_dec(&cop_feat_caps->nr_used_credits); 45962306a36Sopenharmony_ci kfree(txwin); 46062306a36Sopenharmony_ci return ERR_PTR(rc); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic u64 vas_paste_address(struct vas_window *vwin) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct pseries_vas_window *win; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci win = container_of(vwin, struct pseries_vas_window, vas_win); 46862306a36Sopenharmony_ci return win->win_addr; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int deallocate_free_window(struct pseries_vas_window *win) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int rc = 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * The hypervisor waits for all requests including faults 47762306a36Sopenharmony_ci * are processed before closing the window - Means all 47862306a36Sopenharmony_ci * credits have to be returned. In the case of fault 47962306a36Sopenharmony_ci * request, a credit is returned after OS issues 48062306a36Sopenharmony_ci * H_GET_NX_FAULT hcall. 48162306a36Sopenharmony_ci * So free IRQ after executing H_DEALLOCATE_VAS_WINDOW 48262306a36Sopenharmony_ci * hcall. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci rc = h_deallocate_vas_window(win->vas_win.winid); 48562306a36Sopenharmony_ci if (!rc) 48662306a36Sopenharmony_ci free_irq_setup(win); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return rc; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int vas_deallocate_window(struct vas_window *vwin) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct pseries_vas_window *win; 49462306a36Sopenharmony_ci struct vas_cop_feat_caps *caps; 49562306a36Sopenharmony_ci int rc = 0; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (!vwin) 49862306a36Sopenharmony_ci return -EINVAL; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci win = container_of(vwin, struct pseries_vas_window, vas_win); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* Should not happen */ 50362306a36Sopenharmony_ci if (win->win_type >= VAS_MAX_FEAT_TYPE) { 50462306a36Sopenharmony_ci pr_err("Window (%u): Invalid window type %u\n", 50562306a36Sopenharmony_ci vwin->winid, win->win_type); 50662306a36Sopenharmony_ci return -EINVAL; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci caps = &vascaps[win->win_type].caps; 51062306a36Sopenharmony_ci mutex_lock(&vas_pseries_mutex); 51162306a36Sopenharmony_ci /* 51262306a36Sopenharmony_ci * VAS window is already closed in the hypervisor when 51362306a36Sopenharmony_ci * lost the credit or with migration. So just remove the entry 51462306a36Sopenharmony_ci * from the list, remove task references and free vas_window 51562306a36Sopenharmony_ci * struct. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci if (!(win->vas_win.status & VAS_WIN_NO_CRED_CLOSE) && 51862306a36Sopenharmony_ci !(win->vas_win.status & VAS_WIN_MIGRATE_CLOSE)) { 51962306a36Sopenharmony_ci rc = deallocate_free_window(win); 52062306a36Sopenharmony_ci if (rc) { 52162306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 52262306a36Sopenharmony_ci return rc; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci } else 52562306a36Sopenharmony_ci vascaps[win->win_type].nr_close_wins--; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci list_del(&win->win_list); 52862306a36Sopenharmony_ci atomic_dec(&caps->nr_used_credits); 52962306a36Sopenharmony_ci vascaps[win->win_type].nr_open_windows--; 53062306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci mm_context_remove_vas_window(vwin->task_ref.mm); 53362306a36Sopenharmony_ci put_vas_user_win_ref(&vwin->task_ref); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci kfree(win); 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic const struct vas_user_win_ops vops_pseries = { 54062306a36Sopenharmony_ci .open_win = vas_allocate_window, /* Open and configure window */ 54162306a36Sopenharmony_ci .paste_addr = vas_paste_address, /* To do copy/paste */ 54262306a36Sopenharmony_ci .close_win = vas_deallocate_window, /* Close window */ 54362306a36Sopenharmony_ci}; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/* 54662306a36Sopenharmony_ci * Supporting only nx-gzip coprocessor type now, but this API code 54762306a36Sopenharmony_ci * extended to other coprocessor types later. 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ciint vas_register_api_pseries(struct module *mod, enum vas_cop_type cop_type, 55062306a36Sopenharmony_ci const char *name) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci if (!copypaste_feat) 55362306a36Sopenharmony_ci return -ENOTSUPP; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return vas_register_coproc_api(mod, cop_type, name, &vops_pseries); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vas_register_api_pseries); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_civoid vas_unregister_api_pseries(void) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci vas_unregister_coproc_api(); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vas_unregister_api_pseries); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/* 56662306a36Sopenharmony_ci * Get the specific capabilities based on the feature type. 56762306a36Sopenharmony_ci * Right now supports GZIP default and GZIP QoS capabilities. 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_cistatic int __init get_vas_capabilities(u8 feat, enum vas_cop_feat_type type, 57062306a36Sopenharmony_ci struct hv_vas_cop_feat_caps *hv_caps) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct vas_cop_feat_caps *caps; 57362306a36Sopenharmony_ci struct vas_caps *vcaps; 57462306a36Sopenharmony_ci int rc = 0; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci vcaps = &vascaps[type]; 57762306a36Sopenharmony_ci memset(vcaps, 0, sizeof(*vcaps)); 57862306a36Sopenharmony_ci INIT_LIST_HEAD(&vcaps->list); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci vcaps->feat = feat; 58162306a36Sopenharmony_ci caps = &vcaps->caps; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, feat, 58462306a36Sopenharmony_ci (u64)virt_to_phys(hv_caps)); 58562306a36Sopenharmony_ci if (rc) 58662306a36Sopenharmony_ci return rc; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci caps->user_mode = hv_caps->user_mode; 58962306a36Sopenharmony_ci if (!(caps->user_mode & VAS_COPY_PASTE_USER_MODE)) { 59062306a36Sopenharmony_ci pr_err("User space COPY/PASTE is not supported\n"); 59162306a36Sopenharmony_ci return -ENOTSUPP; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci caps->descriptor = be64_to_cpu(hv_caps->descriptor); 59562306a36Sopenharmony_ci caps->win_type = hv_caps->win_type; 59662306a36Sopenharmony_ci if (caps->win_type >= VAS_MAX_FEAT_TYPE) { 59762306a36Sopenharmony_ci pr_err("Unsupported window type %u\n", caps->win_type); 59862306a36Sopenharmony_ci return -EINVAL; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci caps->max_lpar_creds = be16_to_cpu(hv_caps->max_lpar_creds); 60162306a36Sopenharmony_ci caps->max_win_creds = be16_to_cpu(hv_caps->max_win_creds); 60262306a36Sopenharmony_ci atomic_set(&caps->nr_total_credits, 60362306a36Sopenharmony_ci be16_to_cpu(hv_caps->target_lpar_creds)); 60462306a36Sopenharmony_ci if (feat == VAS_GZIP_DEF_FEAT) { 60562306a36Sopenharmony_ci caps->def_lpar_creds = be16_to_cpu(hv_caps->def_lpar_creds); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (caps->max_win_creds < DEF_WIN_CREDS) { 60862306a36Sopenharmony_ci pr_err("Window creds(%u) > max allowed window creds(%u)\n", 60962306a36Sopenharmony_ci DEF_WIN_CREDS, caps->max_win_creds); 61062306a36Sopenharmony_ci return -EINVAL; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci rc = sysfs_add_vas_caps(caps); 61562306a36Sopenharmony_ci if (rc) 61662306a36Sopenharmony_ci return rc; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci copypaste_feat = true; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* 62462306a36Sopenharmony_ci * VAS windows can be closed due to lost credits when the core is 62562306a36Sopenharmony_ci * removed. So reopen them if credits are available due to DLPAR 62662306a36Sopenharmony_ci * core add and set the window active status. When NX sees the page 62762306a36Sopenharmony_ci * fault on the unmapped paste address, the kernel handles the fault 62862306a36Sopenharmony_ci * by setting the remapping to new paste address if the window is 62962306a36Sopenharmony_ci * active. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_cistatic int reconfig_open_windows(struct vas_caps *vcaps, int creds, 63262306a36Sopenharmony_ci bool migrate) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci long domain[PLPAR_HCALL9_BUFSIZE] = {VAS_DEFAULT_DOMAIN_ID}; 63562306a36Sopenharmony_ci struct vas_cop_feat_caps *caps = &vcaps->caps; 63662306a36Sopenharmony_ci struct pseries_vas_window *win = NULL, *tmp; 63762306a36Sopenharmony_ci int rc, mv_ents = 0; 63862306a36Sopenharmony_ci int flag; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* 64162306a36Sopenharmony_ci * Nothing to do if there are no closed windows. 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_ci if (!vcaps->nr_close_wins) 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * For the core removal, the hypervisor reduces the credits 64862306a36Sopenharmony_ci * assigned to the LPAR and the kernel closes VAS windows 64962306a36Sopenharmony_ci * in the hypervisor depends on reduced credits. The kernel 65062306a36Sopenharmony_ci * uses LIFO (the last windows that are opened will be closed 65162306a36Sopenharmony_ci * first) and expects to open in the same order when credits 65262306a36Sopenharmony_ci * are available. 65362306a36Sopenharmony_ci * For example, 40 windows are closed when the LPAR lost 2 cores 65462306a36Sopenharmony_ci * (dedicated). If 1 core is added, this LPAR can have 20 more 65562306a36Sopenharmony_ci * credits. It means the kernel can reopen 20 windows. So move 65662306a36Sopenharmony_ci * 20 entries in the VAS windows lost and reopen next 20 windows. 65762306a36Sopenharmony_ci * For partition migration, reopen all windows that are closed 65862306a36Sopenharmony_ci * during resume. 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci if ((vcaps->nr_close_wins > creds) && !migrate) 66162306a36Sopenharmony_ci mv_ents = vcaps->nr_close_wins - creds; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci list_for_each_entry_safe(win, tmp, &vcaps->list, win_list) { 66462306a36Sopenharmony_ci if (!mv_ents) 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci mv_ents--; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * Open windows if they are closed only with migration or 67262306a36Sopenharmony_ci * DLPAR (lost credit) before. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ci if (migrate) 67562306a36Sopenharmony_ci flag = VAS_WIN_MIGRATE_CLOSE; 67662306a36Sopenharmony_ci else 67762306a36Sopenharmony_ci flag = VAS_WIN_NO_CRED_CLOSE; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci list_for_each_entry_safe_from(win, tmp, &vcaps->list, win_list) { 68062306a36Sopenharmony_ci /* 68162306a36Sopenharmony_ci * This window is closed with DLPAR and migration events. 68262306a36Sopenharmony_ci * So reopen the window with the last event. 68362306a36Sopenharmony_ci * The user space is not suspended with the current 68462306a36Sopenharmony_ci * migration notifier. So the user space can issue DLPAR 68562306a36Sopenharmony_ci * CPU hotplug while migration in progress. In this case 68662306a36Sopenharmony_ci * this window will be opened with the last event. 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ci if ((win->vas_win.status & VAS_WIN_NO_CRED_CLOSE) && 68962306a36Sopenharmony_ci (win->vas_win.status & VAS_WIN_MIGRATE_CLOSE)) { 69062306a36Sopenharmony_ci win->vas_win.status &= ~flag; 69162306a36Sopenharmony_ci continue; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* 69562306a36Sopenharmony_ci * Nothing to do on this window if it is not closed 69662306a36Sopenharmony_ci * with this flag 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci if (!(win->vas_win.status & flag)) 69962306a36Sopenharmony_ci continue; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci rc = allocate_setup_window(win, (u64 *)&domain[0], 70262306a36Sopenharmony_ci caps->win_type); 70362306a36Sopenharmony_ci if (rc) 70462306a36Sopenharmony_ci return rc; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci rc = h_modify_vas_window(win); 70762306a36Sopenharmony_ci if (rc) 70862306a36Sopenharmony_ci goto out; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci mutex_lock(&win->vas_win.task_ref.mmap_mutex); 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * Set window status to active 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_ci win->vas_win.status &= ~flag; 71562306a36Sopenharmony_ci mutex_unlock(&win->vas_win.task_ref.mmap_mutex); 71662306a36Sopenharmony_ci win->win_type = caps->win_type; 71762306a36Sopenharmony_ci if (!--vcaps->nr_close_wins) 71862306a36Sopenharmony_ci break; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ciout: 72362306a36Sopenharmony_ci /* 72462306a36Sopenharmony_ci * Window modify HCALL failed. So close the window to the 72562306a36Sopenharmony_ci * hypervisor and return. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_ci free_irq_setup(win); 72862306a36Sopenharmony_ci h_deallocate_vas_window(win->vas_win.winid); 72962306a36Sopenharmony_ci return rc; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci/* 73362306a36Sopenharmony_ci * The hypervisor reduces the available credits if the LPAR lost core. It 73462306a36Sopenharmony_ci * means the excessive windows should not be active and the user space 73562306a36Sopenharmony_ci * should not be using these windows to send compression requests to NX. 73662306a36Sopenharmony_ci * So the kernel closes the excessive windows and unmap the paste address 73762306a36Sopenharmony_ci * such that the user space receives paste instruction failure. Then up to 73862306a36Sopenharmony_ci * the user space to fall back to SW compression and manage with the 73962306a36Sopenharmony_ci * existing windows. 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_cistatic int reconfig_close_windows(struct vas_caps *vcap, int excess_creds, 74262306a36Sopenharmony_ci bool migrate) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct pseries_vas_window *win, *tmp; 74562306a36Sopenharmony_ci struct vas_user_win_ref *task_ref; 74662306a36Sopenharmony_ci struct vm_area_struct *vma; 74762306a36Sopenharmony_ci int rc = 0, flag; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (migrate) 75062306a36Sopenharmony_ci flag = VAS_WIN_MIGRATE_CLOSE; 75162306a36Sopenharmony_ci else 75262306a36Sopenharmony_ci flag = VAS_WIN_NO_CRED_CLOSE; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci list_for_each_entry_safe(win, tmp, &vcap->list, win_list) { 75562306a36Sopenharmony_ci /* 75662306a36Sopenharmony_ci * This window is already closed due to lost credit 75762306a36Sopenharmony_ci * or for migration before. Go for next window. 75862306a36Sopenharmony_ci * For migration, nothing to do since this window 75962306a36Sopenharmony_ci * closed for DLPAR and will be reopened even on 76062306a36Sopenharmony_ci * the destination system with other DLPAR operation. 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci if ((win->vas_win.status & VAS_WIN_MIGRATE_CLOSE) || 76362306a36Sopenharmony_ci (win->vas_win.status & VAS_WIN_NO_CRED_CLOSE)) { 76462306a36Sopenharmony_ci win->vas_win.status |= flag; 76562306a36Sopenharmony_ci continue; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci task_ref = &win->vas_win.task_ref; 76962306a36Sopenharmony_ci /* 77062306a36Sopenharmony_ci * VAS mmap (coproc_mmap()) and its fault handler 77162306a36Sopenharmony_ci * (vas_mmap_fault()) are called after holding mmap lock. 77262306a36Sopenharmony_ci * So hold mmap mutex after mmap_lock to avoid deadlock. 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_ci mmap_write_lock(task_ref->mm); 77562306a36Sopenharmony_ci mutex_lock(&task_ref->mmap_mutex); 77662306a36Sopenharmony_ci vma = task_ref->vma; 77762306a36Sopenharmony_ci /* 77862306a36Sopenharmony_ci * Number of available credits are reduced, So select 77962306a36Sopenharmony_ci * and close windows. 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ci win->vas_win.status |= flag; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* 78462306a36Sopenharmony_ci * vma is set in the original mapping. But this mapping 78562306a36Sopenharmony_ci * is done with mmap() after the window is opened with ioctl. 78662306a36Sopenharmony_ci * so we may not see the original mapping if the core remove 78762306a36Sopenharmony_ci * is done before the original mmap() and after the ioctl. 78862306a36Sopenharmony_ci */ 78962306a36Sopenharmony_ci if (vma) 79062306a36Sopenharmony_ci zap_vma_pages(vma); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci mutex_unlock(&task_ref->mmap_mutex); 79362306a36Sopenharmony_ci mmap_write_unlock(task_ref->mm); 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * Close VAS window in the hypervisor, but do not 79662306a36Sopenharmony_ci * free vas_window struct since it may be reused 79762306a36Sopenharmony_ci * when the credit is available later (DLPAR with 79862306a36Sopenharmony_ci * adding cores). This struct will be used 79962306a36Sopenharmony_ci * later when the process issued with close(FD). 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_ci rc = deallocate_free_window(win); 80262306a36Sopenharmony_ci /* 80362306a36Sopenharmony_ci * This failure is from the hypervisor. 80462306a36Sopenharmony_ci * No way to stop migration for these failures. 80562306a36Sopenharmony_ci * So ignore error and continue closing other windows. 80662306a36Sopenharmony_ci */ 80762306a36Sopenharmony_ci if (rc && !migrate) 80862306a36Sopenharmony_ci return rc; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci vcap->nr_close_wins++; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* 81362306a36Sopenharmony_ci * For migration, do not depend on lpar_creds in case if 81462306a36Sopenharmony_ci * mismatch with the hypervisor value (should not happen). 81562306a36Sopenharmony_ci * So close all active windows in the list and will be 81662306a36Sopenharmony_ci * reopened windows based on the new lpar_creds on the 81762306a36Sopenharmony_ci * destination system during resume. 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_ci if (!migrate && !--excess_creds) 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/* 82762306a36Sopenharmony_ci * Get new VAS capabilities when the core add/removal configuration 82862306a36Sopenharmony_ci * changes. Reconfig window configurations based on the credits 82962306a36Sopenharmony_ci * availability from this new capabilities. 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ciint vas_reconfig_capabilties(u8 type, int new_nr_creds) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci struct vas_cop_feat_caps *caps; 83462306a36Sopenharmony_ci int old_nr_creds; 83562306a36Sopenharmony_ci struct vas_caps *vcaps; 83662306a36Sopenharmony_ci int rc = 0, nr_active_wins; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (type >= VAS_MAX_FEAT_TYPE) { 83962306a36Sopenharmony_ci pr_err("Invalid credit type %d\n", type); 84062306a36Sopenharmony_ci return -EINVAL; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci vcaps = &vascaps[type]; 84462306a36Sopenharmony_ci caps = &vcaps->caps; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci mutex_lock(&vas_pseries_mutex); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci old_nr_creds = atomic_read(&caps->nr_total_credits); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci atomic_set(&caps->nr_total_credits, new_nr_creds); 85162306a36Sopenharmony_ci /* 85262306a36Sopenharmony_ci * The total number of available credits may be decreased or 85362306a36Sopenharmony_ci * increased with DLPAR operation. Means some windows have to be 85462306a36Sopenharmony_ci * closed / reopened. Hold the vas_pseries_mutex so that the 85562306a36Sopenharmony_ci * user space can not open new windows. 85662306a36Sopenharmony_ci */ 85762306a36Sopenharmony_ci if (old_nr_creds < new_nr_creds) { 85862306a36Sopenharmony_ci /* 85962306a36Sopenharmony_ci * If the existing target credits is less than the new 86062306a36Sopenharmony_ci * target, reopen windows if they are closed due to 86162306a36Sopenharmony_ci * the previous DLPAR (core removal). 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci rc = reconfig_open_windows(vcaps, new_nr_creds - old_nr_creds, 86462306a36Sopenharmony_ci false); 86562306a36Sopenharmony_ci } else { 86662306a36Sopenharmony_ci /* 86762306a36Sopenharmony_ci * # active windows is more than new LPAR available 86862306a36Sopenharmony_ci * credits. So close the excessive windows. 86962306a36Sopenharmony_ci * On pseries, each window will have 1 credit. 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_ci nr_active_wins = vcaps->nr_open_windows - vcaps->nr_close_wins; 87262306a36Sopenharmony_ci if (nr_active_wins > new_nr_creds) 87362306a36Sopenharmony_ci rc = reconfig_close_windows(vcaps, 87462306a36Sopenharmony_ci nr_active_wins - new_nr_creds, 87562306a36Sopenharmony_ci false); 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 87962306a36Sopenharmony_ci return rc; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ciint pseries_vas_dlpar_cpu(void) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci int new_nr_creds, rc; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* 88762306a36Sopenharmony_ci * NX-GZIP is not enabled. Nothing to do for DLPAR event 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_ci if (!copypaste_feat) 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, 89462306a36Sopenharmony_ci vascaps[VAS_GZIP_DEF_FEAT_TYPE].feat, 89562306a36Sopenharmony_ci (u64)virt_to_phys(&hv_cop_caps)); 89662306a36Sopenharmony_ci if (!rc) { 89762306a36Sopenharmony_ci new_nr_creds = be16_to_cpu(hv_cop_caps.target_lpar_creds); 89862306a36Sopenharmony_ci rc = vas_reconfig_capabilties(VAS_GZIP_DEF_FEAT_TYPE, new_nr_creds); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (rc) 90262306a36Sopenharmony_ci pr_err("Failed reconfig VAS capabilities with DLPAR\n"); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci return rc; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci/* 90862306a36Sopenharmony_ci * Total number of default credits available (target_credits) 90962306a36Sopenharmony_ci * in LPAR depends on number of cores configured. It varies based on 91062306a36Sopenharmony_ci * whether processors are in shared mode or dedicated mode. 91162306a36Sopenharmony_ci * Get the notifier when CPU configuration is changed with DLPAR 91262306a36Sopenharmony_ci * operation so that get the new target_credits (vas default capabilities) 91362306a36Sopenharmony_ci * and then update the existing windows usage if needed. 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_cistatic int pseries_vas_notifier(struct notifier_block *nb, 91662306a36Sopenharmony_ci unsigned long action, void *data) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci struct of_reconfig_data *rd = data; 91962306a36Sopenharmony_ci struct device_node *dn = rd->dn; 92062306a36Sopenharmony_ci const __be32 *intserv = NULL; 92162306a36Sopenharmony_ci int len; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * For shared CPU partition, the hypervisor assigns total credits 92562306a36Sopenharmony_ci * based on entitled core capacity. So updating VAS windows will 92662306a36Sopenharmony_ci * be called from lparcfg_write(). 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci if (is_shared_processor()) 92962306a36Sopenharmony_ci return NOTIFY_OK; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if ((action == OF_RECONFIG_ATTACH_NODE) || 93262306a36Sopenharmony_ci (action == OF_RECONFIG_DETACH_NODE)) 93362306a36Sopenharmony_ci intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", 93462306a36Sopenharmony_ci &len); 93562306a36Sopenharmony_ci /* 93662306a36Sopenharmony_ci * Processor config is not changed 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci if (!intserv) 93962306a36Sopenharmony_ci return NOTIFY_OK; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci return pseries_vas_dlpar_cpu(); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic struct notifier_block pseries_vas_nb = { 94562306a36Sopenharmony_ci .notifier_call = pseries_vas_notifier, 94662306a36Sopenharmony_ci}; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/* 94962306a36Sopenharmony_ci * For LPM, all windows have to be closed on the source partition 95062306a36Sopenharmony_ci * before migration and reopen them on the destination partition 95162306a36Sopenharmony_ci * after migration. So closing windows during suspend and 95262306a36Sopenharmony_ci * reopen them during resume. 95362306a36Sopenharmony_ci */ 95462306a36Sopenharmony_ciint vas_migration_handler(int action) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct vas_cop_feat_caps *caps; 95762306a36Sopenharmony_ci int old_nr_creds, new_nr_creds = 0; 95862306a36Sopenharmony_ci struct vas_caps *vcaps; 95962306a36Sopenharmony_ci int i, rc = 0; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci pr_info("VAS migration event %d\n", action); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* 96462306a36Sopenharmony_ci * NX-GZIP is not enabled. Nothing to do for migration. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_ci if (!copypaste_feat) 96762306a36Sopenharmony_ci return rc; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (action == VAS_SUSPEND) 97062306a36Sopenharmony_ci migration_in_progress = true; 97162306a36Sopenharmony_ci else 97262306a36Sopenharmony_ci migration_in_progress = false; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci for (i = 0; i < VAS_MAX_FEAT_TYPE; i++) { 97562306a36Sopenharmony_ci vcaps = &vascaps[i]; 97662306a36Sopenharmony_ci caps = &vcaps->caps; 97762306a36Sopenharmony_ci old_nr_creds = atomic_read(&caps->nr_total_credits); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, 98062306a36Sopenharmony_ci vcaps->feat, 98162306a36Sopenharmony_ci (u64)virt_to_phys(&hv_cop_caps)); 98262306a36Sopenharmony_ci if (!rc) { 98362306a36Sopenharmony_ci new_nr_creds = be16_to_cpu(hv_cop_caps.target_lpar_creds); 98462306a36Sopenharmony_ci /* 98562306a36Sopenharmony_ci * Should not happen. But incase print messages, close 98662306a36Sopenharmony_ci * all windows in the list during suspend and reopen 98762306a36Sopenharmony_ci * windows based on new lpar_creds on the destination 98862306a36Sopenharmony_ci * system. 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_ci if (old_nr_creds != new_nr_creds) { 99162306a36Sopenharmony_ci pr_err("Target credits mismatch with the hypervisor\n"); 99262306a36Sopenharmony_ci pr_err("state(%d): lpar creds: %d HV lpar creds: %d\n", 99362306a36Sopenharmony_ci action, old_nr_creds, new_nr_creds); 99462306a36Sopenharmony_ci pr_err("Used creds: %d, Active creds: %d\n", 99562306a36Sopenharmony_ci atomic_read(&caps->nr_used_credits), 99662306a36Sopenharmony_ci vcaps->nr_open_windows - vcaps->nr_close_wins); 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci } else { 99962306a36Sopenharmony_ci pr_err("state(%d): Get VAS capabilities failed with %d\n", 100062306a36Sopenharmony_ci action, rc); 100162306a36Sopenharmony_ci /* 100262306a36Sopenharmony_ci * We can not stop migration with the current lpm 100362306a36Sopenharmony_ci * implementation. So continue closing all windows in 100462306a36Sopenharmony_ci * the list (during suspend) and return without 100562306a36Sopenharmony_ci * opening windows (during resume) if VAS capabilities 100662306a36Sopenharmony_ci * HCALL failed. 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_ci if (action == VAS_RESUME) 100962306a36Sopenharmony_ci goto out; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci switch (action) { 101362306a36Sopenharmony_ci case VAS_SUSPEND: 101462306a36Sopenharmony_ci mutex_lock(&vas_pseries_mutex); 101562306a36Sopenharmony_ci rc = reconfig_close_windows(vcaps, vcaps->nr_open_windows, 101662306a36Sopenharmony_ci true); 101762306a36Sopenharmony_ci /* 101862306a36Sopenharmony_ci * Windows are included in the list after successful 101962306a36Sopenharmony_ci * open. So wait for closing these in-progress open 102062306a36Sopenharmony_ci * windows in vas_allocate_window() which will be 102162306a36Sopenharmony_ci * done if the migration_in_progress is set. 102262306a36Sopenharmony_ci */ 102362306a36Sopenharmony_ci while (vcaps->nr_open_wins_progress) { 102462306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 102562306a36Sopenharmony_ci msleep(10); 102662306a36Sopenharmony_ci mutex_lock(&vas_pseries_mutex); 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 102962306a36Sopenharmony_ci break; 103062306a36Sopenharmony_ci case VAS_RESUME: 103162306a36Sopenharmony_ci mutex_lock(&vas_pseries_mutex); 103262306a36Sopenharmony_ci atomic_set(&caps->nr_total_credits, new_nr_creds); 103362306a36Sopenharmony_ci rc = reconfig_open_windows(vcaps, new_nr_creds, true); 103462306a36Sopenharmony_ci mutex_unlock(&vas_pseries_mutex); 103562306a36Sopenharmony_ci break; 103662306a36Sopenharmony_ci default: 103762306a36Sopenharmony_ci /* should not happen */ 103862306a36Sopenharmony_ci pr_err("Invalid migration action %d\n", action); 103962306a36Sopenharmony_ci rc = -EINVAL; 104062306a36Sopenharmony_ci goto out; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci /* 104462306a36Sopenharmony_ci * Ignore errors during suspend and return for resume. 104562306a36Sopenharmony_ci */ 104662306a36Sopenharmony_ci if (rc && (action == VAS_RESUME)) 104762306a36Sopenharmony_ci goto out; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci pr_info("VAS migration event (%d) successful\n", action); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ciout: 105362306a36Sopenharmony_ci return rc; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic int __init pseries_vas_init(void) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci struct hv_vas_all_caps *hv_caps; 105962306a36Sopenharmony_ci int rc = 0; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* 106262306a36Sopenharmony_ci * Linux supports user space COPY/PASTE only with Radix 106362306a36Sopenharmony_ci */ 106462306a36Sopenharmony_ci if (!radix_enabled()) { 106562306a36Sopenharmony_ci copypaste_feat = false; 106662306a36Sopenharmony_ci pr_err("API is supported only with radix page tables\n"); 106762306a36Sopenharmony_ci return -ENOTSUPP; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci hv_caps = kmalloc(sizeof(*hv_caps), GFP_KERNEL); 107162306a36Sopenharmony_ci if (!hv_caps) 107262306a36Sopenharmony_ci return -ENOMEM; 107362306a36Sopenharmony_ci /* 107462306a36Sopenharmony_ci * Get VAS overall capabilities by passing 0 to feature type. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, 0, 107762306a36Sopenharmony_ci (u64)virt_to_phys(hv_caps)); 107862306a36Sopenharmony_ci if (rc) 107962306a36Sopenharmony_ci goto out; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci caps_all.descriptor = be64_to_cpu(hv_caps->descriptor); 108262306a36Sopenharmony_ci caps_all.feat_type = be64_to_cpu(hv_caps->feat_type); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci sysfs_pseries_vas_init(&caps_all); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* 108762306a36Sopenharmony_ci * QOS capabilities available 108862306a36Sopenharmony_ci */ 108962306a36Sopenharmony_ci if (caps_all.feat_type & VAS_GZIP_QOS_FEAT_BIT) { 109062306a36Sopenharmony_ci rc = get_vas_capabilities(VAS_GZIP_QOS_FEAT, 109162306a36Sopenharmony_ci VAS_GZIP_QOS_FEAT_TYPE, &hv_cop_caps); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (rc) 109462306a36Sopenharmony_ci goto out; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci /* 109762306a36Sopenharmony_ci * Default capabilities available 109862306a36Sopenharmony_ci */ 109962306a36Sopenharmony_ci if (caps_all.feat_type & VAS_GZIP_DEF_FEAT_BIT) 110062306a36Sopenharmony_ci rc = get_vas_capabilities(VAS_GZIP_DEF_FEAT, 110162306a36Sopenharmony_ci VAS_GZIP_DEF_FEAT_TYPE, &hv_cop_caps); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (!rc && copypaste_feat) { 110462306a36Sopenharmony_ci if (firmware_has_feature(FW_FEATURE_LPAR)) 110562306a36Sopenharmony_ci of_reconfig_notifier_register(&pseries_vas_nb); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci pr_info("GZIP feature is available\n"); 110862306a36Sopenharmony_ci } else { 110962306a36Sopenharmony_ci /* 111062306a36Sopenharmony_ci * Should not happen, but only when get default 111162306a36Sopenharmony_ci * capabilities HCALL failed. So disable copy paste 111262306a36Sopenharmony_ci * feature. 111362306a36Sopenharmony_ci */ 111462306a36Sopenharmony_ci copypaste_feat = false; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ciout: 111862306a36Sopenharmony_ci kfree(hv_caps); 111962306a36Sopenharmony_ci return rc; 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_cimachine_device_initcall(pseries, pseries_vas_init); 1122