Loading drivers/gpu/drm/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ msm_drm-y := \ sde/sde_color_processing.o \ sde/sde_vbif.o \ sde/sde_splash.o \ sde/sde_recovery_manager.o \ sde_dbg.o \ sde_dbg_evtlog.o \ sde_io_util.o \ Loading drivers/gpu/drm/msm/sde/sde_encoder.c +2 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/seq_file.h> #include "msm_drv.h" #include "sde_recovery_manager.h" #include "sde_kms.h" #include "drm_crtc.h" #include "drm_crtc_helper.h" Loading Loading @@ -603,6 +604,7 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, atomic_inc(&phy_enc->underrun_cnt); SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); sde_recovery_set_events(SDE_UNDERRUN); trace_sde_encoder_underrun(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); SDE_DBG_CTRL("stop_ftrace"); Loading drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +3 −1 Original line number Diff line number Diff line /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -11,6 +11,7 @@ */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ #include "sde_recovery_manager.h" #include "sde_encoder_phys.h" #include "sde_hw_interrupts.h" #include "sde_core_irq.h" Loading Loading @@ -704,6 +705,7 @@ static int sde_encoder_phys_vid_wait_for_vblank( SDE_EVT32(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0); SDE_ERROR_VIDENC(vid_enc, "kickoff timed out\n"); sde_recovery_set_events(SDE_VSYNC_MISS); if (notify && phys_enc->parent_ops.handle_frame_done) phys_enc->parent_ops.handle_frame_done( phys_enc->parent, phys_enc, Loading drivers/gpu/drm/msm/sde/sde_kms.c +52 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ #include "sde_encoder.h" #include "sde_plane.h" #include "sde_crtc.h" #include "sde_recovery_manager.h" #define CREATE_TRACE_POINTS #include "sde_trace.h" Loading @@ -58,6 +59,19 @@ #define SDE_DEBUGFS_DIR "msm_sde" #define SDE_DEBUGFS_HWMASKNAME "hw_log_mask" static int sde_kms_recovery_callback(int err_code, struct recovery_client_info *client_info); static struct recovery_client_info info = { .name = "sde_kms", .recovery_cb = sde_kms_recovery_callback, .err_supported[0] = {SDE_UNDERRUN, 0, 0}, .err_supported[1] = {SDE_VSYNC_MISS, 0, 0}, .no_of_err = 2, .handle = NULL, .pdata = NULL, }; /** * sdecustom - enable certain driver customizations for sde clients * Enabling this modifies the standard DRM behavior slightly and assumes Loading Loading @@ -1062,6 +1076,8 @@ static void sde_kms_destroy(struct msm_kms *kms) return; } sde_recovery_client_unregister(info.handle); info.handle = NULL; _sde_kms_hw_destroy(sde_kms, dev->platformdev); kfree(sde_kms); } Loading Loading @@ -1264,6 +1280,11 @@ static int sde_kms_hw_init(struct msm_kms *kms) goto end; } rc = sde_recovery_client_register(&info); if (rc) pr_err("%s recovery mgr register failed %d\n", __func__, rc); sde_kms = to_sde_kms(kms); dev = sde_kms->dev; if (!dev || !dev->platformdev) { Loading Loading @@ -1487,10 +1508,34 @@ end: return rc; } static int sde_kms_recovery_callback(int err_code, struct recovery_client_info *client_info) { int rc = 0; switch (err_code) { case SDE_UNDERRUN: pr_debug("%s [SDE_UNDERRUN] error is auto HW receovered\n", __func__); break; case SDE_VSYNC_MISS: pr_debug("%s [SDE_VSYNC_MISS] trigger soft reset\n", __func__); break; default: pr_err("%s error %d undefined\n", __func__, err_code); } return rc; } struct msm_kms *sde_kms_init(struct drm_device *dev) { struct msm_drm_private *priv; struct sde_kms *sde_kms; int rc = 0; if (!dev || !dev->dev_private) { SDE_ERROR("drm device node invalid\n"); Loading @@ -1505,6 +1550,13 @@ struct msm_kms *sde_kms_init(struct drm_device *dev) return ERR_PTR(-ENOMEM); } rc = sde_init_recovery_mgr(dev); if (rc) { SDE_ERROR("Failed SDE recovery mgr Init, err = %d\n", rc); kfree(sde_kms); return ERR_PTR(-EFAULT); } msm_kms_init(&sde_kms->base, &kms_funcs); sde_kms->dev = dev; Loading drivers/gpu/drm/msm/sde/sde_recovery_manager.c 0 → 100644 +399 −0 Original line number Diff line number Diff line /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include "sde_recovery_manager.h" #include "sde_kms.h" static struct recovery_mgr_info *rec_mgr; static ssize_t sde_recovery_mgr_rda_clients_attr(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; struct list_head *pos; struct recovery_client_db *temp = NULL; mutex_lock(&rec_mgr->rec_lock); len = snprintf(buf, PAGE_SIZE, "Clients:\n"); list_for_each(pos, &rec_mgr->client_list) { temp = list_entry(pos, struct recovery_client_db, list); len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", temp->client_info.name); } mutex_unlock(&rec_mgr->rec_lock); return len; } static DEVICE_ATTR(clients, S_IRUGO, sde_recovery_mgr_rda_clients_attr, NULL); static struct attribute *recovery_attrs[] = { &dev_attr_clients.attr, NULL, }; static struct attribute_group recovery_mgr_attr_group = { .attrs = recovery_attrs, }; static void sde_recovery_mgr_notify(bool err_state) { char *envp[2]; char *uevent_str = kzalloc(SZ_4K, GFP_KERNEL); if (uevent_str == NULL) { DRM_ERROR("failed to allocate event string\n"); return; } if (err_state == true) snprintf(uevent_str, MAX_REC_UEVENT_LEN, "DISPLAY_ERROR_RECOVERED\n"); else snprintf(uevent_str, MAX_REC_UEVENT_LEN, "DISPLAY_CRITICAL_ERROR\n"); DRM_DEBUG("generating uevent [%s]\n", uevent_str); envp[0] = uevent_str; envp[1] = NULL; mutex_lock(&rec_mgr->dev->mode_config.mutex); kobject_uevent_env(&rec_mgr->dev->primary->kdev->kobj, KOBJ_CHANGE, envp); mutex_unlock(&rec_mgr->dev->mode_config.mutex); kfree(uevent_str); } static void sde_recovery_mgr_recover(int err_code) { struct list_head *pos; struct recovery_client_db *c = NULL; int tmp_err, rc, pre, post, i; bool found = false; static bool rec_flag = true; mutex_lock(&rec_mgr->rec_lock); list_for_each(pos, &rec_mgr->client_list) { c = list_entry(pos, struct recovery_client_db, list); mutex_unlock(&rec_mgr->rec_lock); for (i = 0; i < MAX_REC_ERR_SUPPORT; i++) { tmp_err = c->client_info.err_supported[i]. reported_err_code; if (tmp_err == err_code) { found = true; break; } } if (found == true) { pre = c->client_info.err_supported[i].pre_err_code; if (pre && pre != '0') sde_recovery_mgr_recover(pre); if (c->client_info.recovery_cb) { rc = c->client_info.recovery_cb(err_code, &c->client_info); if (rc) { pr_err("%s failed to recover error %d\n", __func__, err_code); rec_flag = false; } else { pr_debug("%s Recovery successful[%d]\n", __func__, err_code); } } post = c->client_info.err_supported[i].post_err_code; if (post && post != '0') sde_recovery_mgr_recover(post); } mutex_lock(&rec_mgr->rec_lock); if (found) break; } if (rec_flag) { pr_debug("%s successful full recovery\n", __func__); sde_recovery_mgr_notify(true); } mutex_unlock(&rec_mgr->rec_lock); } static void sde_recovery_mgr_event_work(struct work_struct *work) { struct list_head *pos, *q; struct recovery_event_db *temp_event; int err_code; if (!rec_mgr) { pr_err("%s recovery manager is NULL\n", __func__); return; } mutex_lock(&rec_mgr->rec_lock); list_for_each_safe(pos, q, &rec_mgr->event_list) { temp_event = list_entry(pos, struct recovery_event_db, list); err_code = temp_event->err; rec_mgr->recovery_ongoing = true; mutex_unlock(&rec_mgr->rec_lock); /* notify error */ sde_recovery_mgr_notify(false); /* recover error */ sde_recovery_mgr_recover(err_code); mutex_lock(&rec_mgr->rec_lock); list_del(pos); kfree(temp_event); } rec_mgr->recovery_ongoing = false; mutex_unlock(&rec_mgr->rec_lock); } int sde_recovery_set_events(int err) { int rc = 0; struct list_head *pos; struct recovery_event_db *temp; bool found = false; mutex_lock(&rec_mgr->rec_lock); /* check if there is same event in the list */ list_for_each(pos, &rec_mgr->event_list) { temp = list_entry(pos, struct recovery_event_db, list); if (err == temp->err) { found = true; pr_info("%s error %d is already present in list\n", __func__, err); break; } } if (!found) { temp = kzalloc(sizeof(struct recovery_event_db), GFP_KERNEL); if (!temp) { pr_err("%s out of memory\n", __func__); rc = -ENOMEM; goto out; } temp->err = err; list_add_tail(&temp->list, &rec_mgr->event_list); queue_work(rec_mgr->event_queue, &rec_mgr->event_work); } out: mutex_unlock(&rec_mgr->rec_lock); return rc; } int sde_recovery_client_register(struct recovery_client_info *client) { int rc = 0; struct list_head *pos; struct recovery_client_db *c = NULL; bool found = false; if (!rec_mgr) { pr_err("%s recovery manager is not initialized\n", __func__); return -EPERM; } if (!strlen(client->name)) { pr_err("%s client name is empty\n", __func__); return -EINVAL; } mutex_lock(&rec_mgr->rec_lock); /* check if there is same client */ list_for_each(pos, &rec_mgr->client_list) { c = list_entry(pos, struct recovery_client_db, list); if (!strcmp(c->client_info.name, client->name)) { found = true; break; } } if (!found) { c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) { pr_err("%s out of memory for client", __func__); rc = -ENOMEM; goto out; } } else { pr_err("%s client = %s is already registered\n", __func__, client->name); client->handle = c; goto out; } memcpy(&(c->client_info), client, sizeof(struct recovery_client_info)); list_add_tail(&c->list, &rec_mgr->client_list); rec_mgr->num_of_clients++; client->handle = c; out: mutex_unlock(&rec_mgr->rec_lock); return rc; } int sde_recovery_client_unregister(void *handle) { struct list_head *pos, *q, *pos1; struct recovery_client_db *temp_client; struct recovery_event_db *temp; int client_err = 0; bool found = false; bool found_pending = false; int i, rc = 0; struct recovery_client_info *client = &((struct recovery_client_db *)handle)->client_info; if (!handle) { pr_err("%s handle is NULL\n", __func__); return -EINVAL; } if (!strlen(client->name)) { pr_err("%s client name is empty\n", __func__); return -EINVAL; } mutex_lock(&rec_mgr->rec_lock); if (rec_mgr->recovery_ongoing) { pr_err("%s SDE Executing Recovery, Failed! Unregister client %s\n", __func__, client->name); goto out; } /* check if client is present in the list */ list_for_each_safe(pos, q, &rec_mgr->client_list) { temp_client = list_entry(pos, struct recovery_client_db, list); if (!strcmp(temp_client->client_info.name, client->name)) { found = true; /* free any pending event for this client */ list_for_each(pos1, &rec_mgr->event_list) { temp = list_entry(pos1, struct recovery_event_db, list); found_pending = false; for (i = 0; i < MAX_REC_ERR_SUPPORT; i++) { client_err = temp_client-> client_info.err_supported[i]. reported_err_code; if (temp->err == client_err) found_pending = true; } if (found_pending) { list_del(pos1); kfree(temp); } } list_del(pos); kfree(temp_client); rec_mgr->num_of_clients--; break; } } if (!found) { pr_err("%s can't find the client[%s] from db\n", __func__, client->name); rc = -EFAULT; } out: mutex_unlock(&rec_mgr->rec_lock); return rc; } int sde_init_recovery_mgr(struct drm_device *dev) { struct recovery_mgr_info *rec = NULL; int rc = 0; if (!dev || !dev->dev_private) { SDE_ERROR("drm device node invalid\n"); return -EINVAL; } rec = kzalloc(sizeof(struct recovery_mgr_info), GFP_KERNEL); if (!rec) return -ENOMEM; mutex_init(&rec->rec_lock); rec->dev = dev; rc = sysfs_create_group(&dev->primary->kdev->kobj, &recovery_mgr_attr_group); if (rc) { pr_err("%s sysfs_create_group fails=%d", __func__, rc); rec->sysfs_created = false; } else { rec->sysfs_created = true; } INIT_LIST_HEAD(&rec->event_list); INIT_LIST_HEAD(&rec->client_list); INIT_WORK(&rec->event_work, sde_recovery_mgr_event_work); rec->event_queue = create_workqueue("recovery_event"); if (IS_ERR_OR_NULL(rec->event_queue)) { pr_err("%s unable to create queue; errno = %ld", __func__, PTR_ERR(rec->event_queue)); rec->event_queue = NULL; rc = -EFAULT; goto err; } rec_mgr = rec; return rc; err: mutex_destroy(&rec->rec_lock); if (rec->sysfs_created) sysfs_remove_group(&rec_mgr->dev->primary->kdev->kobj, &recovery_mgr_attr_group); kfree(rec); return rc; } Loading
drivers/gpu/drm/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ msm_drm-y := \ sde/sde_color_processing.o \ sde/sde_vbif.o \ sde/sde_splash.o \ sde/sde_recovery_manager.o \ sde_dbg.o \ sde_dbg_evtlog.o \ sde_io_util.o \ Loading
drivers/gpu/drm/msm/sde/sde_encoder.c +2 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/seq_file.h> #include "msm_drv.h" #include "sde_recovery_manager.h" #include "sde_kms.h" #include "drm_crtc.h" #include "drm_crtc_helper.h" Loading Loading @@ -603,6 +604,7 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, atomic_inc(&phy_enc->underrun_cnt); SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); sde_recovery_set_events(SDE_UNDERRUN); trace_sde_encoder_underrun(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); SDE_DBG_CTRL("stop_ftrace"); Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +3 −1 Original line number Diff line number Diff line /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -11,6 +11,7 @@ */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ #include "sde_recovery_manager.h" #include "sde_encoder_phys.h" #include "sde_hw_interrupts.h" #include "sde_core_irq.h" Loading Loading @@ -704,6 +705,7 @@ static int sde_encoder_phys_vid_wait_for_vblank( SDE_EVT32(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0); SDE_ERROR_VIDENC(vid_enc, "kickoff timed out\n"); sde_recovery_set_events(SDE_VSYNC_MISS); if (notify && phys_enc->parent_ops.handle_frame_done) phys_enc->parent_ops.handle_frame_done( phys_enc->parent, phys_enc, Loading
drivers/gpu/drm/msm/sde/sde_kms.c +52 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ #include "sde_encoder.h" #include "sde_plane.h" #include "sde_crtc.h" #include "sde_recovery_manager.h" #define CREATE_TRACE_POINTS #include "sde_trace.h" Loading @@ -58,6 +59,19 @@ #define SDE_DEBUGFS_DIR "msm_sde" #define SDE_DEBUGFS_HWMASKNAME "hw_log_mask" static int sde_kms_recovery_callback(int err_code, struct recovery_client_info *client_info); static struct recovery_client_info info = { .name = "sde_kms", .recovery_cb = sde_kms_recovery_callback, .err_supported[0] = {SDE_UNDERRUN, 0, 0}, .err_supported[1] = {SDE_VSYNC_MISS, 0, 0}, .no_of_err = 2, .handle = NULL, .pdata = NULL, }; /** * sdecustom - enable certain driver customizations for sde clients * Enabling this modifies the standard DRM behavior slightly and assumes Loading Loading @@ -1062,6 +1076,8 @@ static void sde_kms_destroy(struct msm_kms *kms) return; } sde_recovery_client_unregister(info.handle); info.handle = NULL; _sde_kms_hw_destroy(sde_kms, dev->platformdev); kfree(sde_kms); } Loading Loading @@ -1264,6 +1280,11 @@ static int sde_kms_hw_init(struct msm_kms *kms) goto end; } rc = sde_recovery_client_register(&info); if (rc) pr_err("%s recovery mgr register failed %d\n", __func__, rc); sde_kms = to_sde_kms(kms); dev = sde_kms->dev; if (!dev || !dev->platformdev) { Loading Loading @@ -1487,10 +1508,34 @@ end: return rc; } static int sde_kms_recovery_callback(int err_code, struct recovery_client_info *client_info) { int rc = 0; switch (err_code) { case SDE_UNDERRUN: pr_debug("%s [SDE_UNDERRUN] error is auto HW receovered\n", __func__); break; case SDE_VSYNC_MISS: pr_debug("%s [SDE_VSYNC_MISS] trigger soft reset\n", __func__); break; default: pr_err("%s error %d undefined\n", __func__, err_code); } return rc; } struct msm_kms *sde_kms_init(struct drm_device *dev) { struct msm_drm_private *priv; struct sde_kms *sde_kms; int rc = 0; if (!dev || !dev->dev_private) { SDE_ERROR("drm device node invalid\n"); Loading @@ -1505,6 +1550,13 @@ struct msm_kms *sde_kms_init(struct drm_device *dev) return ERR_PTR(-ENOMEM); } rc = sde_init_recovery_mgr(dev); if (rc) { SDE_ERROR("Failed SDE recovery mgr Init, err = %d\n", rc); kfree(sde_kms); return ERR_PTR(-EFAULT); } msm_kms_init(&sde_kms->base, &kms_funcs); sde_kms->dev = dev; Loading
drivers/gpu/drm/msm/sde/sde_recovery_manager.c 0 → 100644 +399 −0 Original line number Diff line number Diff line /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include "sde_recovery_manager.h" #include "sde_kms.h" static struct recovery_mgr_info *rec_mgr; static ssize_t sde_recovery_mgr_rda_clients_attr(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; struct list_head *pos; struct recovery_client_db *temp = NULL; mutex_lock(&rec_mgr->rec_lock); len = snprintf(buf, PAGE_SIZE, "Clients:\n"); list_for_each(pos, &rec_mgr->client_list) { temp = list_entry(pos, struct recovery_client_db, list); len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", temp->client_info.name); } mutex_unlock(&rec_mgr->rec_lock); return len; } static DEVICE_ATTR(clients, S_IRUGO, sde_recovery_mgr_rda_clients_attr, NULL); static struct attribute *recovery_attrs[] = { &dev_attr_clients.attr, NULL, }; static struct attribute_group recovery_mgr_attr_group = { .attrs = recovery_attrs, }; static void sde_recovery_mgr_notify(bool err_state) { char *envp[2]; char *uevent_str = kzalloc(SZ_4K, GFP_KERNEL); if (uevent_str == NULL) { DRM_ERROR("failed to allocate event string\n"); return; } if (err_state == true) snprintf(uevent_str, MAX_REC_UEVENT_LEN, "DISPLAY_ERROR_RECOVERED\n"); else snprintf(uevent_str, MAX_REC_UEVENT_LEN, "DISPLAY_CRITICAL_ERROR\n"); DRM_DEBUG("generating uevent [%s]\n", uevent_str); envp[0] = uevent_str; envp[1] = NULL; mutex_lock(&rec_mgr->dev->mode_config.mutex); kobject_uevent_env(&rec_mgr->dev->primary->kdev->kobj, KOBJ_CHANGE, envp); mutex_unlock(&rec_mgr->dev->mode_config.mutex); kfree(uevent_str); } static void sde_recovery_mgr_recover(int err_code) { struct list_head *pos; struct recovery_client_db *c = NULL; int tmp_err, rc, pre, post, i; bool found = false; static bool rec_flag = true; mutex_lock(&rec_mgr->rec_lock); list_for_each(pos, &rec_mgr->client_list) { c = list_entry(pos, struct recovery_client_db, list); mutex_unlock(&rec_mgr->rec_lock); for (i = 0; i < MAX_REC_ERR_SUPPORT; i++) { tmp_err = c->client_info.err_supported[i]. reported_err_code; if (tmp_err == err_code) { found = true; break; } } if (found == true) { pre = c->client_info.err_supported[i].pre_err_code; if (pre && pre != '0') sde_recovery_mgr_recover(pre); if (c->client_info.recovery_cb) { rc = c->client_info.recovery_cb(err_code, &c->client_info); if (rc) { pr_err("%s failed to recover error %d\n", __func__, err_code); rec_flag = false; } else { pr_debug("%s Recovery successful[%d]\n", __func__, err_code); } } post = c->client_info.err_supported[i].post_err_code; if (post && post != '0') sde_recovery_mgr_recover(post); } mutex_lock(&rec_mgr->rec_lock); if (found) break; } if (rec_flag) { pr_debug("%s successful full recovery\n", __func__); sde_recovery_mgr_notify(true); } mutex_unlock(&rec_mgr->rec_lock); } static void sde_recovery_mgr_event_work(struct work_struct *work) { struct list_head *pos, *q; struct recovery_event_db *temp_event; int err_code; if (!rec_mgr) { pr_err("%s recovery manager is NULL\n", __func__); return; } mutex_lock(&rec_mgr->rec_lock); list_for_each_safe(pos, q, &rec_mgr->event_list) { temp_event = list_entry(pos, struct recovery_event_db, list); err_code = temp_event->err; rec_mgr->recovery_ongoing = true; mutex_unlock(&rec_mgr->rec_lock); /* notify error */ sde_recovery_mgr_notify(false); /* recover error */ sde_recovery_mgr_recover(err_code); mutex_lock(&rec_mgr->rec_lock); list_del(pos); kfree(temp_event); } rec_mgr->recovery_ongoing = false; mutex_unlock(&rec_mgr->rec_lock); } int sde_recovery_set_events(int err) { int rc = 0; struct list_head *pos; struct recovery_event_db *temp; bool found = false; mutex_lock(&rec_mgr->rec_lock); /* check if there is same event in the list */ list_for_each(pos, &rec_mgr->event_list) { temp = list_entry(pos, struct recovery_event_db, list); if (err == temp->err) { found = true; pr_info("%s error %d is already present in list\n", __func__, err); break; } } if (!found) { temp = kzalloc(sizeof(struct recovery_event_db), GFP_KERNEL); if (!temp) { pr_err("%s out of memory\n", __func__); rc = -ENOMEM; goto out; } temp->err = err; list_add_tail(&temp->list, &rec_mgr->event_list); queue_work(rec_mgr->event_queue, &rec_mgr->event_work); } out: mutex_unlock(&rec_mgr->rec_lock); return rc; } int sde_recovery_client_register(struct recovery_client_info *client) { int rc = 0; struct list_head *pos; struct recovery_client_db *c = NULL; bool found = false; if (!rec_mgr) { pr_err("%s recovery manager is not initialized\n", __func__); return -EPERM; } if (!strlen(client->name)) { pr_err("%s client name is empty\n", __func__); return -EINVAL; } mutex_lock(&rec_mgr->rec_lock); /* check if there is same client */ list_for_each(pos, &rec_mgr->client_list) { c = list_entry(pos, struct recovery_client_db, list); if (!strcmp(c->client_info.name, client->name)) { found = true; break; } } if (!found) { c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) { pr_err("%s out of memory for client", __func__); rc = -ENOMEM; goto out; } } else { pr_err("%s client = %s is already registered\n", __func__, client->name); client->handle = c; goto out; } memcpy(&(c->client_info), client, sizeof(struct recovery_client_info)); list_add_tail(&c->list, &rec_mgr->client_list); rec_mgr->num_of_clients++; client->handle = c; out: mutex_unlock(&rec_mgr->rec_lock); return rc; } int sde_recovery_client_unregister(void *handle) { struct list_head *pos, *q, *pos1; struct recovery_client_db *temp_client; struct recovery_event_db *temp; int client_err = 0; bool found = false; bool found_pending = false; int i, rc = 0; struct recovery_client_info *client = &((struct recovery_client_db *)handle)->client_info; if (!handle) { pr_err("%s handle is NULL\n", __func__); return -EINVAL; } if (!strlen(client->name)) { pr_err("%s client name is empty\n", __func__); return -EINVAL; } mutex_lock(&rec_mgr->rec_lock); if (rec_mgr->recovery_ongoing) { pr_err("%s SDE Executing Recovery, Failed! Unregister client %s\n", __func__, client->name); goto out; } /* check if client is present in the list */ list_for_each_safe(pos, q, &rec_mgr->client_list) { temp_client = list_entry(pos, struct recovery_client_db, list); if (!strcmp(temp_client->client_info.name, client->name)) { found = true; /* free any pending event for this client */ list_for_each(pos1, &rec_mgr->event_list) { temp = list_entry(pos1, struct recovery_event_db, list); found_pending = false; for (i = 0; i < MAX_REC_ERR_SUPPORT; i++) { client_err = temp_client-> client_info.err_supported[i]. reported_err_code; if (temp->err == client_err) found_pending = true; } if (found_pending) { list_del(pos1); kfree(temp); } } list_del(pos); kfree(temp_client); rec_mgr->num_of_clients--; break; } } if (!found) { pr_err("%s can't find the client[%s] from db\n", __func__, client->name); rc = -EFAULT; } out: mutex_unlock(&rec_mgr->rec_lock); return rc; } int sde_init_recovery_mgr(struct drm_device *dev) { struct recovery_mgr_info *rec = NULL; int rc = 0; if (!dev || !dev->dev_private) { SDE_ERROR("drm device node invalid\n"); return -EINVAL; } rec = kzalloc(sizeof(struct recovery_mgr_info), GFP_KERNEL); if (!rec) return -ENOMEM; mutex_init(&rec->rec_lock); rec->dev = dev; rc = sysfs_create_group(&dev->primary->kdev->kobj, &recovery_mgr_attr_group); if (rc) { pr_err("%s sysfs_create_group fails=%d", __func__, rc); rec->sysfs_created = false; } else { rec->sysfs_created = true; } INIT_LIST_HEAD(&rec->event_list); INIT_LIST_HEAD(&rec->client_list); INIT_WORK(&rec->event_work, sde_recovery_mgr_event_work); rec->event_queue = create_workqueue("recovery_event"); if (IS_ERR_OR_NULL(rec->event_queue)) { pr_err("%s unable to create queue; errno = %ld", __func__, PTR_ERR(rec->event_queue)); rec->event_queue = NULL; rc = -EFAULT; goto err; } rec_mgr = rec; return rc; err: mutex_destroy(&rec->rec_lock); if (rec->sysfs_created) sysfs_remove_group(&rec_mgr->dev->primary->kdev->kobj, &recovery_mgr_attr_group); kfree(rec); return rc; }