struct list_head param_list;
        struct list_head region_list;
        u32 snapshot_id;
+       struct list_head reporter_list;
        struct devlink_dpipe_headers *dpipe_headers;
        const struct devlink_ops *ops;
        struct device *dev;
 typedef void devlink_snapshot_data_dest_t(const void *data);
 
 struct devlink_fmsg;
+struct devlink_health_reporter;
+
+/**
+ * struct devlink_health_reporter_ops - Reporter operations
+ * @name: reporter name
+ * @recover: callback to recover from reported error
+ *           if priv_ctx is NULL, run a full recover
+ * @dump: callback to dump an object
+ *        if priv_ctx is NULL, run a full dump
+ * @diagnose: callback to diagnose the current status
+ */
+
+struct devlink_health_reporter_ops {
+       char *name;
+       int (*recover)(struct devlink_health_reporter *reporter,
+                      void *priv_ctx);
+       int (*dump)(struct devlink_health_reporter *reporter,
+                   struct devlink_fmsg *fmsg, void *priv_ctx);
+       int (*diagnose)(struct devlink_health_reporter *reporter,
+                       struct devlink_fmsg *fmsg);
+};
 
 struct devlink_ops {
        int (*reload)(struct devlink *devlink, struct netlink_ext_ack *extack);
 int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
                                 const void *value, u16 value_len);
 
+struct devlink_health_reporter *
+devlink_health_reporter_create(struct devlink *devlink,
+                              const struct devlink_health_reporter_ops *ops,
+                              u64 graceful_period, bool auto_recover,
+                              void *priv);
+void
+devlink_health_reporter_destroy(struct devlink_health_reporter *reporter);
+
+void *
+devlink_health_reporter_priv(struct devlink_health_reporter *reporter);
+
 #else
 
 static inline struct devlink *devlink_alloc(const struct devlink_ops *ops,
 {
        return 0;
 }
+
+static inline struct devlink_health_reporter *
+devlink_health_reporter_create(struct devlink *devlink,
+                              const struct devlink_health_reporter_ops *ops,
+                              u64 graceful_period, bool auto_recover,
+                              void *priv)
+{
+       return NULL;
+}
+
+static inline void
+devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
+{
+}
+
+static inline void *
+devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
+{
+       return NULL;
+}
 #endif
 
 #if IS_REACHABLE(CONFIG_NET_DEVLINK)
 
        return err;
 }
 
+struct devlink_health_reporter {
+       struct list_head list;
+       void *priv;
+       const struct devlink_health_reporter_ops *ops;
+       struct devlink *devlink;
+       u64 graceful_period;
+       bool auto_recover;
+       u8 health_state;
+};
+
+void *
+devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
+{
+       return reporter->priv;
+}
+EXPORT_SYMBOL_GPL(devlink_health_reporter_priv);
+
+static struct devlink_health_reporter *
+devlink_health_reporter_find_by_name(struct devlink *devlink,
+                                    const char *reporter_name)
+{
+       struct devlink_health_reporter *reporter;
+
+       list_for_each_entry(reporter, &devlink->reporter_list, list)
+               if (!strcmp(reporter->ops->name, reporter_name))
+                       return reporter;
+       return NULL;
+}
+
+/**
+ *     devlink_health_reporter_create - create devlink health reporter
+ *
+ *     @devlink: devlink
+ *     @ops: ops
+ *     @graceful_period: to avoid recovery loops, in msecs
+ *     @auto_recover: auto recover when error occurs
+ *     @priv: priv
+ */
+struct devlink_health_reporter *
+devlink_health_reporter_create(struct devlink *devlink,
+                              const struct devlink_health_reporter_ops *ops,
+                              u64 graceful_period, bool auto_recover,
+                              void *priv)
+{
+       struct devlink_health_reporter *reporter;
+
+       mutex_lock(&devlink->lock);
+       if (devlink_health_reporter_find_by_name(devlink, ops->name)) {
+               reporter = ERR_PTR(-EEXIST);
+               goto unlock;
+       }
+
+       if (WARN_ON(auto_recover && !ops->recover) ||
+           WARN_ON(graceful_period && !ops->recover)) {
+               reporter = ERR_PTR(-EINVAL);
+               goto unlock;
+       }
+
+       reporter = kzalloc(sizeof(*reporter), GFP_KERNEL);
+       if (!reporter) {
+               reporter = ERR_PTR(-ENOMEM);
+               goto unlock;
+       }
+
+       reporter->priv = priv;
+       reporter->ops = ops;
+       reporter->devlink = devlink;
+       reporter->graceful_period = graceful_period;
+       reporter->auto_recover = auto_recover;
+       list_add_tail(&reporter->list, &devlink->reporter_list);
+unlock:
+       mutex_unlock(&devlink->lock);
+       return reporter;
+}
+EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
+
+/**
+ *     devlink_health_reporter_destroy - destroy devlink health reporter
+ *
+ *     @reporter: devlink health reporter to destroy
+ */
+void
+devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
+{
+       mutex_lock(&reporter->devlink->lock);
+       list_del(&reporter->list);
+       mutex_unlock(&reporter->devlink->lock);
+       kfree(reporter);
+}
+EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
+
 static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
        [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
        [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
        INIT_LIST_HEAD(&devlink->resource_list);
        INIT_LIST_HEAD(&devlink->param_list);
        INIT_LIST_HEAD(&devlink->region_list);
+       INIT_LIST_HEAD(&devlink->reporter_list);
        mutex_init(&devlink->lock);
        return devlink;
 }