#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/ib/adapters/tavor/tavor.h>
static kstat_t *tavor_kstat_picN_create(tavor_state_t *state, int num_pic,
int num_evt, tavor_ks_mask_t *ev_array);
static kstat_t *tavor_kstat_cntr_create(tavor_state_t *state, int num_pic,
int (*update)(kstat_t *, int));
static int tavor_kstat_cntr_update(kstat_t *ksp, int rw);
void tavor_kstat_perfcntr64_create(tavor_state_t *state, uint_t port_num);
static int tavor_kstat_perfcntr64_read(tavor_state_t *state, uint_t port,
int reset);
static void tavor_kstat_perfcntr64_thread_exit(tavor_ks_info_t *ksi);
static int tavor_kstat_perfcntr64_update(kstat_t *ksp, int rw);
tavor_ks_mask_t tavor_ib_perfcnt_list[TAVOR_CNTR_NUMENTRIES] = {
{"port_xmit_data", TAVOR_HW_PMEG_PORTXMITDATA_OFFSET,
0, 0xFFFFFFFF, 0, 0},
{"port_recv_data", TAVOR_HW_PMEG_PORTRECVDATA_OFFSET,
0, 0xFFFFFFFF, 0, 0},
{"port_xmit_pkts", TAVOR_HW_PMEG_PORTXMITPKTS_OFFSET,
0, 0xFFFFFFFF, 0, 0},
{"port_recv_pkts", TAVOR_HW_PMEG_PORTRECVPKTS_OFFSET,
0, 0xFFFFFFFF, 0, 0},
{"port_recv_err", TAVOR_HW_PMEG_PORTRECVERR_OFFSET,
0, 0xFFFF, 0, 0},
{"port_xmit_discards", TAVOR_HW_PMEG_PORTXMITDISCARD_OFFSET,
0, 0xFFFF, 0, 0},
{"vl15_dropped", TAVOR_HW_PMEG_VL15DROPPED_OFFSET,
0, 0xFFFF, 0, 0},
{"port_xmit_wait", TAVOR_HW_PMEG_PORTXMITWAIT_OFFSET,
0, 0xFFFFFFFF, 0, 0},
{"port_recv_remote_phys_err", TAVOR_HW_PMEG_PORTRECVREMPHYSERR_OFFSET,
0, 0xFFFF, 0, 0},
{"port_xmit_constraint_err", TAVOR_HW_PMEG_PORTXMITCONSTERR_OFFSET,
0, 0xFF, 0, 0},
{"port_recv_constraint_err", TAVOR_HW_PMEG_PORTRECVCONSTERR_OFFSET,
0, 0xFF, 0, 0},
{"symbol_err_counter", TAVOR_HW_PMEG_SYMBOLERRCNT_OFFSET,
0, 0xFFFF, 0, 0},
{"link_err_recovery_cnt", TAVOR_HW_PMEG_LINKERRRECOVERCNT_OFFSET,
0, 0xFFFF, 0, 0},
{"link_downed_cnt", TAVOR_HW_PMEG_LINKDOWNEDCNT_OFFSET,
16, 0xFFFF, 0, 0},
{"excessive_buffer_overruns", TAVOR_HW_PMEG_EXCESSBUFOVERRUN_OFFSET,
0, 0xF, 0, 0},
{"local_link_integrity_err", TAVOR_HW_PMEG_LOCALLINKINTERR_OFFSET,
8, 0xF, 0, 0},
{"clear_pic", 0, 0, 0, 0}
};
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define SET_TO_MAX(x, y) \
{ \
if ((x) < (y)) \
(x) = (y); \
}
int
tavor_kstat_init(tavor_state_t *state)
{
tavor_ks_info_t *ksi;
uint_t numports;
int i;
ksi = (tavor_ks_info_t *)kmem_zalloc(sizeof (tavor_ks_info_t),
KM_SLEEP);
if (ksi == NULL) {
return (DDI_FAILURE);
}
state->ts_ks_info = ksi;
numports = state->ts_cfg_profile->cp_num_ports;
for (i = 0; i < numports; i++) {
ksi->tki_picN_ksp[i] = tavor_kstat_picN_create(state, i,
TAVOR_CNTR_NUMENTRIES, tavor_ib_perfcnt_list);
if (ksi->tki_picN_ksp[i] == NULL) {
goto kstat_init_fail;
}
tavor_kstat_perfcntr64_create(state, i + 1);
if (ksi->tki_perfcntr64[i].tki64_ksp == NULL) {
goto kstat_init_fail;
}
}
ksi->tki_cntr_ksp = tavor_kstat_cntr_create(state, numports,
tavor_kstat_cntr_update);
if (ksi->tki_cntr_ksp == NULL) {
goto kstat_init_fail;
}
ksi->tki_pcr = 0;
ksi->tki_pic0 = 0;
ksi->tki_pic1 = 0;
for (i = 0; i < TAVOR_CNTR_NUMENTRIES; i++) {
ksi->tki_ib_perfcnt[i] = tavor_ib_perfcnt_list[i];
}
mutex_init(&ksi->tki_perfcntr64_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&ksi->tki_perfcntr64_cv, NULL, CV_DRIVER, NULL);
return (DDI_SUCCESS);
kstat_init_fail:
if (ksi->tki_cntr_ksp != NULL) {
kstat_delete(ksi->tki_cntr_ksp);
}
for (i = 0; i < numports; i++) {
if (ksi->tki_picN_ksp[i] != NULL) {
kstat_delete(ksi->tki_picN_ksp[i]);
}
if (ksi->tki_perfcntr64[i].tki64_ksp != NULL) {
kstat_delete(ksi->tki_perfcntr64[i].tki64_ksp);
}
}
kmem_free(ksi, sizeof (tavor_ks_info_t));
return (DDI_FAILURE);
}
void
tavor_kstat_fini(tavor_state_t *state)
{
tavor_ks_info_t *ksi;
uint_t numports;
int i;
ksi = state->ts_ks_info;
mutex_enter(&ksi->tki_perfcntr64_lock);
tavor_kstat_perfcntr64_thread_exit(ksi);
mutex_exit(&ksi->tki_perfcntr64_lock);
numports = state->ts_cfg_profile->cp_num_ports;
for (i = 0; i < numports; i++) {
if (ksi->tki_picN_ksp[i] != NULL) {
kstat_delete(ksi->tki_picN_ksp[i]);
}
if (ksi->tki_perfcntr64[i].tki64_ksp != NULL) {
kstat_delete(ksi->tki_perfcntr64[i].tki64_ksp);
}
}
kstat_delete(ksi->tki_cntr_ksp);
cv_destroy(&ksi->tki_perfcntr64_cv);
mutex_destroy(&ksi->tki_perfcntr64_lock);
kmem_free(ksi, sizeof (tavor_ks_info_t));
}
static kstat_t *
tavor_kstat_picN_create(tavor_state_t *state, int num_pic, int num_evt,
tavor_ks_mask_t *ev_array)
{
kstat_t *picN_ksp;
struct kstat_named *pic_named_data;
int drv_instance, i;
char *drv_name;
char pic_name[16];
drv_name = (char *)ddi_driver_name(state->ts_dip);
drv_instance = ddi_get_instance(state->ts_dip);
(void) sprintf(pic_name, "pic%d", num_pic);
picN_ksp = kstat_create(drv_name, drv_instance, pic_name, "bus",
KSTAT_TYPE_NAMED, num_evt, 0);
if (picN_ksp == NULL) {
return (NULL);
}
pic_named_data = (struct kstat_named *)(picN_ksp->ks_data);
for (i = 0; i < num_evt - 1; i++) {
pic_named_data[i].value.ui64 =
((uint64_t)i << (num_pic * TAVOR_CNTR_SIZE));
kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name,
KSTAT_DATA_UINT64);
}
pic_named_data[i].value.ui64 =
~((uint64_t)TAVOR_CNTR_MASK << (num_pic * TAVOR_CNTR_SIZE));
kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name,
KSTAT_DATA_UINT64);
kstat_install(picN_ksp);
return (picN_ksp);
}
static kstat_t *
tavor_kstat_cntr_create(tavor_state_t *state, int num_pic,
int (*update)(kstat_t *, int))
{
struct kstat *cntr_ksp;
struct kstat_named *cntr_named_data;
int drv_instance, i;
char *drv_name;
char pic_str[16];
drv_name = (char *)ddi_driver_name(state->ts_dip);
drv_instance = ddi_get_instance(state->ts_dip);
cntr_ksp = kstat_create(drv_name, drv_instance, "counters", "bus",
KSTAT_TYPE_NAMED, num_pic + 1, KSTAT_FLAG_WRITABLE);
if (cntr_ksp == NULL) {
return (NULL);
}
cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data);
kstat_named_init(&cntr_named_data[0], "pcr", KSTAT_DATA_UINT64);
for (i = 0; i < num_pic; i++) {
(void) sprintf(pic_str, "pic%d", i);
kstat_named_init(&cntr_named_data[i+1], pic_str,
KSTAT_DATA_UINT64);
}
cntr_ksp->ks_private = (void *)state;
cntr_ksp->ks_update = update;
kstat_install(cntr_ksp);
return (cntr_ksp);
}
static int
tavor_kstat_cntr_update(kstat_t *ksp, int rw)
{
tavor_state_t *state;
tavor_ks_mask_t *ib_perf;
tavor_ks_info_t *ksi;
struct kstat_named *data;
uint64_t offset, pcr;
uint32_t pic0, pic1, tmp;
uint32_t shift, mask, oldval;
uint_t numports, indx;
state = ksp->ks_private;
data = (struct kstat_named *)(ksp->ks_data);
ksi = state->ts_ks_info;
ib_perf = &ksi->tki_ib_perfcnt[0];
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ksi))
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*data))
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ib_perf))
numports = state->ts_cfg_profile->cp_num_ports;
if (rw == KSTAT_WRITE) {
ksi->tki_pcr = data[0].value.ui64;
return (0);
} else {
pcr = ksi->tki_pcr;
indx = pcr & TAVOR_CNTR_MASK;
data[0].value.ui64 = pcr;
offset = ib_perf[indx].ks_reg_offset;
shift = ib_perf[indx].ks_reg_shift;
mask = ib_perf[indx].ks_reg_mask;
oldval = ib_perf[indx].ks_old_pic0;
pic0 = ddi_get32(state->ts_reg_cmdhdl, (uint32_t *)
(uintptr_t)((uintptr_t)state->ts_reg_cmd_baseaddr +
offset));
tmp = ((pic0 >> shift) & mask);
ib_perf[indx].ks_old_pic0 = tmp;
tmp = tmp - oldval;
ksi->tki_pic0 += tmp;
data[1].value.ui64 = ksi->tki_pic0;
if (numports == TAVOR_NUM_PORTS) {
indx = pcr >> TAVOR_CNTR_SIZE;
offset = ib_perf[indx].ks_reg_offset;
shift = ib_perf[indx].ks_reg_shift;
mask = ib_perf[indx].ks_reg_mask;
oldval = ib_perf[indx].ks_old_pic1;
pic1 = ddi_get32(state->ts_reg_cmdhdl, (uint32_t *)
(uintptr_t)((uintptr_t)state->ts_reg_cmd_baseaddr +
offset + TAVOR_HW_PORT_SIZE));
tmp = ((pic1 >> shift) & mask);
ib_perf[indx].ks_old_pic1 = tmp;
tmp = tmp - oldval;
ksi->tki_pic1 += tmp;
data[2].value.ui64 = ksi->tki_pic1;
}
return (0);
}
}
void
tavor_kstat_perfcntr64_create(tavor_state_t *state, uint_t port_num)
{
tavor_ks_info_t *ksi = state->ts_ks_info;
struct kstat *cntr_ksp;
struct kstat_named *cntr_named_data;
int drv_instance;
char *drv_name;
char kname[32];
ASSERT(port_num != 0);
drv_name = (char *)ddi_driver_name(state->ts_dip);
drv_instance = ddi_get_instance(state->ts_dip);
(void) snprintf(kname, sizeof (kname), "port%u/perf_counters",
port_num);
cntr_ksp = kstat_create(drv_name, drv_instance, kname, "ib",
KSTAT_TYPE_NAMED, TAVOR_PERFCNTR64_NUM_COUNTERS,
KSTAT_FLAG_WRITABLE);
if (cntr_ksp == NULL) {
return;
}
cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data);
kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_ENABLE_IDX],
"enable", KSTAT_DATA_UINT32);
kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_XMIT_DATA_IDX],
"xmit_data", KSTAT_DATA_UINT64);
kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_RECV_DATA_IDX],
"recv_data", KSTAT_DATA_UINT64);
kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_XMIT_PKTS_IDX],
"xmit_pkts", KSTAT_DATA_UINT64);
kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_RECV_PKTS_IDX],
"recv_pkts", KSTAT_DATA_UINT64);
ksi->tki_perfcntr64[port_num - 1].tki64_ksp = cntr_ksp;
ksi->tki_perfcntr64[port_num - 1].tki64_port_num = port_num;
ksi->tki_perfcntr64[port_num - 1].tki64_state = state;
cntr_ksp->ks_private = &ksi->tki_perfcntr64[port_num - 1];
cntr_ksp->ks_update = tavor_kstat_perfcntr64_update;
kstat_install(cntr_ksp);
}
static int
tavor_kstat_perfcntr64_read(tavor_state_t *state, uint_t port, int reset)
{
tavor_ks_info_t *ksi = state->ts_ks_info;
tavor_perfcntr64_ks_info_t *ksi64 = &ksi->tki_perfcntr64[port - 1];
int status, i;
uint32_t tmp;
tavor_hw_sm_perfcntr_t sm_perfcntr;
ASSERT(MUTEX_HELD(&ksi->tki_perfcntr64_lock));
ASSERT(port != 0);
status = tavor_getperfcntr_cmd_post(state, port,
TAVOR_CMD_NOSLEEP_SPIN, &sm_perfcntr, 0);
if (status != TAVOR_CMD_SUCCESS) {
return (status);
}
if (reset) {
status = tavor_getperfcntr_cmd_post(state, port,
TAVOR_CMD_NOSLEEP_SPIN, NULL, 1);
if (status != TAVOR_CMD_SUCCESS) {
return (status);
}
tmp = MAX(sm_perfcntr.portxmdata,
ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_DATA_IDX]);
ksi64->tki64_counters[TAVOR_PERFCNTR64_XMIT_DATA_IDX] += tmp;
tmp = MAX(sm_perfcntr.portrcdata,
ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_DATA_IDX]);
ksi64->tki64_counters[TAVOR_PERFCNTR64_RECV_DATA_IDX] += tmp;
tmp = MAX(sm_perfcntr.portxmpkts,
ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_PKTS_IDX]);
ksi64->tki64_counters[TAVOR_PERFCNTR64_XMIT_PKTS_IDX] += tmp;
tmp = MAX(sm_perfcntr.portrcpkts,
ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_PKTS_IDX]);
ksi64->tki64_counters[TAVOR_PERFCNTR64_RECV_PKTS_IDX] += tmp;
for (i = 0; i < TAVOR_PERFCNTR64_NUM_COUNTERS; i++)
ksi64->tki64_last_read[i] = 0;
} else {
SET_TO_MAX(
ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_DATA_IDX],
sm_perfcntr.portxmdata);
SET_TO_MAX(
ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_DATA_IDX],
sm_perfcntr.portrcdata);
SET_TO_MAX(
ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_PKTS_IDX],
sm_perfcntr.portxmpkts);
SET_TO_MAX(
ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_PKTS_IDX],
sm_perfcntr.portrcpkts);
}
return (TAVOR_CMD_SUCCESS);
}
static void
tavor_kstat_perfcntr64_update_thread(void *arg)
{
tavor_state_t *state = (tavor_state_t *)arg;
tavor_ks_info_t *ksi = state->ts_ks_info;
uint_t i;
mutex_enter(&ksi->tki_perfcntr64_lock);
while (!(ksi->tki_perfcntr64_flags & TAVOR_PERFCNTR64_THREAD_EXIT)) {
for (i = 0; i < state->ts_cfg_profile->cp_num_ports; i++) {
if (ksi->tki_perfcntr64[i].tki64_enabled) {
(void) tavor_kstat_perfcntr64_read(state,
i + 1, 1);
}
}
(void) cv_timedwait(&ksi->tki_perfcntr64_cv,
&ksi->tki_perfcntr64_lock,
ddi_get_lbolt() + drv_usectohz(1000000));
}
ksi->tki_perfcntr64_flags = 0;
mutex_exit(&ksi->tki_perfcntr64_lock);
}
static void
tavor_kstat_perfcntr64_thread_create(tavor_state_t *state)
{
tavor_ks_info_t *ksi = state->ts_ks_info;
kthread_t *thr;
ASSERT(MUTEX_HELD(&ksi->tki_perfcntr64_lock));
if (!(ksi->tki_perfcntr64_flags & TAVOR_PERFCNTR64_THREAD_CREATED)) {
thr = thread_create(NULL, 0,
tavor_kstat_perfcntr64_update_thread,
state, 0, &p0, TS_RUN, minclsyspri);
ksi->tki_perfcntr64_thread_id = thr->t_did;
ksi->tki_perfcntr64_flags |= TAVOR_PERFCNTR64_THREAD_CREATED;
}
}
static void
tavor_kstat_perfcntr64_thread_exit(tavor_ks_info_t *ksi)
{
kt_did_t tid;
ASSERT(MUTEX_HELD(&ksi->tki_perfcntr64_lock));
if (ksi->tki_perfcntr64_flags & TAVOR_PERFCNTR64_THREAD_CREATED) {
ksi->tki_perfcntr64_flags |= TAVOR_PERFCNTR64_THREAD_EXIT;
tid = ksi->tki_perfcntr64_thread_id;
cv_signal(&ksi->tki_perfcntr64_cv);
mutex_exit(&ksi->tki_perfcntr64_lock);
thread_join(tid);
mutex_enter(&ksi->tki_perfcntr64_lock);
}
}
static int
tavor_kstat_perfcntr64_update(kstat_t *ksp, int rw)
{
tavor_state_t *state;
struct kstat_named *data;
tavor_ks_info_t *ksi;
tavor_perfcntr64_ks_info_t *ksi64;
int i, thr_exit;
ksi64 = ksp->ks_private;
state = ksi64->tki64_state;
ksi = state->ts_ks_info;
data = (struct kstat_named *)(ksp->ks_data);
mutex_enter(&ksi->tki_perfcntr64_lock);
if (rw == KSTAT_WRITE) {
if (data[TAVOR_PERFCNTR64_ENABLE_IDX].value.ui32) {
if (ksi64->tki64_enabled == 0) {
if (tavor_getperfcntr_cmd_post(state,
ksi64->tki64_port_num,
TAVOR_CMD_NOSLEEP_SPIN, NULL, 1) !=
TAVOR_CMD_SUCCESS) {
mutex_exit(&ksi->tki_perfcntr64_lock);
return (EIO);
}
ksi64->tki64_enabled = 1;
for (i = 0;
i < TAVOR_PERFCNTR64_NUM_COUNTERS; i++) {
ksi64->tki64_counters[i] = 0;
ksi64->tki64_last_read[i] = 0;
}
tavor_kstat_perfcntr64_thread_create(state);
}
} else if (ksi64->tki64_enabled) {
ksi64->tki64_enabled = 0;
thr_exit = 1;
for (i = 0; i < state->ts_cfg_profile->cp_num_ports;
i++) {
if (ksi->tki_perfcntr64[i].tki64_enabled) {
thr_exit = 0;
break;
}
}
if (thr_exit)
tavor_kstat_perfcntr64_thread_exit(ksi);
}
} else if (ksi64->tki64_enabled) {
if (tavor_kstat_perfcntr64_read(state, ksi64->tki64_port_num,
0) != TAVOR_CMD_SUCCESS) {
mutex_exit(&ksi->tki_perfcntr64_lock);
return (EIO);
}
data[TAVOR_PERFCNTR64_ENABLE_IDX].value.ui32 = 1;
data[TAVOR_PERFCNTR64_XMIT_DATA_IDX].value.ui64 =
ksi64->tki64_counters[TAVOR_PERFCNTR64_XMIT_DATA_IDX] +
ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_DATA_IDX];
data[TAVOR_PERFCNTR64_RECV_DATA_IDX].value.ui64 =
ksi64->tki64_counters[TAVOR_PERFCNTR64_RECV_DATA_IDX] +
ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_DATA_IDX];
data[TAVOR_PERFCNTR64_XMIT_PKTS_IDX].value.ui64 =
ksi64->tki64_counters[TAVOR_PERFCNTR64_XMIT_PKTS_IDX] +
ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_PKTS_IDX];
data[TAVOR_PERFCNTR64_RECV_PKTS_IDX].value.ui64 =
ksi64->tki64_counters[TAVOR_PERFCNTR64_RECV_PKTS_IDX] +
ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_PKTS_IDX];
} else {
data[TAVOR_PERFCNTR64_ENABLE_IDX].value.ui32 = 0;
for (i = 1; i < TAVOR_PERFCNTR64_NUM_COUNTERS; i++)
data[i].value.ui64 = 0;
}
mutex_exit(&ksi->tki_perfcntr64_lock);
return (0);
}