#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/ddi.h>
#include <sys/sunndi.h>
#include <sys/ddi_impldefs.h>
#include <sys/psm_types.h>
#include <sys/smp_impldefs.h>
#include <sys/apic.h>
#include <sys/processor.h>
#include <sys/apix_irm_impl.h>
extern int ddi_msix_alloc_limit;
extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
psm_intr_op_t, int *);
int apix_system_max_vectors = -1;
int apix_irm_cpu_factor = 0;
int apix_irm_reserve_fixed_vectors = 0;
int apix_irm_free_fixed_vector = 1;
apix_irm_info_t apix_irminfo;
kmutex_t apix_irm_lock;
ddi_irm_params_t apix_irm_params;
int apix_irm_cache_size = 0;
int apix_irm_cpu_factor_available = 0;
int apix_irm_max_cpus = 0;
int apix_irm_cpus_used = 0;
int apix_irm_fixed_intr_vectors_used;
extern int ncpus;
static int apix_irm_chk_apix();
int apix_irm_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *handle,
psm_intr_op_t op, int *result);
int apix_irm_disable_intr(processorid_t);
void apix_irm_enable_intr(processorid_t);
int (*psm_intr_ops_saved)(dev_info_t *dip, ddi_intr_handle_impl_t *handle,
psm_intr_op_t op, int *result) = NULL;
int (*psm_disable_intr_saved)(processorid_t) = NULL;
void (*psm_enable_intr_saved)(processorid_t) = NULL;
int apix_irm_alloc_fixed(dev_info_t *, ddi_intr_handle_impl_t *, int *);
int apix_irm_free_fixed(dev_info_t *, ddi_intr_handle_impl_t *, int *);
void
apix_irm_init(void)
{
dev_info_t *dip;
int total_avail_vectors;
int cpus_used;
int cache_size;
if (!irm_enable)
return;
dip = ddi_root_node();
if ((psm_intr_ops == NULL) || !apix_irm_chk_apix()) {
APIX_IRM_DEBUG((CE_CONT,
"apix_irm_init: APIX module not present"));
return;
}
if (apix_irminfo.apix_ncpus > ncpus)
apix_irminfo.apix_ncpus = ncpus;
if ((apix_irm_cpu_factor > 0) &&
(apix_irminfo.apix_ncpus > apix_irm_cpu_factor)) {
cpus_used = apix_irminfo.apix_ncpus - apix_irm_cpu_factor;
apix_irm_cpu_factor_available = apix_irm_cpu_factor;
} else {
cpus_used = apix_irminfo.apix_ncpus;
}
apix_irm_cpus_used = apix_irm_max_cpus = cpus_used;
APIX_IRM_DEBUG((CE_CONT,
"apix_irm_init: %d CPUs used for IRM pool size", cpus_used));
total_avail_vectors = cpus_used * apix_irminfo.apix_per_cpu_vectors -
apix_irminfo.apix_vectors_allocated;
apix_irm_fixed_intr_vectors_used = apix_irminfo.apix_vectors_allocated;
if (total_avail_vectors <= 0) {
APIX_IRM_DEBUG((CE_NOTE,
"apix_irm_init: can not determine pool size"));
return;
}
if ((apix_system_max_vectors > 0) &&
(apix_system_max_vectors < total_avail_vectors))
total_avail_vectors = apix_system_max_vectors;
if (apix_irm_reserve_fixed_vectors != 0) {
cache_size = apix_irm_reserve_fixed_vectors;
if ((cache_size == -1) ||
(cache_size > apix_irminfo.apix_ioapic_max_vectors))
cache_size = apix_irminfo.apix_ioapic_max_vectors;
total_avail_vectors -= cache_size;
apix_irm_cache_size = cache_size;
}
if (total_avail_vectors <= 0) {
APIX_IRM_DEBUG((CE_NOTE,
"apix_irm_init: invalid config parameters!"));
return;
}
apix_irm_params.iparams_types = DDI_INTR_TYPE_MSI | DDI_INTR_TYPE_MSIX;
apix_irm_params.iparams_total = total_avail_vectors;
if (ndi_irm_create(dip, &apix_irm_params,
&apix_irm_pool_p) == NDI_SUCCESS) {
psm_intr_ops_saved = psm_intr_ops;
psm_intr_ops = apix_irm_intr_ops;
psm_disable_intr_saved = psm_disable_intr;
psm_enable_intr_saved = psm_enable_intr;
psm_enable_intr = apix_irm_enable_intr;
psm_disable_intr = apix_irm_disable_intr;
mutex_init(&apix_irm_lock, NULL, MUTEX_DRIVER, NULL);
if (ddi_msix_alloc_limit < DDI_MIN_MSIX_ALLOC)
ddi_msix_alloc_limit = DDI_MIN_MSIX_ALLOC;
} else {
APIX_IRM_DEBUG((CE_NOTE,
"apix_irm_init: ndi_irm_create() failed"));
apix_irm_pool_p = NULL;
}
}
static int
apix_irm_chk_apix(void)
{
ddi_intr_handle_impl_t info_hdl;
apic_get_type_t type_info;
if (!psm_intr_ops)
return (0);
bzero(&info_hdl, sizeof (ddi_intr_handle_impl_t));
info_hdl.ih_private = &type_info;
if (((*psm_intr_ops)(NULL, &info_hdl, PSM_INTR_OP_APIC_TYPE,
NULL)) != PSM_SUCCESS) {
return (0);
}
if (strcmp(type_info.avgi_type, APIC_APIX_NAME) == 0)
return (1);
else
return (0);
}
int
apix_irm_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *handle,
psm_intr_op_t op, int *result)
{
switch (op) {
case PSM_INTR_OP_ALLOC_VECTORS:
if (handle->ih_type == DDI_INTR_TYPE_FIXED)
return (apix_irm_alloc_fixed(dip, handle, result));
else
break;
case PSM_INTR_OP_FREE_VECTORS:
if (handle->ih_type == DDI_INTR_TYPE_FIXED)
return (apix_irm_free_fixed(dip, handle, result));
else
break;
default:
break;
}
return ((*psm_intr_ops_saved)(dip, handle, op, result));
}
int
apix_irm_alloc_fixed(dev_info_t *dip, ddi_intr_handle_impl_t *handle,
int *result)
{
int vector;
uint_t new_pool_size;
int ret;
ret = (*psm_intr_ops_saved)(dip, handle, PSM_INTR_OP_XLATE_VECTOR,
&vector);
if (ret == PSM_SUCCESS) {
APIX_IRM_DEBUG((CE_CONT,
"apix_irm_alloc_fixed: dip %p (%s) xlated vector 0x%x",
(void *)dip, ddi_driver_name(dip), vector));
return ((*psm_intr_ops_saved)(dip, handle,
PSM_INTR_OP_ALLOC_VECTORS, result));
}
mutex_enter(&apix_irm_lock);
if (apix_irm_cache_size) {
--apix_irm_cache_size;
apix_irm_fixed_intr_vectors_used++;
mutex_exit(&apix_irm_lock);
return ((*psm_intr_ops_saved)(dip, handle,
PSM_INTR_OP_ALLOC_VECTORS, result));
}
new_pool_size = apix_irm_params.iparams_total - 1;
APIX_IRM_DEBUG((CE_CONT, "apix_irm_alloc_fixed: dip %p (%s) resize pool"
" from %x to %x\n", (void *)dip, ddi_driver_name(dip),
apix_irm_pool_p->ipool_totsz, new_pool_size));
if (ndi_irm_resize_pool(apix_irm_pool_p, new_pool_size) ==
NDI_SUCCESS) {
apix_irm_params.iparams_total = new_pool_size;
apix_irm_fixed_intr_vectors_used++;
mutex_exit(&apix_irm_lock);
return ((*psm_intr_ops_saved)(dip, handle,
PSM_INTR_OP_ALLOC_VECTORS, result));
}
mutex_exit(&apix_irm_lock);
return (PSM_FAILURE);
}
int
apix_irm_free_fixed(dev_info_t *dip, ddi_intr_handle_impl_t *handle,
int *result)
{
int shared;
int ret;
uint_t new_pool_size;
ret = (*psm_intr_ops_saved)(dip, handle,
PSM_INTR_OP_GET_SHARED, &shared);
if ((ret == PSM_SUCCESS) && (shared > 0)) {
APIX_IRM_DEBUG((CE_CONT, "apix_irm_free_fixed: dip %p (%s) "
"shared %d\n", (void *)dip, ddi_driver_name(dip), shared));
return ((*psm_intr_ops_saved)(dip, handle,
PSM_INTR_OP_FREE_VECTORS, result));
}
ret = (*psm_intr_ops_saved)(dip, handle,
PSM_INTR_OP_FREE_VECTORS, result);
if (ret == PSM_SUCCESS) {
mutex_enter(&apix_irm_lock);
if (apix_irm_free_fixed_vector) {
new_pool_size = apix_irm_params.iparams_total + 1;
APIX_IRM_DEBUG((CE_CONT, "apix_irm_free_fixed: "
"dip %p (%s) resize pool from %x to %x\n",
(void *)dip, ddi_driver_name(dip),
apix_irm_pool_p->ipool_totsz, new_pool_size));
if (ndi_irm_resize_pool(apix_irm_pool_p,
new_pool_size) == NDI_SUCCESS) {
apix_irm_params.iparams_total = new_pool_size;
} else {
cmn_err(CE_NOTE,
"apix_irm_free_fixed: failed to add"
" a vector to IRM pool");
}
} else {
apix_irm_cache_size += 1;
}
apix_irm_fixed_intr_vectors_used--;
mutex_exit(&apix_irm_lock);
}
return (ret);
}
int
apix_irm_disable_intr(processorid_t id)
{
uint_t new_pool_size;
if (apic_cpus[id].aci_status & APIC_CPU_SUSPEND)
return ((*psm_disable_intr_saved)(id));
mutex_enter(&apix_irm_lock);
if ((apix_irm_cpu_factor > 0) && (apix_irm_cpu_factor_available > 0)) {
apix_irm_cpu_factor_available--;
} else {
if (apix_irm_cpus_used == 1) {
mutex_exit(&apix_irm_lock);
return (PSM_FAILURE);
}
new_pool_size = apix_irm_params.iparams_total -
apix_irminfo.apix_per_cpu_vectors;
if (apix_system_max_vectors > 0) {
uint_t max;
max = apix_system_max_vectors -
apix_irm_fixed_intr_vectors_used -
apix_irm_cache_size;
new_pool_size = MIN(new_pool_size, max);
}
if (new_pool_size == 0) {
cmn_err(CE_WARN, "Invalid pool size 0 with "
"apix_system_max_vectors = %d",
apix_system_max_vectors);
mutex_exit(&apix_irm_lock);
return (PSM_FAILURE);
}
if (new_pool_size != apix_irm_params.iparams_total) {
if (ndi_irm_resize_pool(apix_irm_pool_p,
new_pool_size) != NDI_SUCCESS) {
mutex_exit(&apix_irm_lock);
APIX_IRM_DEBUG((CE_NOTE,
"apix_irm_disable_intr: failed to resize"
" the IRM pool"));
return (PSM_FAILURE);
}
apix_irm_params.iparams_total = new_pool_size;
}
apix_irm_cpus_used--;
}
if ((*psm_disable_intr_saved)(id) != PSM_SUCCESS) {
APIX_IRM_DEBUG((CE_NOTE,
"apix_irm_disable_intr: failed to disable CPU interrupts"
" for CPU#%d", id));
mutex_exit(&apix_irm_lock);
return (PSM_FAILURE);
}
apix_irm_max_cpus--;
mutex_exit(&apix_irm_lock);
return (PSM_SUCCESS);
}
void
apix_irm_enable_intr(processorid_t id)
{
uint_t new_pool_size;
if (apic_cpus[id].aci_status & APIC_CPU_SUSPEND) {
(*psm_enable_intr_saved)(id);
return;
}
mutex_enter(&apix_irm_lock);
(*psm_enable_intr_saved)(id);
apix_irm_max_cpus++;
ASSERT(apix_irminfo.apix_per_cpu_vectors > 0);
if ((apix_irm_cpu_factor > 0) &&
(apix_irm_cpu_factor_available < apix_irm_cpu_factor)) {
apix_irm_cpu_factor_available++;
mutex_exit(&apix_irm_lock);
return;
}
apix_irm_cpus_used++;
new_pool_size = apix_irm_params.iparams_total +
apix_irminfo.apix_per_cpu_vectors;
if (apix_system_max_vectors > 0) {
uint_t max;
max = apix_system_max_vectors -
apix_irm_fixed_intr_vectors_used -
apix_irm_cache_size;
new_pool_size = MIN(new_pool_size, max);
}
if (new_pool_size == apix_irm_params.iparams_total) {
mutex_exit(&apix_irm_lock);
return;
}
if (new_pool_size < apix_irm_params.iparams_total) {
cmn_err(CE_WARN, "new_pool_size %d is inconsistent "
"with irm_params.iparams_total %d",
new_pool_size, apix_irm_params.iparams_total);
mutex_exit(&apix_irm_lock);
return;
}
(void) ndi_irm_resize_pool(apix_irm_pool_p, new_pool_size);
apix_irm_params.iparams_total = new_pool_size;
mutex_exit(&apix_irm_lock);
}