net/mlx5: Add support for FW reporter dump
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / diag / fw_tracer.c
index 6999f4486e9ec786424be5dd9f72d91e333c0086..8a4930c8bf6214118d7782e2607e5b1fc8a21a4b 100644 (file)
@@ -243,6 +243,19 @@ free_strings_db:
        return -ENOMEM;
 }
 
+static void
+mlx5_fw_tracer_init_saved_traces_array(struct mlx5_fw_tracer *tracer)
+{
+       tracer->st_arr.saved_traces_index = 0;
+       mutex_init(&tracer->st_arr.lock);
+}
+
+static void
+mlx5_fw_tracer_clean_saved_traces_array(struct mlx5_fw_tracer *tracer)
+{
+       mutex_destroy(&tracer->st_arr.lock);
+}
+
 static void mlx5_tracer_read_strings_db(struct work_struct *work)
 {
        struct mlx5_fw_tracer *tracer = container_of(work, struct mlx5_fw_tracer,
@@ -522,6 +535,24 @@ static void mlx5_fw_tracer_clean_ready_list(struct mlx5_fw_tracer *tracer)
                list_del(&str_frmt->list);
 }
 
+static void mlx5_fw_tracer_save_trace(struct mlx5_fw_tracer *tracer,
+                                     u64 timestamp, bool lost,
+                                     u8 event_id, char *msg)
+{
+       struct mlx5_fw_trace_data *trace_data;
+
+       mutex_lock(&tracer->st_arr.lock);
+       trace_data = &tracer->st_arr.straces[tracer->st_arr.saved_traces_index];
+       trace_data->timestamp = timestamp;
+       trace_data->lost = lost;
+       trace_data->event_id = event_id;
+       strncpy(trace_data->msg, msg, TRACE_STR_MSG);
+
+       tracer->st_arr.saved_traces_index =
+               (tracer->st_arr.saved_traces_index + 1) & (SAVED_TRACES_NUM - 1);
+       mutex_unlock(&tracer->st_arr.lock);
+}
+
 static void mlx5_tracer_print_trace(struct tracer_string_format *str_frmt,
                                    struct mlx5_core_dev *dev,
                                    u64 trace_timestamp)
@@ -540,6 +571,9 @@ static void mlx5_tracer_print_trace(struct tracer_string_format *str_frmt,
        trace_mlx5_fw(dev->tracer, trace_timestamp, str_frmt->lost,
                      str_frmt->event_id, tmp);
 
+       mlx5_fw_tracer_save_trace(dev->tracer, trace_timestamp,
+                                 str_frmt->lost, str_frmt->event_id, tmp);
+
        /* remove it from hash */
        mlx5_tracer_clean_message(str_frmt);
 }
@@ -786,6 +820,109 @@ static void mlx5_fw_tracer_ownership_change(struct work_struct *work)
        mlx5_fw_tracer_start(tracer);
 }
 
+static int mlx5_fw_tracer_set_core_dump_reg(struct mlx5_core_dev *dev,
+                                           u32 *in, int size_in)
+{
+       u32 out[MLX5_ST_SZ_DW(core_dump_reg)] = {};
+
+       if (!MLX5_CAP_DEBUG(dev, core_dump_general) &&
+           !MLX5_CAP_DEBUG(dev, core_dump_qp))
+               return -EOPNOTSUPP;
+
+       return mlx5_core_access_reg(dev, in, size_in, out, sizeof(out),
+                                   MLX5_REG_CORE_DUMP, 0, 1);
+}
+
+int mlx5_fw_tracer_trigger_core_dump_general(struct mlx5_core_dev *dev)
+{
+       struct mlx5_fw_tracer *tracer = dev->tracer;
+       u32 in[MLX5_ST_SZ_DW(core_dump_reg)] = {};
+       int err;
+
+       if (!MLX5_CAP_DEBUG(dev, core_dump_general) || !tracer)
+               return -EOPNOTSUPP;
+       if (!tracer->owner)
+               return -EPERM;
+
+       MLX5_SET(core_dump_reg, in, core_dump_type, 0x0);
+
+       err =  mlx5_fw_tracer_set_core_dump_reg(dev, in, sizeof(in));
+       if (err)
+               return err;
+       queue_work(tracer->work_queue, &tracer->handle_traces_work);
+       flush_workqueue(tracer->work_queue);
+       return 0;
+}
+
+static int
+mlx5_devlink_fmsg_fill_trace(struct devlink_fmsg *fmsg,
+                            struct mlx5_fw_trace_data *trace_data)
+{
+       int err;
+
+       err = devlink_fmsg_obj_nest_start(fmsg);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_u64_pair_put(fmsg, "timestamp", trace_data->timestamp);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_bool_pair_put(fmsg, "lost", trace_data->lost);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_u8_pair_put(fmsg, "event_id", trace_data->event_id);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_string_pair_put(fmsg, "msg", trace_data->msg);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_obj_nest_end(fmsg);
+       if (err)
+               return err;
+       return 0;
+}
+
+int mlx5_fw_tracer_get_saved_traces_objects(struct mlx5_fw_tracer *tracer,
+                                           struct devlink_fmsg *fmsg)
+{
+       struct mlx5_fw_trace_data *straces = tracer->st_arr.straces;
+       u32 index, start_index, end_index;
+       u32 saved_traces_index;
+       int err;
+
+       if (!straces[0].timestamp)
+               return -ENOMSG;
+
+       mutex_lock(&tracer->st_arr.lock);
+       saved_traces_index = tracer->st_arr.saved_traces_index;
+       if (straces[saved_traces_index].timestamp)
+               start_index = saved_traces_index;
+       else
+               start_index = 0;
+       end_index = (saved_traces_index - 1) & (SAVED_TRACES_NUM - 1);
+
+       err = devlink_fmsg_arr_pair_nest_start(fmsg, "dump fw traces");
+       if (err)
+               goto unlock;
+       index = start_index;
+       while (index != end_index) {
+               err = mlx5_devlink_fmsg_fill_trace(fmsg, &straces[index]);
+               if (err)
+                       goto unlock;
+
+               index = (index + 1) & (SAVED_TRACES_NUM - 1);
+       }
+
+       err = devlink_fmsg_arr_pair_nest_end(fmsg);
+unlock:
+       mutex_unlock(&tracer->st_arr.lock);
+       return err;
+}
+
 /* Create software resources (Buffers, etc ..) */
 struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
 {
@@ -833,6 +970,7 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
                goto free_log_buf;
        }
 
+       mlx5_fw_tracer_init_saved_traces_array(tracer);
        mlx5_core_dbg(dev, "FWTracer: Tracer created\n");
 
        return tracer;
@@ -917,6 +1055,7 @@ void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer)
        cancel_work_sync(&tracer->read_fw_strings_work);
        mlx5_fw_tracer_clean_ready_list(tracer);
        mlx5_fw_tracer_clean_print_hash(tracer);
+       mlx5_fw_tracer_clean_saved_traces_array(tracer);
        mlx5_fw_tracer_free_strings_db(tracer);
        mlx5_fw_tracer_destroy_log_buf(tracer);
        flush_workqueue(tracer->work_queue);