// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2023 Intel Corporation. */ #define dev_fmt(fmt) "Telemetry: " fmt #include #include #include #include #include #include #include #include #include #include #include #include "adf_admin.h" #include "adf_accel_devices.h" #include "adf_common_drv.h" #include "adf_telemetry.h" #define TL_IS_ZERO(input) ((input) == 0) static bool is_tl_supported(struct adf_accel_dev *accel_dev) { u16 fw_caps = GET_HW_DATA(accel_dev)->fw_capabilities; return fw_caps & TL_CAPABILITY_BIT; } static int validate_tl_data(struct adf_tl_hw_data *tl_data) { if (!tl_data->dev_counters || TL_IS_ZERO(tl_data->num_dev_counters) || !tl_data->sl_util_counters || !tl_data->sl_exec_counters || !tl_data->rp_counters || TL_IS_ZERO(tl_data->num_rp_counters)) return -EOPNOTSUPP; return 0; } static int validate_tl_slice_counters(struct icp_qat_fw_init_admin_slice_cnt *slice_count, u8 max_slices_per_type) { u8 *sl_counter = (u8 *)slice_count; int i; for (i = 0; i < ADF_TL_SL_CNT_COUNT; i++) { if (sl_counter[i] > max_slices_per_type) return -EINVAL; } return 0; } static int adf_tl_alloc_mem(struct adf_accel_dev *accel_dev) { struct adf_tl_hw_data *tl_data = &GET_TL_DATA(accel_dev); struct device *dev = &GET_DEV(accel_dev); size_t regs_sz = tl_data->layout_sz; struct adf_telemetry *telemetry; int node = dev_to_node(dev); void *tl_data_regs; unsigned int i; telemetry = kzalloc_node(sizeof(*telemetry), GFP_KERNEL, node); if (!telemetry) return -ENOMEM; telemetry->rp_num_indexes = kmalloc_array(tl_data->max_rp, sizeof(*telemetry->rp_num_indexes), GFP_KERNEL); if (!telemetry->rp_num_indexes) goto err_free_tl; telemetry->regs_hist_buff = kmalloc_array(tl_data->num_hbuff, sizeof(*telemetry->regs_hist_buff), GFP_KERNEL); if (!telemetry->regs_hist_buff) goto err_free_rp_indexes; telemetry->regs_data = dma_alloc_coherent(dev, regs_sz, &telemetry->regs_data_p, GFP_KERNEL); if (!telemetry->regs_data) goto err_free_regs_hist_buff; for (i = 0; i < tl_data->num_hbuff; i++) { tl_data_regs = kzalloc_node(regs_sz, GFP_KERNEL, node); if (!tl_data_regs) goto err_free_dma; telemetry->regs_hist_buff[i] = tl_data_regs; } accel_dev->telemetry = telemetry; return 0; err_free_dma: dma_free_coherent(dev, regs_sz, telemetry->regs_data, telemetry->regs_data_p); while (i--) kfree(telemetry->regs_hist_buff[i]); err_free_regs_hist_buff: kfree(telemetry->regs_hist_buff); err_free_rp_indexes: kfree(telemetry->rp_num_indexes); err_free_tl: kfree(telemetry); return -ENOMEM; } static void adf_tl_free_mem(struct adf_accel_dev *accel_dev) { struct adf_tl_hw_data *tl_data = &GET_TL_DATA(accel_dev); struct adf_telemetry *telemetry = accel_dev->telemetry; struct device *dev = &GET_DEV(accel_dev); size_t regs_sz = tl_data->layout_sz; unsigned int i; for (i = 0; i < tl_data->num_hbuff; i++) kfree(telemetry->regs_hist_buff[i]); dma_free_coherent(dev, regs_sz, telemetry->regs_data, telemetry->regs_data_p); kfree(telemetry->regs_hist_buff); kfree(telemetry->rp_num_indexes); kfree(telemetry); accel_dev->telemetry = NULL; } static unsigned long get_next_timeout(void) { return msecs_to_jiffies(ADF_TL_TIMER_INT_MS); } static void snapshot_regs(struct adf_telemetry *telemetry, size_t size) { void *dst = telemetry->regs_hist_buff[telemetry->hb_num]; void *src = telemetry->regs_data; memcpy(dst, src, size); } static void tl_work_handler(struct work_struct *work) { struct delayed_work *delayed_work; struct adf_telemetry *telemetry; struct adf_tl_hw_data *tl_data; u32 msg_cnt, old_msg_cnt; size_t layout_sz; u32 *regs_data; size_t id; delayed_work = to_delayed_work(work); telemetry = container_of(delayed_work, struct adf_telemetry, work_ctx); tl_data = &GET_TL_DATA(telemetry->accel_dev); regs_data = telemetry->regs_data; id = tl_data->msg_cnt_off / sizeof(*regs_data); layout_sz = tl_data->layout_sz; if (!atomic_read(&telemetry->state)) { cancel_delayed_work_sync(&telemetry->work_ctx); return; } msg_cnt = regs_data[id]; old_msg_cnt = msg_cnt; if (msg_cnt == telemetry->msg_cnt) goto out; mutex_lock(&telemetry->regs_hist_lock); snapshot_regs(telemetry, layout_sz); /* Check if data changed while updating it */ msg_cnt = regs_data[id]; if (old_msg_cnt != msg_cnt) snapshot_regs(telemetry, layout_sz); telemetry->msg_cnt = msg_cnt; telemetry->hb_num++; telemetry->hb_num %= telemetry->hbuffs; mutex_unlock(&telemetry->regs_hist_lock); out: adf_misc_wq_queue_delayed_work(&telemetry->work_ctx, get_next_timeout()); } int adf_tl_halt(struct adf_accel_dev *accel_dev) { struct adf_telemetry *telemetry = accel_dev->telemetry; struct device *dev = &GET_DEV(accel_dev); int ret; cancel_delayed_work_sync(&telemetry->work_ctx); atomic_set(&telemetry->state, 0); ret = adf_send_admin_tl_stop(accel_dev); if (ret) dev_err(dev, "failed to stop telemetry\n"); return ret; } int adf_tl_run(struct adf_accel_dev *accel_dev, int state) { struct adf_tl_hw_data *tl_data = &GET_TL_DATA(accel_dev); struct adf_telemetry *telemetry = accel_dev->telemetry; struct device *dev = &GET_DEV(accel_dev); size_t layout_sz = tl_data->layout_sz; int ret; ret = adf_send_admin_tl_start(accel_dev, telemetry->regs_data_p, layout_sz, telemetry->rp_num_indexes, &telemetry->slice_cnt); if (ret) { dev_err(dev, "failed to start telemetry\n"); return ret; } ret = validate_tl_slice_counters(&telemetry->slice_cnt, tl_data->max_sl_cnt); if (ret) { dev_err(dev, "invalid value returned by FW\n"); adf_send_admin_tl_stop(accel_dev); return ret; } telemetry->hbuffs = state; atomic_set(&telemetry->state, state); adf_misc_wq_queue_delayed_work(&telemetry->work_ctx, get_next_timeout()); return 0; } int adf_tl_init(struct adf_accel_dev *accel_dev) { struct adf_tl_hw_data *tl_data = &GET_TL_DATA(accel_dev); u8 max_rp = GET_TL_DATA(accel_dev).max_rp; struct device *dev = &GET_DEV(accel_dev); struct adf_telemetry *telemetry; unsigned int i; int ret; ret = validate_tl_data(tl_data); if (ret) return ret; ret = adf_tl_alloc_mem(accel_dev); if (ret) { dev_err(dev, "failed to initialize: %d\n", ret); return ret; } telemetry = accel_dev->telemetry; telemetry->accel_dev = accel_dev; mutex_init(&telemetry->wr_lock); mutex_init(&telemetry->regs_hist_lock); INIT_DELAYED_WORK(&telemetry->work_ctx, tl_work_handler); for (i = 0; i < max_rp; i++) telemetry->rp_num_indexes[i] = ADF_TL_RP_REGS_DISABLED; return 0; } int adf_tl_start(struct adf_accel_dev *accel_dev) { struct device *dev = &GET_DEV(accel_dev); if (!accel_dev->telemetry) return -EOPNOTSUPP; if (!is_tl_supported(accel_dev)) { dev_info(dev, "feature not supported by FW\n"); adf_tl_free_mem(accel_dev); return -EOPNOTSUPP; } return 0; } void adf_tl_stop(struct adf_accel_dev *accel_dev) { if (!accel_dev->telemetry) return; if (atomic_read(&accel_dev->telemetry->state)) adf_tl_halt(accel_dev); } void adf_tl_shutdown(struct adf_accel_dev *accel_dev) { if (!accel_dev->telemetry) return; adf_tl_free_mem(accel_dev); }