/* SPDX-License-Identifier: BSD-3-Clause */ /* Copyright(c) 2007-2022 Intel Corporation */ #include #include #include "qat_freebsd.h" #include "adf_heartbeat.h" #include "adf_common_drv.h" #include "adf_cfg.h" #include "adf_cfg_strings.h" #include "icp_qat_fw_init_admin.h" #include "adf_transport_internal.h" #define MAX_HB_TICKS 0xFFFFFFFF static int adf_check_hb_poll_freq(struct adf_accel_dev *accel_dev) { u64 curr_hb_check_time = 0; char timer_str[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { 0 }; unsigned int timer_val = ADF_CFG_HB_DEFAULT_VALUE; curr_hb_check_time = adf_clock_get_current_time(); if (!adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC, ADF_HEARTBEAT_TIMER, (char *)timer_str)) { if (compat_strtouint((char *)timer_str, ADF_CFG_BASE_DEC, &timer_val)) timer_val = ADF_CFG_HB_DEFAULT_VALUE; } if ((curr_hb_check_time - accel_dev->heartbeat->last_hb_check_time) < timer_val) { return EINVAL; } accel_dev->heartbeat->last_hb_check_time = curr_hb_check_time; return 0; } int adf_heartbeat_init(struct adf_accel_dev *accel_dev) { if (accel_dev->heartbeat) adf_heartbeat_clean(accel_dev); accel_dev->heartbeat = malloc(sizeof(*accel_dev->heartbeat), M_QAT, M_WAITOK | M_ZERO); return 0; } void adf_heartbeat_clean(struct adf_accel_dev *accel_dev) { free(accel_dev->heartbeat, M_QAT); accel_dev->heartbeat = NULL; } int adf_get_hb_timer(struct adf_accel_dev *accel_dev, unsigned int *value) { struct adf_hw_device_data *hw_data = accel_dev->hw_device; char timer_str[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { 0 }; unsigned int timer_val = ADF_CFG_HB_DEFAULT_VALUE; u32 clk_per_sec = 0; /* HB clock may be different than AE clock */ if (hw_data->get_hb_clock) { clk_per_sec = (u32)hw_data->get_hb_clock(hw_data); } else if (hw_data->get_ae_clock) { clk_per_sec = (u32)hw_data->get_ae_clock(hw_data); } else { return EINVAL; } /* Get Heartbeat Timer value from the configuration */ if (!adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC, ADF_HEARTBEAT_TIMER, (char *)timer_str)) { if (compat_strtouint((char *)timer_str, ADF_CFG_BASE_DEC, &timer_val)) timer_val = ADF_CFG_HB_DEFAULT_VALUE; } if (timer_val < ADF_MIN_HB_TIMER_MS) { device_printf(GET_DEV(accel_dev), "%s value cannot be lesser than %u\n", ADF_HEARTBEAT_TIMER, ADF_MIN_HB_TIMER_MS); return EINVAL; } /* Convert msec to clocks */ clk_per_sec = clk_per_sec / 1000; *value = timer_val * clk_per_sec; return 0; } int adf_get_heartbeat_status(struct adf_accel_dev *accel_dev) { struct icp_qat_fw_init_admin_hb_cnt *live_s, *last_s, *curr_s; struct adf_hw_device_data *hw_device = accel_dev->hw_device; const size_t max_aes = hw_device->get_num_aes(hw_device); const size_t hb_ctrs = hw_device->heartbeat_ctr_num; const size_t stats_size = max_aes * hb_ctrs * sizeof(struct icp_qat_fw_init_admin_hb_cnt); int ret = 0; size_t ae, thr; u16 *count_s; unsigned long ae_mask = 0; /* * Memory layout of Heartbeat * * +----------------+----------------+---------+ * | Live value | Last value | Count | * +----------------+----------------+---------+ * \_______________/\_______________/\________/ * ^ ^ ^ * | | | * | | max_aes * hb_ctrs * * | | sizeof(u16) * | | * | max_aes * hb_ctrs * * | sizeof(icp_qat_fw_init_admin_hb_cnt) * | * max_aes * hb_ctrs * * sizeof(icp_qat_fw_init_admin_hb_cnt) */ live_s = (struct icp_qat_fw_init_admin_hb_cnt *) accel_dev->admin->virt_hb_addr; last_s = live_s + (max_aes * hb_ctrs); count_s = (u16 *)(last_s + (max_aes * hb_ctrs)); curr_s = malloc(stats_size, M_QAT, M_WAITOK | M_ZERO); memcpy(curr_s, live_s, stats_size); ae_mask = hw_device->ae_mask; for_each_set_bit(ae, &ae_mask, max_aes) { struct icp_qat_fw_init_admin_hb_cnt *curr = curr_s + ae * hb_ctrs; struct icp_qat_fw_init_admin_hb_cnt *prev = last_s + ae * hb_ctrs; u16 *count = count_s + ae * hb_ctrs; for (thr = 0; thr < hb_ctrs; ++thr) { u16 req = curr[thr].req_heartbeat_cnt; u16 resp = curr[thr].resp_heartbeat_cnt; u16 last = prev[thr].resp_heartbeat_cnt; if ((thr == ADF_AE_ADMIN_THREAD || req != resp) && resp == last) { u16 retry = ++count[thr]; if (retry >= ADF_CFG_HB_COUNT_THRESHOLD) ret = EIO; } else { count[thr] = 0; } } } /* Copy current stats for the next iteration */ memcpy(last_s, curr_s, stats_size); free(curr_s, M_QAT); return ret; } int adf_heartbeat_status(struct adf_accel_dev *accel_dev, enum adf_device_heartbeat_status *hb_status) { /* Heartbeat is not implemented in VFs at the moment so they do not * set get_heartbeat_status. Also, in case the device is not up, * unsupported should be returned */ if (!accel_dev || !accel_dev->hw_device || !accel_dev->hw_device->get_heartbeat_status || !accel_dev->heartbeat) { *hb_status = DEV_HB_UNSUPPORTED; return 0; } if (!adf_dev_started(accel_dev) || test_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) { *hb_status = DEV_HB_UNRESPONSIVE; accel_dev->heartbeat->last_hb_status = DEV_HB_UNRESPONSIVE; return 0; } if (adf_check_hb_poll_freq(accel_dev) == EINVAL) { *hb_status = accel_dev->heartbeat->last_hb_status; return 0; } accel_dev->heartbeat->hb_sent_counter++; if (unlikely(accel_dev->hw_device->get_heartbeat_status(accel_dev))) { device_printf(GET_DEV(accel_dev), "ERROR: QAT is not responding.\n"); *hb_status = DEV_HB_UNRESPONSIVE; accel_dev->heartbeat->last_hb_status = DEV_HB_UNRESPONSIVE; accel_dev->heartbeat->hb_failed_counter++; return adf_notify_fatal_error(accel_dev); } *hb_status = DEV_HB_ALIVE; accel_dev->heartbeat->last_hb_status = DEV_HB_ALIVE; return 0; }