linux/drivers/net/ethernet/qlogic/qed/qed_devlink.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* Marvell/Qlogic FastLinQ NIC driver
   3 *
   4 * Copyright (C) 2020 Marvell International Ltd.
   5 */
   6
   7#include <linux/kernel.h>
   8#include <linux/qed/qed_if.h>
   9#include <linux/vmalloc.h>
  10#include "qed.h"
  11#include "qed_devlink.h"
  12
  13enum qed_devlink_param_id {
  14        QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
  15        QED_DEVLINK_PARAM_ID_IWARP_CMT,
  16};
  17
  18struct qed_fw_fatal_ctx {
  19        enum qed_hw_err_type err_type;
  20};
  21
  22int qed_report_fatal_error(struct devlink *devlink, enum qed_hw_err_type err_type)
  23{
  24        struct qed_devlink *qdl = devlink_priv(devlink);
  25        struct qed_fw_fatal_ctx fw_fatal_ctx = {
  26                .err_type = err_type,
  27        };
  28
  29        if (qdl->fw_reporter)
  30                devlink_health_report(qdl->fw_reporter,
  31                                      "Fatal error occurred", &fw_fatal_ctx);
  32
  33        return 0;
  34}
  35
  36static int
  37qed_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter,
  38                           struct devlink_fmsg *fmsg, void *priv_ctx,
  39                           struct netlink_ext_ack *extack)
  40{
  41        struct qed_devlink *qdl = devlink_health_reporter_priv(reporter);
  42        struct qed_fw_fatal_ctx *fw_fatal_ctx = priv_ctx;
  43        struct qed_dev *cdev = qdl->cdev;
  44        u32 dbg_data_buf_size;
  45        u8 *p_dbg_data_buf;
  46        int err;
  47
  48        /* Having context means that was a dump request after fatal,
  49         * so we enable extra debugging while gathering the dump,
  50         * just in case
  51         */
  52        cdev->print_dbg_data = fw_fatal_ctx ? true : false;
  53
  54        dbg_data_buf_size = qed_dbg_all_data_size(cdev);
  55        p_dbg_data_buf = vzalloc(dbg_data_buf_size);
  56        if (!p_dbg_data_buf) {
  57                DP_NOTICE(cdev,
  58                          "Failed to allocate memory for a debug data buffer\n");
  59                return -ENOMEM;
  60        }
  61
  62        err = qed_dbg_all_data(cdev, p_dbg_data_buf);
  63        if (err) {
  64                DP_NOTICE(cdev, "Failed to obtain debug data\n");
  65                vfree(p_dbg_data_buf);
  66                return err;
  67        }
  68
  69        err = devlink_fmsg_binary_pair_put(fmsg, "dump_data",
  70                                           p_dbg_data_buf, dbg_data_buf_size);
  71
  72        vfree(p_dbg_data_buf);
  73
  74        return err;
  75}
  76
  77static int
  78qed_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter,
  79                              void *priv_ctx,
  80                              struct netlink_ext_ack *extack)
  81{
  82        struct qed_devlink *qdl = devlink_health_reporter_priv(reporter);
  83        struct qed_dev *cdev = qdl->cdev;
  84
  85        qed_recovery_process(cdev);
  86
  87        return 0;
  88}
  89
  90static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = {
  91                .name = "fw_fatal",
  92                .recover = qed_fw_fatal_reporter_recover,
  93                .dump = qed_fw_fatal_reporter_dump,
  94};
  95
  96#define QED_REPORTER_FW_GRACEFUL_PERIOD 0
  97
  98void qed_fw_reporters_create(struct devlink *devlink)
  99{
 100        struct qed_devlink *dl = devlink_priv(devlink);
 101
 102        dl->fw_reporter = devlink_health_reporter_create(devlink, &qed_fw_fatal_reporter_ops,
 103                                                         QED_REPORTER_FW_GRACEFUL_PERIOD, dl);
 104        if (IS_ERR(dl->fw_reporter)) {
 105                DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n",
 106                          PTR_ERR(dl->fw_reporter));
 107                dl->fw_reporter = NULL;
 108        }
 109}
 110
 111void qed_fw_reporters_destroy(struct devlink *devlink)
 112{
 113        struct qed_devlink *dl = devlink_priv(devlink);
 114        struct devlink_health_reporter *rep;
 115
 116        rep = dl->fw_reporter;
 117
 118        if (!IS_ERR_OR_NULL(rep))
 119                devlink_health_reporter_destroy(rep);
 120}
 121
 122static int qed_dl_param_get(struct devlink *dl, u32 id,
 123                            struct devlink_param_gset_ctx *ctx)
 124{
 125        struct qed_devlink *qed_dl = devlink_priv(dl);
 126        struct qed_dev *cdev;
 127
 128        cdev = qed_dl->cdev;
 129        ctx->val.vbool = cdev->iwarp_cmt;
 130
 131        return 0;
 132}
 133
 134static int qed_dl_param_set(struct devlink *dl, u32 id,
 135                            struct devlink_param_gset_ctx *ctx)
 136{
 137        struct qed_devlink *qed_dl = devlink_priv(dl);
 138        struct qed_dev *cdev;
 139
 140        cdev = qed_dl->cdev;
 141        cdev->iwarp_cmt = ctx->val.vbool;
 142
 143        return 0;
 144}
 145
 146static const struct devlink_param qed_devlink_params[] = {
 147        DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT,
 148                             "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL,
 149                             BIT(DEVLINK_PARAM_CMODE_RUNTIME),
 150                             qed_dl_param_get, qed_dl_param_set, NULL),
 151};
 152
 153static int qed_devlink_info_get(struct devlink *devlink,
 154                                struct devlink_info_req *req,
 155                                struct netlink_ext_ack *extack)
 156{
 157        struct qed_devlink *qed_dl = devlink_priv(devlink);
 158        struct qed_dev *cdev = qed_dl->cdev;
 159        struct qed_dev_info *dev_info;
 160        char buf[100];
 161        int err;
 162
 163        dev_info = &cdev->common_dev_info;
 164
 165        err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
 166        if (err)
 167                return err;
 168
 169        memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
 170        buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
 171
 172        if (buf[0]) {
 173                err = devlink_info_board_serial_number_put(req, buf);
 174                if (err)
 175                        return err;
 176        }
 177
 178        snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
 179                 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3),
 180                 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2),
 181                 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1),
 182                 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0));
 183
 184        err = devlink_info_version_stored_put(req,
 185                                              DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
 186        if (err)
 187                return err;
 188
 189        snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
 190                 dev_info->fw_major,
 191                 dev_info->fw_minor,
 192                 dev_info->fw_rev,
 193                 dev_info->fw_eng);
 194
 195        return devlink_info_version_running_put(req,
 196                                                DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf);
 197}
 198
 199static const struct devlink_ops qed_dl_ops = {
 200        .info_get = qed_devlink_info_get,
 201};
 202
 203struct devlink *qed_devlink_register(struct qed_dev *cdev)
 204{
 205        union devlink_param_value value;
 206        struct qed_devlink *qdevlink;
 207        struct devlink *dl;
 208        int rc;
 209
 210        dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink),
 211                           &cdev->pdev->dev);
 212        if (!dl)
 213                return ERR_PTR(-ENOMEM);
 214
 215        qdevlink = devlink_priv(dl);
 216        qdevlink->cdev = cdev;
 217
 218        rc = devlink_register(dl);
 219        if (rc)
 220                goto err_free;
 221
 222        rc = devlink_params_register(dl, qed_devlink_params,
 223                                     ARRAY_SIZE(qed_devlink_params));
 224        if (rc)
 225                goto err_unregister;
 226
 227        value.vbool = false;
 228        devlink_param_driverinit_value_set(dl,
 229                                           QED_DEVLINK_PARAM_ID_IWARP_CMT,
 230                                           value);
 231
 232        devlink_params_publish(dl);
 233        cdev->iwarp_cmt = false;
 234
 235        qed_fw_reporters_create(dl);
 236
 237        return dl;
 238
 239err_unregister:
 240        devlink_unregister(dl);
 241
 242err_free:
 243        devlink_free(dl);
 244
 245        return ERR_PTR(rc);
 246}
 247
 248void qed_devlink_unregister(struct devlink *devlink)
 249{
 250        if (!devlink)
 251                return;
 252
 253        qed_fw_reporters_destroy(devlink);
 254
 255        devlink_params_unregister(devlink, qed_devlink_params,
 256                                  ARRAY_SIZE(qed_devlink_params));
 257
 258        devlink_unregister(devlink);
 259        devlink_free(devlink);
 260}
 261