2018-01-23 12:31:41 +01:00
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2017 IBM Corp.
# include <linux/sched/mm.h>
# include <linux/mutex.h>
2020-11-25 16:50:12 +01:00
# include <linux/mm.h>
2018-08-17 15:44:47 -07:00
# include <linux/mm_types.h>
2018-01-23 12:31:41 +01:00
# include <linux/mmu_context.h>
2020-11-25 16:50:12 +01:00
# include <linux/mmu_notifier.h>
2018-01-23 12:31:41 +01:00
# include <asm/copro.h>
# include <asm/pnv-ocxl.h>
2020-04-03 17:38:37 +02:00
# include <asm/xive.h>
2018-01-23 12:31:43 +01:00
# include <misc/ocxl.h>
2018-01-23 12:31:41 +01:00
# include "ocxl_internal.h"
2018-01-23 12:31:44 +01:00
# include "trace.h"
2018-01-23 12:31:41 +01:00
# define SPA_PASID_BITS 15
# define SPA_PASID_MAX ((1 << SPA_PASID_BITS) - 1)
# define SPA_PE_MASK SPA_PASID_MAX
# define SPA_SPA_SIZE_LOG 22 /* Each SPA is 4 Mb */
# define SPA_CFG_SF (1ull << (63-0))
# define SPA_CFG_TA (1ull << (63-1))
# define SPA_CFG_HV (1ull << (63-3))
# define SPA_CFG_UV (1ull << (63-4))
# define SPA_CFG_XLAT_hpt (0ull << (63-6)) /* Hashed page table (HPT) mode */
# define SPA_CFG_XLAT_roh (2ull << (63-6)) /* Radix on HPT mode */
# define SPA_CFG_XLAT_ror (3ull << (63-6)) /* Radix on Radix mode */
# define SPA_CFG_PR (1ull << (63-49))
# define SPA_CFG_TC (1ull << (63-54))
# define SPA_CFG_DR (1ull << (63-59))
# define SPA_XSL_TF (1ull << (63-3)) /* Translation fault */
# define SPA_XSL_S (1ull << (63-38)) /* Store operation */
# define SPA_PE_VALID 0x80000000
2020-11-25 16:50:12 +01:00
struct ocxl_link ;
2018-01-23 12:31:41 +01:00
struct pe_data {
struct mm_struct * mm ;
/* callback to trigger when a translation fault occurs */
void ( * xsl_err_cb ) ( void * data , u64 addr , u64 dsisr ) ;
/* opaque pointer to be passed to the above callback */
void * xsl_err_data ;
struct rcu_head rcu ;
2020-11-25 16:50:12 +01:00
struct ocxl_link * link ;
struct mmu_notifier mmu_notifier ;
2018-01-23 12:31:41 +01:00
} ;
struct spa {
struct ocxl_process_element * spa_mem ;
int spa_order ;
struct mutex spa_lock ;
struct radix_tree_root pe_tree ; /* Maps PE handles to pe_data */
char * irq_name ;
int virq ;
void __iomem * reg_dsisr ;
void __iomem * reg_dar ;
void __iomem * reg_tfc ;
void __iomem * reg_pe_handle ;
/*
* The following field are used by the memory fault
* interrupt handler . We can only have one interrupt at a
* time . The NPU won ' t raise another interrupt until the
* previous one has been ack ' d by writing to the TFC register
*/
struct xsl_fault {
struct work_struct fault_work ;
u64 pe ;
u64 dsisr ;
u64 dar ;
struct pe_data pe_data ;
} xsl_fault ;
} ;
/*
* A opencapi link can be used be by several PCI functions . We have
* one link per device slot .
*
* A linked list of opencapi links should suffice , as there ' s a
* limited number of opencapi slots on a system and lookup is only
* done when the device is probed
*/
2019-03-25 16:34:52 +11:00
struct ocxl_link {
2018-01-23 12:31:41 +01:00
struct list_head list ;
struct kref ref ;
int domain ;
int bus ;
int dev ;
2020-11-25 16:50:12 +01:00
void __iomem * arva ; /* ATSD register virtual address */
spinlock_t atsd_lock ; /* to serialize shootdowns */
2018-01-23 12:31:41 +01:00
atomic_t irq_available ;
struct spa * spa ;
void * platform_data ;
} ;
static struct list_head links_list = LIST_HEAD_INIT ( links_list ) ;
static DEFINE_MUTEX ( links_list_lock ) ;
enum xsl_response {
CONTINUE ,
ADDRESS_ERROR ,
RESTART ,
} ;
static void read_irq ( struct spa * spa , u64 * dsisr , u64 * dar , u64 * pe )
{
u64 reg ;
* dsisr = in_be64 ( spa - > reg_dsisr ) ;
* dar = in_be64 ( spa - > reg_dar ) ;
reg = in_be64 ( spa - > reg_pe_handle ) ;
* pe = reg & SPA_PE_MASK ;
}
static void ack_irq ( struct spa * spa , enum xsl_response r )
{
u64 reg = 0 ;
/* continue is not supported */
if ( r = = RESTART )
reg = PPC_BIT ( 31 ) ;
else if ( r = = ADDRESS_ERROR )
reg = PPC_BIT ( 30 ) ;
else
WARN ( 1 , " Invalid irq response %d \n " , r ) ;
2018-01-23 12:31:44 +01:00
if ( reg ) {
trace_ocxl_fault_ack ( spa - > spa_mem , spa - > xsl_fault . pe ,
spa - > xsl_fault . dsisr , spa - > xsl_fault . dar , reg ) ;
2018-01-23 12:31:41 +01:00
out_be64 ( spa - > reg_tfc , reg ) ;
2018-01-23 12:31:44 +01:00
}
2018-01-23 12:31:41 +01:00
}
static void xsl_fault_handler_bh ( struct work_struct * fault_work )
{
2018-08-17 15:44:47 -07:00
vm_fault_t flt = 0 ;
2018-01-23 12:31:41 +01:00
unsigned long access , flags , inv_flags = 0 ;
enum xsl_response r ;
struct xsl_fault * fault = container_of ( fault_work , struct xsl_fault ,
fault_work ) ;
struct spa * spa = container_of ( fault , struct spa , xsl_fault ) ;
int rc ;
/*
2018-06-18 14:14:36 +02:00
* We must release a reference on mm_users whenever exiting this
2018-01-23 12:31:41 +01:00
* function ( taken in the memory fault interrupt handler )
*/
rc = copro_handle_mm_fault ( fault - > pe_data . mm , fault - > dar , fault - > dsisr ,
& flt ) ;
if ( rc ) {
pr_debug ( " copro_handle_mm_fault failed: %d \n " , rc ) ;
if ( fault - > pe_data . xsl_err_cb ) {
fault - > pe_data . xsl_err_cb (
fault - > pe_data . xsl_err_data ,
fault - > dar , fault - > dsisr ) ;
}
r = ADDRESS_ERROR ;
goto ack ;
}
if ( ! radix_enabled ( ) ) {
/*
* update_mmu_cache ( ) will not have loaded the hash
* since current - > trap is not a 0x400 or 0x300 , so
* just call hash_page_mm ( ) here .
*/
access = _PAGE_PRESENT | _PAGE_READ ;
if ( fault - > dsisr & SPA_XSL_S )
access | = _PAGE_WRITE ;
2019-04-17 18:29:14 +05:30
if ( get_region_id ( fault - > dar ) ! = USER_REGION_ID )
2018-01-23 12:31:41 +01:00
access | = _PAGE_PRIVILEGED ;
local_irq_save ( flags ) ;
hash_page_mm ( fault - > pe_data . mm , fault - > dar , access , 0x300 ,
inv_flags ) ;
local_irq_restore ( flags ) ;
}
r = RESTART ;
ack :
2018-06-18 14:14:36 +02:00
mmput ( fault - > pe_data . mm ) ;
2018-01-23 12:31:41 +01:00
ack_irq ( spa , r ) ;
}
static irqreturn_t xsl_fault_handler ( int irq , void * data )
{
2019-03-25 16:34:52 +11:00
struct ocxl_link * link = ( struct ocxl_link * ) data ;
2018-01-23 12:31:41 +01:00
struct spa * spa = link - > spa ;
u64 dsisr , dar , pe_handle ;
struct pe_data * pe_data ;
struct ocxl_process_element * pe ;
2019-03-29 23:44:56 +08:00
int pid ;
2018-06-18 14:14:36 +02:00
bool schedule = false ;
2018-01-23 12:31:41 +01:00
read_irq ( spa , & dsisr , & dar , & pe_handle ) ;
2018-01-23 12:31:44 +01:00
trace_ocxl_fault ( spa - > spa_mem , pe_handle , dsisr , dar , - 1 ) ;
2018-01-23 12:31:41 +01:00
WARN_ON ( pe_handle > SPA_PE_MASK ) ;
pe = spa - > spa_mem + pe_handle ;
pid = be32_to_cpu ( pe - > pid ) ;
/* We could be reading all null values here if the PE is being
* removed while an interrupt kicks in . It ' s not supposed to
* happen if the driver notified the AFU to terminate the
* PASID , and the AFU waited for pending operations before
* acknowledging . But even if it happens , we won ' t find a
* memory context below and fail silently , so it should be ok .
*/
if ( ! ( dsisr & SPA_XSL_TF ) ) {
WARN ( 1 , " Invalid xsl interrupt fault register %#llx \n " , dsisr ) ;
ack_irq ( spa , ADDRESS_ERROR ) ;
return IRQ_HANDLED ;
}
rcu_read_lock ( ) ;
pe_data = radix_tree_lookup ( & spa - > pe_tree , pe_handle ) ;
if ( ! pe_data ) {
/*
* Could only happen if the driver didn ' t notify the
* AFU about PASID termination before removing the PE ,
* or the AFU didn ' t wait for all memory access to
* have completed .
*
* Either way , we fail early , but we shouldn ' t log an
* error message , as it is a valid ( if unexpected )
* scenario
*/
rcu_read_unlock ( ) ;
pr_debug ( " Unknown mm context for xsl interrupt \n " ) ;
ack_irq ( spa , ADDRESS_ERROR ) ;
return IRQ_HANDLED ;
}
2019-06-20 14:12:01 +10:00
if ( ! pe_data - > mm ) {
/*
* translation fault from a kernel context - an OpenCAPI
* device tried to access a bad kernel address
*/
rcu_read_unlock ( ) ;
pr_warn ( " Unresolved OpenCAPI xsl fault in kernel context \n " ) ;
ack_irq ( spa , ADDRESS_ERROR ) ;
return IRQ_HANDLED ;
}
2018-01-23 12:31:41 +01:00
WARN_ON ( pe_data - > mm - > context . id ! = pid ) ;
2018-06-18 14:14:36 +02:00
if ( mmget_not_zero ( pe_data - > mm ) ) {
spa - > xsl_fault . pe = pe_handle ;
spa - > xsl_fault . dar = dar ;
spa - > xsl_fault . dsisr = dsisr ;
spa - > xsl_fault . pe_data = * pe_data ;
schedule = true ;
/* mm_users count released by bottom half */
}
2018-01-23 12:31:41 +01:00
rcu_read_unlock ( ) ;
2018-06-18 14:14:36 +02:00
if ( schedule )
schedule_work ( & spa - > xsl_fault . fault_work ) ;
else
ack_irq ( spa , ADDRESS_ERROR ) ;
2018-01-23 12:31:41 +01:00
return IRQ_HANDLED ;
}
static void unmap_irq_registers ( struct spa * spa )
{
pnv_ocxl_unmap_xsl_regs ( spa - > reg_dsisr , spa - > reg_dar , spa - > reg_tfc ,
spa - > reg_pe_handle ) ;
}
static int map_irq_registers ( struct pci_dev * dev , struct spa * spa )
{
return pnv_ocxl_map_xsl_regs ( dev , & spa - > reg_dsisr , & spa - > reg_dar ,
& spa - > reg_tfc , & spa - > reg_pe_handle ) ;
}
2019-03-25 16:34:52 +11:00
static int setup_xsl_irq ( struct pci_dev * dev , struct ocxl_link * link )
2018-01-23 12:31:41 +01:00
{
struct spa * spa = link - > spa ;
int rc ;
int hwirq ;
rc = pnv_ocxl_get_xsl_irq ( dev , & hwirq ) ;
if ( rc )
return rc ;
rc = map_irq_registers ( dev , spa ) ;
if ( rc )
return rc ;
spa - > irq_name = kasprintf ( GFP_KERNEL , " ocxl-xsl-%x-%x-%x " ,
link - > domain , link - > bus , link - > dev ) ;
if ( ! spa - > irq_name ) {
dev_err ( & dev - > dev , " Can't allocate name for xsl interrupt \n " ) ;
2018-12-10 16:18:13 +01:00
rc = - ENOMEM ;
goto err_xsl ;
2018-01-23 12:31:41 +01:00
}
/*
* At some point , we ' ll need to look into allowing a higher
* number of interrupts . Could we have an IRQ domain per link ?
*/
spa - > virq = irq_create_mapping ( NULL , hwirq ) ;
if ( ! spa - > virq ) {
dev_err ( & dev - > dev ,
" irq_create_mapping failed for translation interrupt \n " ) ;
2018-12-10 16:18:13 +01:00
rc = - EINVAL ;
goto err_name ;
2018-01-23 12:31:41 +01:00
}
dev_dbg ( & dev - > dev , " hwirq %d mapped to virq %d \n " , hwirq , spa - > virq ) ;
rc = request_irq ( spa - > virq , xsl_fault_handler , 0 , spa - > irq_name ,
link ) ;
if ( rc ) {
dev_err ( & dev - > dev ,
" request_irq failed for translation interrupt: %d \n " ,
rc ) ;
2018-12-10 16:18:13 +01:00
rc = - EINVAL ;
goto err_mapping ;
2018-01-23 12:31:41 +01:00
}
return 0 ;
2018-12-10 16:18:13 +01:00
err_mapping :
irq_dispose_mapping ( spa - > virq ) ;
err_name :
kfree ( spa - > irq_name ) ;
err_xsl :
unmap_irq_registers ( spa ) ;
return rc ;
2018-01-23 12:31:41 +01:00
}
2019-03-25 16:34:52 +11:00
static void release_xsl_irq ( struct ocxl_link * link )
2018-01-23 12:31:41 +01:00
{
struct spa * spa = link - > spa ;
if ( spa - > virq ) {
free_irq ( spa - > virq , link ) ;
irq_dispose_mapping ( spa - > virq ) ;
}
kfree ( spa - > irq_name ) ;
unmap_irq_registers ( spa ) ;
}
2019-03-25 16:34:52 +11:00
static int alloc_spa ( struct pci_dev * dev , struct ocxl_link * link )
2018-01-23 12:31:41 +01:00
{
struct spa * spa ;
spa = kzalloc ( sizeof ( struct spa ) , GFP_KERNEL ) ;
if ( ! spa )
return - ENOMEM ;
mutex_init ( & spa - > spa_lock ) ;
INIT_RADIX_TREE ( & spa - > pe_tree , GFP_KERNEL ) ;
INIT_WORK ( & spa - > xsl_fault . fault_work , xsl_fault_handler_bh ) ;
spa - > spa_order = SPA_SPA_SIZE_LOG - PAGE_SHIFT ;
spa - > spa_mem = ( struct ocxl_process_element * )
__get_free_pages ( GFP_KERNEL | __GFP_ZERO , spa - > spa_order ) ;
if ( ! spa - > spa_mem ) {
dev_err ( & dev - > dev , " Can't allocate Shared Process Area \n " ) ;
kfree ( spa ) ;
return - ENOMEM ;
}
pr_debug ( " Allocated SPA for %x:%x:%x at %p \n " , link - > domain , link - > bus ,
link - > dev , spa - > spa_mem ) ;
link - > spa = spa ;
return 0 ;
}
2019-03-25 16:34:52 +11:00
static void free_spa ( struct ocxl_link * link )
2018-01-23 12:31:41 +01:00
{
struct spa * spa = link - > spa ;
pr_debug ( " Freeing SPA for %x:%x:%x \n " , link - > domain , link - > bus ,
link - > dev ) ;
if ( spa & & spa - > spa_mem ) {
free_pages ( ( unsigned long ) spa - > spa_mem , spa - > spa_order ) ;
kfree ( spa ) ;
link - > spa = NULL ;
}
}
2019-03-25 16:34:52 +11:00
static int alloc_link ( struct pci_dev * dev , int PE_mask , struct ocxl_link * * out_link )
2018-01-23 12:31:41 +01:00
{
2019-03-25 16:34:52 +11:00
struct ocxl_link * link ;
2018-01-23 12:31:41 +01:00
int rc ;
2019-03-25 16:34:52 +11:00
link = kzalloc ( sizeof ( struct ocxl_link ) , GFP_KERNEL ) ;
2018-01-23 12:31:41 +01:00
if ( ! link )
return - ENOMEM ;
kref_init ( & link - > ref ) ;
link - > domain = pci_domain_nr ( dev - > bus ) ;
link - > bus = dev - > bus - > number ;
link - > dev = PCI_SLOT ( dev - > devfn ) ;
atomic_set ( & link - > irq_available , MAX_IRQ_PER_LINK ) ;
2020-11-25 16:50:12 +01:00
spin_lock_init ( & link - > atsd_lock ) ;
2018-01-23 12:31:41 +01:00
rc = alloc_spa ( dev , link ) ;
if ( rc )
goto err_free ;
rc = setup_xsl_irq ( dev , link ) ;
if ( rc )
goto err_spa ;
/* platform specific hook */
rc = pnv_ocxl_spa_setup ( dev , link - > spa - > spa_mem , PE_mask ,
& link - > platform_data ) ;
if ( rc )
goto err_xsl_irq ;
2020-11-25 16:50:12 +01:00
/* if link->arva is not defeined, MMIO registers are not used to
* generate TLB invalidate . PowerBus snooping is enabled .
* Otherwise , PowerBus snooping is disabled . TLB Invalidates are
* initiated using MMIO registers .
*/
pnv_ocxl_map_lpar ( dev , mfspr ( SPRN_LPID ) , 0 , & link - > arva ) ;
2018-01-23 12:31:41 +01:00
* out_link = link ;
return 0 ;
err_xsl_irq :
release_xsl_irq ( link ) ;
err_spa :
free_spa ( link ) ;
err_free :
kfree ( link ) ;
return rc ;
}
2019-03-25 16:34:52 +11:00
static void free_link ( struct ocxl_link * link )
2018-01-23 12:31:41 +01:00
{
release_xsl_irq ( link ) ;
free_spa ( link ) ;
kfree ( link ) ;
}
int ocxl_link_setup ( struct pci_dev * dev , int PE_mask , void * * link_handle )
{
int rc = 0 ;
2019-03-25 16:34:52 +11:00
struct ocxl_link * link ;
2018-01-23 12:31:41 +01:00
mutex_lock ( & links_list_lock ) ;
list_for_each_entry ( link , & links_list , list ) {
/* The functions of a device all share the same link */
if ( link - > domain = = pci_domain_nr ( dev - > bus ) & &
link - > bus = = dev - > bus - > number & &
link - > dev = = PCI_SLOT ( dev - > devfn ) ) {
kref_get ( & link - > ref ) ;
* link_handle = link ;
goto unlock ;
}
}
rc = alloc_link ( dev , PE_mask , & link ) ;
if ( rc )
goto unlock ;
list_add ( & link - > list , & links_list ) ;
* link_handle = link ;
unlock :
mutex_unlock ( & links_list_lock ) ;
return rc ;
}
2018-01-23 12:31:43 +01:00
EXPORT_SYMBOL_GPL ( ocxl_link_setup ) ;
2018-01-23 12:31:41 +01:00
static void release_xsl ( struct kref * ref )
{
2019-03-25 16:34:52 +11:00
struct ocxl_link * link = container_of ( ref , struct ocxl_link , ref ) ;
2018-01-23 12:31:41 +01:00
2020-11-25 16:50:12 +01:00
if ( link - > arva ) {
pnv_ocxl_unmap_lpar ( link - > arva ) ;
link - > arva = NULL ;
}
2018-01-23 12:31:41 +01:00
list_del ( & link - > list ) ;
/* call platform code before releasing data */
pnv_ocxl_spa_release ( link - > platform_data ) ;
free_link ( link ) ;
}
void ocxl_link_release ( struct pci_dev * dev , void * link_handle )
{
2019-03-25 16:34:52 +11:00
struct ocxl_link * link = ( struct ocxl_link * ) link_handle ;
2018-01-23 12:31:41 +01:00
mutex_lock ( & links_list_lock ) ;
kref_put ( & link - > ref , release_xsl ) ;
mutex_unlock ( & links_list_lock ) ;
}
2018-01-23 12:31:43 +01:00
EXPORT_SYMBOL_GPL ( ocxl_link_release ) ;
2018-01-23 12:31:41 +01:00
2020-11-25 16:50:12 +01:00
static void invalidate_range ( struct mmu_notifier * mn ,
struct mm_struct * mm ,
unsigned long start , unsigned long end )
{
struct pe_data * pe_data = container_of ( mn , struct pe_data , mmu_notifier ) ;
struct ocxl_link * link = pe_data - > link ;
unsigned long addr , pid , page_size = PAGE_SIZE ;
pid = mm - > context . id ;
2020-11-25 16:50:13 +01:00
trace_ocxl_mmu_notifier_range ( start , end , pid ) ;
2020-11-25 16:50:12 +01:00
spin_lock ( & link - > atsd_lock ) ;
for ( addr = start ; addr < end ; addr + = page_size )
pnv_ocxl_tlb_invalidate ( link - > arva , pid , addr , page_size ) ;
spin_unlock ( & link - > atsd_lock ) ;
}
static const struct mmu_notifier_ops ocxl_mmu_notifier_ops = {
. invalidate_range = invalidate_range ,
} ;
2018-01-23 12:31:41 +01:00
static u64 calculate_cfg_state ( bool kernel )
{
u64 state ;
state = SPA_CFG_DR ;
if ( mfspr ( SPRN_LPCR ) & LPCR_TC )
state | = SPA_CFG_TC ;
if ( radix_enabled ( ) )
state | = SPA_CFG_XLAT_ror ;
else
state | = SPA_CFG_XLAT_hpt ;
state | = SPA_CFG_HV ;
if ( kernel ) {
if ( mfmsr ( ) & MSR_SF )
state | = SPA_CFG_SF ;
} else {
state | = SPA_CFG_PR ;
if ( ! test_tsk_thread_flag ( current , TIF_32BIT ) )
state | = SPA_CFG_SF ;
}
return state ;
}
int ocxl_link_add_pe ( void * link_handle , int pasid , u32 pidr , u32 tidr ,
2020-11-25 16:50:11 +01:00
u64 amr , u16 bdf , struct mm_struct * mm ,
2018-01-23 12:31:41 +01:00
void ( * xsl_err_cb ) ( void * data , u64 addr , u64 dsisr ) ,
void * xsl_err_data )
{
2019-03-25 16:34:52 +11:00
struct ocxl_link * link = ( struct ocxl_link * ) link_handle ;
2018-01-23 12:31:41 +01:00
struct spa * spa = link - > spa ;
struct ocxl_process_element * pe ;
int pe_handle , rc = 0 ;
struct pe_data * pe_data ;
BUILD_BUG_ON ( sizeof ( struct ocxl_process_element ) ! = 128 ) ;
if ( pasid > SPA_PASID_MAX )
return - EINVAL ;
mutex_lock ( & spa - > spa_lock ) ;
pe_handle = pasid & SPA_PE_MASK ;
pe = spa - > spa_mem + pe_handle ;
if ( pe - > software_state ) {
rc = - EBUSY ;
goto unlock ;
}
pe_data = kmalloc ( sizeof ( * pe_data ) , GFP_KERNEL ) ;
if ( ! pe_data ) {
rc = - ENOMEM ;
goto unlock ;
}
pe_data - > mm = mm ;
pe_data - > xsl_err_cb = xsl_err_cb ;
pe_data - > xsl_err_data = xsl_err_data ;
2020-11-25 16:50:12 +01:00
pe_data - > link = link ;
pe_data - > mmu_notifier . ops = & ocxl_mmu_notifier_ops ;
2018-01-23 12:31:41 +01:00
memset ( pe , 0 , sizeof ( struct ocxl_process_element ) ) ;
pe - > config_state = cpu_to_be64 ( calculate_cfg_state ( pidr = = 0 ) ) ;
2020-11-25 16:50:11 +01:00
pe - > pasid = cpu_to_be32 ( pasid < < ( 31 - 19 ) ) ;
pe - > bdf = cpu_to_be16 ( bdf ) ;
2018-01-23 12:31:41 +01:00
pe - > lpid = cpu_to_be32 ( mfspr ( SPRN_LPID ) ) ;
pe - > pid = cpu_to_be32 ( pidr ) ;
pe - > tid = cpu_to_be32 ( tidr ) ;
pe - > amr = cpu_to_be64 ( amr ) ;
pe - > software_state = cpu_to_be32 ( SPA_PE_VALID ) ;
2019-06-20 14:12:01 +10:00
/*
* For user contexts , register a copro so that TLBIs are seen
* by the nest MMU . If we have a kernel context , TLBIs are
* already global .
*/
2020-11-25 16:50:12 +01:00
if ( mm ) {
2019-06-20 14:12:01 +10:00
mm_context_add_copro ( mm ) ;
2020-11-25 16:50:12 +01:00
if ( link - > arva ) {
/* Use MMIO registers for the TLB Invalidate
* operations .
*/
2020-11-25 16:50:13 +01:00
trace_ocxl_init_mmu_notifier ( pasid , mm - > context . id ) ;
2020-11-25 16:50:12 +01:00
mmu_notifier_register ( & pe_data - > mmu_notifier , mm ) ;
}
}
2018-01-23 12:31:41 +01:00
/*
* Barrier is to make sure PE is visible in the SPA before it
* is used by the device . It also helps with the global TLBI
* invalidation
*/
mb ( ) ;
radix_tree_insert ( & spa - > pe_tree , pe_handle , pe_data ) ;
/*
* The mm must stay valid for as long as the device uses it . We
* lower the count when the context is removed from the SPA .
*
* We grab mm_count ( and not mm_users ) , as we don ' t want to
* end up in a circular dependency if a process mmaps its
* mmio , therefore incrementing the file ref count when
* calling mmap ( ) , and forgets to unmap before exiting . In
* that scenario , when the kernel handles the death of the
* process , the file is not cleaned because unmap was not
* called , and the mm wouldn ' t be freed because we would still
* have a reference on mm_users . Incrementing mm_count solves
* the problem .
*/
2019-06-20 14:12:01 +10:00
if ( mm )
mmgrab ( mm ) ;
2018-01-23 12:31:44 +01:00
trace_ocxl_context_add ( current - > pid , spa - > spa_mem , pasid , pidr , tidr ) ;
2018-01-23 12:31:41 +01:00
unlock :
mutex_unlock ( & spa - > spa_lock ) ;
return rc ;
}
2018-01-23 12:31:43 +01:00
EXPORT_SYMBOL_GPL ( ocxl_link_add_pe ) ;
2018-01-23 12:31:41 +01:00
2018-05-11 16:13:01 +10:00
int ocxl_link_update_pe ( void * link_handle , int pasid , __u16 tid )
{
2019-03-25 16:34:52 +11:00
struct ocxl_link * link = ( struct ocxl_link * ) link_handle ;
2018-05-11 16:13:01 +10:00
struct spa * spa = link - > spa ;
struct ocxl_process_element * pe ;
int pe_handle , rc ;
if ( pasid > SPA_PASID_MAX )
return - EINVAL ;
pe_handle = pasid & SPA_PE_MASK ;
pe = spa - > spa_mem + pe_handle ;
mutex_lock ( & spa - > spa_lock ) ;
2018-12-16 22:28:50 +01:00
pe - > tid = cpu_to_be32 ( tid ) ;
2018-05-11 16:13:01 +10:00
/*
* The barrier makes sure the PE is updated
* before we clear the NPU context cache below , so that the
* old PE cannot be reloaded erroneously .
*/
mb ( ) ;
/*
* hook to platform code
* On powerpc , the entry needs to be cleared from the context
* cache of the NPU .
*/
rc = pnv_ocxl_spa_remove_pe_from_cache ( link - > platform_data , pe_handle ) ;
WARN_ON ( rc ) ;
mutex_unlock ( & spa - > spa_lock ) ;
return rc ;
}
2018-01-23 12:31:41 +01:00
int ocxl_link_remove_pe ( void * link_handle , int pasid )
{
2019-03-25 16:34:52 +11:00
struct ocxl_link * link = ( struct ocxl_link * ) link_handle ;
2018-01-23 12:31:41 +01:00
struct spa * spa = link - > spa ;
struct ocxl_process_element * pe ;
struct pe_data * pe_data ;
int pe_handle , rc ;
if ( pasid > SPA_PASID_MAX )
return - EINVAL ;
/*
* About synchronization with our memory fault handler :
*
* Before removing the PE , the driver is supposed to have
* notified the AFU , which should have cleaned up and make
* sure the PASID is no longer in use , including pending
* interrupts . However , there ' s no way to be sure . . .
*
* We clear the PE and remove the context from our radix
* tree . From that point on , any new interrupt for that
* context will fail silently , which is ok . As mentioned
* above , that ' s not expected , but it could happen if the
* driver or AFU didn ' t do the right thing .
*
* There could still be a bottom half running , but we don ' t
* need to wait / flush , as it is managing a reference count on
* the mm it reads from the radix tree .
*/
pe_handle = pasid & SPA_PE_MASK ;
pe = spa - > spa_mem + pe_handle ;
mutex_lock ( & spa - > spa_lock ) ;
if ( ! ( be32_to_cpu ( pe - > software_state ) & SPA_PE_VALID ) ) {
rc = - EINVAL ;
goto unlock ;
}
2018-01-23 12:31:44 +01:00
trace_ocxl_context_remove ( current - > pid , spa - > spa_mem , pasid ,
be32_to_cpu ( pe - > pid ) , be32_to_cpu ( pe - > tid ) ) ;
2018-01-23 12:31:41 +01:00
memset ( pe , 0 , sizeof ( struct ocxl_process_element ) ) ;
/*
* The barrier makes sure the PE is removed from the SPA
* before we clear the NPU context cache below , so that the
* old PE cannot be reloaded erroneously .
*/
mb ( ) ;
/*
* hook to platform code
* On powerpc , the entry needs to be cleared from the context
* cache of the NPU .
*/
2018-05-11 16:13:00 +10:00
rc = pnv_ocxl_spa_remove_pe_from_cache ( link - > platform_data , pe_handle ) ;
2018-01-23 12:31:41 +01:00
WARN_ON ( rc ) ;
pe_data = radix_tree_delete ( & spa - > pe_tree , pe_handle ) ;
if ( ! pe_data ) {
WARN ( 1 , " Couldn't find pe data when removing PE \n " ) ;
} else {
2019-06-20 14:12:01 +10:00
if ( pe_data - > mm ) {
2020-11-25 16:50:12 +01:00
if ( link - > arva ) {
2020-11-25 16:50:13 +01:00
trace_ocxl_release_mmu_notifier ( pasid ,
pe_data - > mm - > context . id ) ;
2020-11-25 16:50:12 +01:00
mmu_notifier_unregister ( & pe_data - > mmu_notifier ,
pe_data - > mm ) ;
spin_lock ( & link - > atsd_lock ) ;
pnv_ocxl_tlb_invalidate ( link - > arva ,
pe_data - > mm - > context . id ,
0ull ,
PAGE_SIZE ) ;
spin_unlock ( & link - > atsd_lock ) ;
}
2019-06-20 14:12:01 +10:00
mm_context_remove_copro ( pe_data - > mm ) ;
mmdrop ( pe_data - > mm ) ;
}
2018-01-23 12:31:41 +01:00
kfree_rcu ( pe_data , rcu ) ;
}
unlock :
mutex_unlock ( & spa - > spa_lock ) ;
return rc ;
}
2018-01-23 12:31:43 +01:00
EXPORT_SYMBOL_GPL ( ocxl_link_remove_pe ) ;
2018-01-23 12:31:42 +01:00
2020-04-03 17:38:37 +02:00
int ocxl_link_irq_alloc ( void * link_handle , int * hw_irq )
2018-01-23 12:31:42 +01:00
{
2019-03-25 16:34:52 +11:00
struct ocxl_link * link = ( struct ocxl_link * ) link_handle ;
2020-04-03 17:38:37 +02:00
int irq ;
2018-01-23 12:31:42 +01:00
if ( atomic_dec_if_positive ( & link - > irq_available ) < 0 )
return - ENOSPC ;
2020-04-03 17:38:37 +02:00
irq = xive_native_alloc_irq ( ) ;
if ( ! irq ) {
2018-01-23 12:31:42 +01:00
atomic_inc ( & link - > irq_available ) ;
2020-04-03 17:38:37 +02:00
return - ENXIO ;
2018-01-23 12:31:42 +01:00
}
* hw_irq = irq ;
return 0 ;
}
2018-01-23 12:31:43 +01:00
EXPORT_SYMBOL_GPL ( ocxl_link_irq_alloc ) ;
2018-01-23 12:31:42 +01:00
void ocxl_link_free_irq ( void * link_handle , int hw_irq )
{
2019-03-25 16:34:52 +11:00
struct ocxl_link * link = ( struct ocxl_link * ) link_handle ;
2018-01-23 12:31:42 +01:00
2020-04-03 17:38:37 +02:00
xive_native_free_irq ( hw_irq ) ;
2018-01-23 12:31:42 +01:00
atomic_inc ( & link - > irq_available ) ;
}
2018-01-23 12:31:43 +01:00
EXPORT_SYMBOL_GPL ( ocxl_link_free_irq ) ;