diff options
author | Whi copybara merger <whitechapel-automerger@google.com> | 2022-01-05 11:12:10 -0800 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2022-01-18 08:39:31 -0800 |
commit | 718b9717ccc1305f3d95b767f28e539c0c084313 (patch) | |
tree | 449095e0a90d76d9b4cbca0f29e93c242040e9e9 | |
parent | 5017274660264e2910eadd6b573e2e87e3c6e009 (diff) | |
download | abrolhos-718b9717ccc1305f3d95b767f28e539c0c084313.tar.gz |
[Copybara Auto Merge] Merge branch 'whitechapel' into android-gs-pixel-5.10
edgetpu: Remove dbg dump error msg to avoid flooding logs.
edgetpu: dbg dump debugFS return EOPNOTSUPP
edgetpu: dbg dump handle recoverable fault
edgetpu: fix wakelock order to avoid lock inversion
Bug: 197788097
edgetpu: power up TPU before sending dbg dump KCI
edgetpu: add debugfs for getting dbg dump
Bug: 207607509
edgetpu: map_size instead of alloc_size on dump
Bug: 210563422
edgetpu: fix UAF on dumping group mappings
Bug: 210549145
edgetpu: dmabuf fix potential UAF
Bug: 210571509
edgetpu: notify MCP-wide thermal shutdown via kworker
Bug: 207807085
Bug: 174552882
edgetpu: expand mobile debug dump support
Bug: 207459857
edgetpu: mobile: share debug dump handlers
Bug: 207459857
edgetpu: abrolhos: init sscd_dev in dbg dump init
Bug: 208359418
edgetpu: expand EXT_MAILBOX support
Bug: 204795149
GitOrigin-RevId: ccd0602e3352e9c241ebccfffa5a7d1322e43050
Change-Id: I72fe4428080e6f8e80ac09828e7fc8e330c91c66
-rw-r--r-- | drivers/edgetpu/abrolhos-debug-dump.c | 207 | ||||
-rw-r--r-- | drivers/edgetpu/abrolhos-device.c | 84 | ||||
-rw-r--r-- | drivers/edgetpu/abrolhos-platform.h | 3 | ||||
-rw-r--r-- | drivers/edgetpu/abrolhos/config-mailbox.h | 6 | ||||
-rw-r--r-- | drivers/edgetpu/edgetpu-debug-dump.c | 43 | ||||
-rw-r--r-- | drivers/edgetpu/edgetpu-dmabuf.c | 2 | ||||
-rw-r--r-- | drivers/edgetpu/edgetpu-fs.c | 109 | ||||
-rw-r--r-- | drivers/edgetpu/edgetpu-mailbox.c | 57 | ||||
-rw-r--r-- | drivers/edgetpu/edgetpu-mcp.h | 7 | ||||
-rw-r--r-- | drivers/edgetpu/edgetpu-mobile-platform.c | 80 | ||||
-rw-r--r-- | drivers/edgetpu/edgetpu-mobile-platform.h | 3 | ||||
-rw-r--r-- | drivers/edgetpu/mobile-debug-dump.c | 277 | ||||
-rw-r--r-- | drivers/edgetpu/mobile-debug-dump.h | 27 |
13 files changed, 473 insertions, 432 deletions
diff --git a/drivers/edgetpu/abrolhos-debug-dump.c b/drivers/edgetpu/abrolhos-debug-dump.c index a56808c..92c3e1a 100644 --- a/drivers/edgetpu/abrolhos-debug-dump.c +++ b/drivers/edgetpu/abrolhos-debug-dump.c @@ -1,216 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 -/* - * Implements chip specific details of debug dump memory initialization and SSCD registration. - * - * Copyright (C) 2021 Google, Inc. - */ #if IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP) || IS_ENABLED(CONFIG_EDGETPU_TEST) -#include <linux/platform_device.h> - -#include "abrolhos-platform.h" - #include "mobile-debug-dump.c" -static void sscd_release(struct device *dev) -{ - pr_debug(DRIVER_NAME " release\n"); -} -static struct sscd_platform_data sscd_pdata; -static struct platform_device sscd_dev = { - .name = DRIVER_NAME, - .driver_override = SSCD_NAME, - .id = -1, - .dev = { - .platform_data = &sscd_pdata, - .release = sscd_release, - }, -}; - -static int abrolhos_sscd_generate_coredump(void *p_etdev, void *p_dump_setup) -{ - struct edgetpu_dev *etdev; - struct edgetpu_debug_dump_setup *dump_setup; - struct abrolhos_platform_dev *pdev; - struct sscd_platform_data *pdata; - struct platform_device *sscd_dev; - struct sscd_segment *segs; - struct edgetpu_debug_dump *debug_dump; - struct edgetpu_crash_reason *crash_reason; - struct edgetpu_dump_segment *dump_seg; - struct edgetpu_device_group *group; - struct edgetpu_device_group **groups; - struct edgetpu_list_group *g; - struct mobile_sscd_mappings_dump *mappings_dump = NULL; - char crash_info[128]; - int sscd_dump_segments_num; - int i, ret; - size_t num_groups = 0, num_queues = 0; - u64 offset; - - if (!p_etdev || !p_dump_setup) - return -EINVAL; - - etdev = (struct edgetpu_dev *)p_etdev; - dump_setup = (struct edgetpu_debug_dump_setup *)p_dump_setup; - pdev = to_abrolhos_dev(etdev); - pdata = (struct sscd_platform_data *)pdev->sscd_info.pdata; - sscd_dev = (struct platform_device *)pdev->sscd_info.dev; - if (!pdata->sscd_report) { - etdev_err(etdev, "failed to generate coredump"); - return -ENOENT; - } - - debug_dump = (struct edgetpu_debug_dump *)(dump_setup + 1); - - /* Populate crash reason */ - crash_reason = (struct edgetpu_crash_reason *)((u8 *)dump_setup + - debug_dump->crash_reason_offset); - scnprintf(crash_info, sizeof(crash_info), - "[edgetpu_coredump] error code: %#llx", crash_reason->code); - - mutex_lock(&etdev->groups_lock); - groups = kmalloc_array(etdev->n_groups, sizeof(*groups), GFP_KERNEL); - if (!groups) { - mutex_unlock(&etdev->groups_lock); - return -ENOMEM; - } - - etdev_for_each_group(etdev, g, group) { - if (edgetpu_device_group_is_disbanded(group)) - continue; - groups[num_groups++] = edgetpu_device_group_get(group); - } - mutex_unlock(&etdev->groups_lock); - - /* Allocate memory for dump segments */ - sscd_dump_segments_num = debug_dump->dump_segments_num; - sscd_dump_segments_num += 2 * num_groups; /* VII cmd and resp queues */ - sscd_dump_segments_num += num_groups ? 1 : 0; /* Mappings info */ - sscd_dump_segments_num += 2; /* KCI cmd and resp queues */ - - segs = kmalloc_array(sscd_dump_segments_num, - sizeof(struct sscd_segment), - GFP_KERNEL); - if (!segs) { - ret = -ENOMEM; - goto out_sscd_generate_coredump; - } - - /* Populate sscd segments */ - dump_seg = (struct edgetpu_dump_segment *)((u8 *)dump_setup + - debug_dump->dump_segments_offset); - offset = debug_dump->dump_segments_offset; - for (i = 0; i < debug_dump->dump_segments_num; i++) { - segs[i].addr = dump_seg; - segs[i].size = sizeof(struct edgetpu_dump_segment) + dump_seg->size; - segs[i].paddr = (void *)(etdev->debug_dump_mem.tpu_addr + - offset); - segs[i].vaddr = (void *)(etdev->debug_dump_mem.vaddr + - offset); - offset += sizeof(struct edgetpu_dump_segment) + dump_seg->size; - dump_seg = (struct edgetpu_dump_segment *) - ((u8 *)dump_setup + ALIGN(offset, sizeof(uint64_t))); - } - - if (num_groups) { - mappings_dump = mobile_sscd_collect_mappings_segment(groups, num_groups, &segs[i]); - if (!mappings_dump) { - ret = -ENOMEM; - goto out_sscd_generate_coredump; - } - i++; - } - - num_queues = mobile_sscd_collect_cmd_resp_queues(etdev, groups, num_groups, &segs[i]); - - /* Adjust num of segments as some groups may have a detached mailbox */ - sscd_dump_segments_num -= (2 * num_groups + 2); /* Subtract number of VII and KCI queues - * according to num_groups. - */ - sscd_dump_segments_num += num_queues; /* Add actual number of valid VII and KCI queues */ - - /* Pass dump data to SSCD daemon */ - etdev_dbg(etdev, "report: %d segments", sscd_dump_segments_num); - ret = pdata->sscd_report(sscd_dev, segs, sscd_dump_segments_num, - SSCD_FLAGS_ELFARM64HDR, crash_info); -out_sscd_generate_coredump: - for (i = 0; i < num_groups; i++) - edgetpu_device_group_put(groups[i]); - kfree(mappings_dump); - kfree(segs); - kfree(groups); - - return ret; -} - -int edgetpu_debug_dump_init(struct edgetpu_dev *etdev) -{ - size_t size; - int ret; - struct edgetpu_debug_dump_setup *dump_setup; - struct abrolhos_platform_dev *pdev; - - pdev = to_abrolhos_dev(etdev); - - size = EDGETPU_DEBUG_DUMP_MEM_SIZE; - - /* Register SSCD platform device */ - ret = platform_device_register(&sscd_dev); - if (ret) { - etdev_err(etdev, "SSCD platform device registration failed: %d", ret); - return ret; - } - /* - * Allocate a buffer for various dump segments - */ - ret = edgetpu_alloc_coherent(etdev, size, &etdev->debug_dump_mem, - EDGETPU_CONTEXT_KCI); - if (ret) { - etdev_err(etdev, "Debug dump seg alloc failed"); - etdev->debug_dump_mem.vaddr = NULL; - goto out_unregister_platform; - } - dump_setup = - (struct edgetpu_debug_dump_setup *)etdev->debug_dump_mem.vaddr; - memset(dump_setup, 0, size); - dump_setup->dump_mem_size = size; - - /* - * Allocate memory for debug dump handlers - */ - etdev->debug_dump_handlers = kcalloc(DUMP_REASON_NUM, - sizeof(*etdev->debug_dump_handlers), - GFP_KERNEL); - if (!etdev->debug_dump_handlers) - return -ENOMEM; - etdev->debug_dump_handlers[DUMP_REASON_REQ_BY_USER] = - abrolhos_sscd_generate_coredump; - - pdev->sscd_info.pdata = &sscd_pdata; - pdev->sscd_info.dev = &sscd_dev; - return ret; -out_unregister_platform: - platform_device_unregister(&sscd_dev); - return ret; -} - -void edgetpu_debug_dump_exit(struct edgetpu_dev *etdev) -{ - if (!etdev->debug_dump_mem.vaddr) { - etdev_dbg(etdev, "Debug dump not allocated"); - return; - } - /* - * Free the memory assigned for debug dump - */ - edgetpu_free_coherent(etdev, &etdev->debug_dump_mem, - EDGETPU_CONTEXT_KCI); - kfree(etdev->debug_dump_handlers); - platform_device_unregister(&sscd_dev); -} - #else /* IS_ENABLED(CONFIG_SUBSYSTEM_COREDUMP) || IS_ENABLED(CONFIG_EDGETPU_TEST) */ #include "edgetpu-debug-dump.c" diff --git a/drivers/edgetpu/abrolhos-device.c b/drivers/edgetpu/abrolhos-device.c index df23c42..67feb70 100644 --- a/drivers/edgetpu/abrolhos-device.c +++ b/drivers/edgetpu/abrolhos-device.c @@ -105,87 +105,3 @@ void edgetpu_chip_handle_reverse_kci(struct edgetpu_dev *etdev, break; } } - -static int abrolhos_check_ext_mailbox_args(const char *func, - struct edgetpu_dev *etdev, - struct edgetpu_ext_mailbox_ioctl *args) -{ - if (args->type != EDGETPU_EXT_MAILBOX_TYPE_TZ) { - etdev_err(etdev, "%s: Invalid type %d != %d\n", func, - args->type, EDGETPU_EXT_MAILBOX_TYPE_TZ); - return -EINVAL; - } - if (args->count != 1) { - etdev_err(etdev, "%s: Invalid mailbox count: %d != 1\n", func, - args->count); - return -EINVAL; - } - return 0; -} - -int edgetpu_chip_acquire_ext_mailbox(struct edgetpu_client *client, - struct edgetpu_ext_mailbox_ioctl *args) -{ - struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(client->etdev); - int ret; - - ret = abrolhos_check_ext_mailbox_args(__func__, client->etdev, - args); - if (ret) - return ret; - - mutex_lock(&etmdev->tz_mailbox_lock); - if (etmdev->secure_client) { - etdev_err(client->etdev, - "TZ mailbox already in use by PID %d\n", - etmdev->secure_client->pid); - mutex_unlock(&etmdev->tz_mailbox_lock); - return -EBUSY; - } - ret = edgetpu_mailbox_enable_ext(client, ABROLHOS_TZ_MAILBOX_ID, NULL); - if (!ret) - etmdev->secure_client = client; - mutex_unlock(&etmdev->tz_mailbox_lock); - return ret; -} - -int edgetpu_chip_release_ext_mailbox(struct edgetpu_client *client, - struct edgetpu_ext_mailbox_ioctl *args) -{ - struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(client->etdev); - int ret = 0; - - ret = abrolhos_check_ext_mailbox_args(__func__, client->etdev, - args); - if (ret) - return ret; - - mutex_lock(&etmdev->tz_mailbox_lock); - if (!etmdev->secure_client) { - etdev_warn(client->etdev, "TZ mailbox already released\n"); - mutex_unlock(&etmdev->tz_mailbox_lock); - return 0; - } - if (etmdev->secure_client != client) { - etdev_err(client->etdev, - "TZ mailbox owned by different client\n"); - mutex_unlock(&etmdev->tz_mailbox_lock); - return -EBUSY; - } - etmdev->secure_client = NULL; - ret = edgetpu_mailbox_disable_ext(client, ABROLHOS_TZ_MAILBOX_ID); - mutex_unlock(&etmdev->tz_mailbox_lock); - return ret; -} - -void edgetpu_chip_client_remove(struct edgetpu_client *client) -{ - struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(client->etdev); - - mutex_lock(&etmdev->tz_mailbox_lock); - if (etmdev->secure_client == client) { - etmdev->secure_client = NULL; - edgetpu_mailbox_disable_ext(client, ABROLHOS_TZ_MAILBOX_ID); - } - mutex_unlock(&etmdev->tz_mailbox_lock); -} diff --git a/drivers/edgetpu/abrolhos-platform.h b/drivers/edgetpu/abrolhos-platform.h index 7ed88ed..4372f30 100644 --- a/drivers/edgetpu/abrolhos-platform.h +++ b/drivers/edgetpu/abrolhos-platform.h @@ -10,15 +10,12 @@ #include "edgetpu-internal.h" #include "edgetpu-mobile-platform.h" -#include "mobile-debug-dump.h" #define to_abrolhos_dev(etdev) \ container_of((to_mobile_dev(etdev)), struct abrolhos_platform_dev, mobile_dev) struct abrolhos_platform_dev { struct edgetpu_mobile_platform_dev mobile_dev; - /* subsystem coredump info struct */ - struct mobile_sscd_info sscd_info; }; #endif /* __ABROLHOS_PLATFORM_H__ */ diff --git a/drivers/edgetpu/abrolhos/config-mailbox.h b/drivers/edgetpu/abrolhos/config-mailbox.h index efbd169..b5a6c88 100644 --- a/drivers/edgetpu/abrolhos/config-mailbox.h +++ b/drivers/edgetpu/abrolhos/config-mailbox.h @@ -15,7 +15,11 @@ #define EDGETPU_NUM_P2P_MAILBOXES 0 #define EDGETPU_NUM_EXT_MAILBOXES 0 -#define ABROLHOS_TZ_MAILBOX_ID 8 +/* + * The TZ mailbox is not managed by the kernel, but we still need to tell firmware to enable it, + * so it's index is placed after the kernel managed mailboxes. + */ +#define EDGETPU_TZ_MAILBOX_ID 8 #define ABROLHOS_CSR_MBOX2_CONTEXT_ENABLE 0xe0000 #define ABROLHOS_CSR_MBOX2_CMD_QUEUE_DOORBELL_SET 0xe1000 diff --git a/drivers/edgetpu/edgetpu-debug-dump.c b/drivers/edgetpu/edgetpu-debug-dump.c index 3836e85..ab5f9e3 100644 --- a/drivers/edgetpu/edgetpu-debug-dump.c +++ b/drivers/edgetpu/edgetpu-debug-dump.c @@ -3,8 +3,10 @@ * Module that defines structures and functions to retrieve debug dump segments * from edgetpu firmware. * - * Copyright (C) 2020 Google, Inc. + * Copyright (C) 2020-2021 Google LLC */ + +#include <linux/debugfs.h> #include <linux/workqueue.h> #include "edgetpu-config.h" @@ -12,6 +14,36 @@ #include "edgetpu-device-group.h" #include "edgetpu-iremap-pool.h" #include "edgetpu-kci.h" +#include "edgetpu-pm.h" + +static int edgetpu_get_debug_dump_set(void *data, u64 val) +{ + struct edgetpu_dev *etdev = data; + int ret = edgetpu_pm_get(etdev->pm); + + if (ret) + return ret; + ret = edgetpu_get_debug_dump(etdev, val); + if (ret > 0) { + etdev_warn(etdev, "FW refused debug dump request: %d", ret); + ret = -EOPNOTSUPP; + } + edgetpu_pm_put(etdev->pm); + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_get_debug_dump, NULL, edgetpu_get_debug_dump_set, "%llu\n"); + +/* + * Creates debugFS entries for interacting with debug dump functions. + * + * This is expected to be called by edgetpu_debug_dump_init(). + */ +static inline void edgetpu_setup_debug_dump_fs(struct edgetpu_dev *etdev) +{ + /* forwards write requests to edgetpu_get_debug_dump() */ + debugfs_create_file("get_debug_dump", 0220, etdev->d_entry, etdev, &fops_get_debug_dump); +} int edgetpu_get_debug_dump(struct edgetpu_dev *etdev, u64 type) { @@ -23,6 +55,10 @@ int edgetpu_get_debug_dump(struct edgetpu_dev *etdev, u64 type) return -EINVAL; } + if (!edgetpu_pm_get_if_powered(etdev->pm)) { + etdev_warn(etdev, "Device not powered, skip debug dump"); + return -ENODEV; + } dump_setup = (struct edgetpu_debug_dump_setup *)etdev->debug_dump_mem.vaddr; dump_setup->type = type; @@ -35,6 +71,7 @@ int edgetpu_get_debug_dump(struct edgetpu_dev *etdev, u64 type) if (ret) etdev_err(etdev, "KCI dump info req failed: %d", ret); + edgetpu_pm_put(etdev->pm); return ret; } @@ -83,10 +120,8 @@ void edgetpu_debug_dump_resp_handler(struct edgetpu_dev *etdev) struct edgetpu_debug_dump_setup *dump_setup; struct edgetpu_debug_dump *debug_dump; - if (!etdev->debug_dump_mem.vaddr) { - etdev_err(etdev, "Debug dump memory not allocated"); + if (!etdev->debug_dump_mem.vaddr) return; - } dump_setup = (struct edgetpu_debug_dump_setup *)etdev->debug_dump_mem.vaddr; debug_dump = (struct edgetpu_debug_dump *)(dump_setup + 1); diff --git a/drivers/edgetpu/edgetpu-dmabuf.c b/drivers/edgetpu/edgetpu-dmabuf.c index f98aafe..276da72 100644 --- a/drivers/edgetpu/edgetpu-dmabuf.c +++ b/drivers/edgetpu/edgetpu-dmabuf.c @@ -723,7 +723,7 @@ int edgetpu_map_dmabuf(struct edgetpu_device_group *group, __func__, ret); goto err_release_map; } - arg->device_address = dmap->map.device_address; + arg->device_address = tpu_addr; mutex_unlock(&group->lock); dma_buf_put(dmabuf); return 0; diff --git a/drivers/edgetpu/edgetpu-fs.c b/drivers/edgetpu/edgetpu-fs.c index 599a6a7..32b8fde 100644 --- a/drivers/edgetpu/edgetpu-fs.c +++ b/drivers/edgetpu/edgetpu-fs.c @@ -111,8 +111,8 @@ static int edgetpu_fs_release(struct inode *inode, struct file *file) return 0; etdev = client->etdev; + LOCK(client); wakelock_count = edgetpu_wakelock_lock(client->wakelock); - mutex_lock(&client->group_lock); /* * @wakelock_count = 0 means the device might be powered off. And for group with a * non-detachable mailbox, its mailbox is removed when the group is released, in such case @@ -130,8 +130,8 @@ static int edgetpu_fs_release(struct inode *inode, struct file *file) /* failed to power on - prevent group releasing from accessing the device */ client->group->dev_inaccessible = true; } - mutex_unlock(&client->group_lock); edgetpu_wakelock_unlock(client->wakelock); + UNLOCK(client); edgetpu_client_remove(client); @@ -222,32 +222,29 @@ static int edgetpu_ioctl_unset_perdie_eventfd(struct edgetpu_client *client, static int edgetpu_ioctl_finalize_group(struct edgetpu_client *client) { struct edgetpu_device_group *group; - int ret = -EINVAL, wakelock_count; + int ret = -EINVAL; - /* - * Hold the wakelock since we need to decide whether VII should be - * initialized during finalization. - */ - wakelock_count = edgetpu_wakelock_lock(client->wakelock); LOCK(client); group = client->group; if (!group || !edgetpu_device_group_is_leader(group, client)) goto out_unlock; /* Finalization has to be performed with device on. */ - if (!wakelock_count) { - ret = edgetpu_pm_get(client->etdev->pm); - if (ret) { - etdev_err(client->etdev, "%s: pm_get failed (%d)", - __func__, ret); - goto out_unlock; - } + ret = edgetpu_pm_get(client->etdev->pm); + if (ret) { + etdev_err(client->etdev, "%s: pm_get failed (%d)", + __func__, ret); + goto out_unlock; } + /* + * Hold the wakelock since we need to decide whether VII should be + * initialized during finalization. + */ + edgetpu_wakelock_lock(client->wakelock); ret = edgetpu_device_group_finalize(group); - if (!wakelock_count) - edgetpu_pm_put(client->etdev->pm); + edgetpu_wakelock_unlock(client->wakelock); + edgetpu_pm_put(client->etdev->pm); out_unlock: UNLOCK(client); - edgetpu_wakelock_unlock(client->wakelock); return ret; } @@ -562,21 +559,22 @@ static int edgetpu_ioctl_release_wakelock(struct edgetpu_client *client) { int count; + LOCK(client); edgetpu_wakelock_lock(client->wakelock); /* when NO_WAKELOCK: count should be 1 so here is a no-op */ count = edgetpu_wakelock_release(client->wakelock); if (count < 0) { edgetpu_wakelock_unlock(client->wakelock); + UNLOCK(client); return count; } if (!count) { - mutex_lock(&client->group_lock); if (client->group) edgetpu_group_close_and_detach_mailbox(client->group); - mutex_unlock(&client->group_lock); edgetpu_pm_put(client->etdev->pm); } edgetpu_wakelock_unlock(client->wakelock); + UNLOCK(client); etdev_dbg(client->etdev, "%s: wakelock req count = %u", __func__, count); return 0; @@ -588,50 +586,49 @@ static int edgetpu_ioctl_acquire_wakelock(struct edgetpu_client *client) int ret; struct edgetpu_thermal *thermal = client->etdev->thermal; + LOCK(client); + edgetpu_thermal_lock(thermal); + if (edgetpu_thermal_is_suspended(thermal)) { + /* TPU is thermal suspended, so fail acquiring wakelock */ + ret = -EAGAIN; + etdev_warn_ratelimited(client->etdev, + "wakelock acquire rejected due to thermal suspend"); + edgetpu_thermal_unlock(thermal); + goto error_unlock; + } else { + ret = edgetpu_pm_get(client->etdev->pm); + edgetpu_thermal_unlock(thermal); + if (ret) { + etdev_warn(client->etdev, "%s: pm_get failed (%d)", + __func__, ret); + goto error_unlock; + } + } edgetpu_wakelock_lock(client->wakelock); /* when NO_WAKELOCK: count should be 1 so here is a no-op */ count = edgetpu_wakelock_acquire(client->wakelock); if (count < 0) { - edgetpu_wakelock_unlock(client->wakelock); - return count; + edgetpu_pm_put(client->etdev->pm); + ret = count; + goto error_unlock; } - if (!count) { - edgetpu_thermal_lock(thermal); - if (edgetpu_thermal_is_suspended(thermal)) { - /* TPU is thermal suspended, so fail acquiring wakelock */ - ret = -EAGAIN; - etdev_warn_ratelimited(client->etdev, - "wakelock acquire rejected due to thermal suspend"); - edgetpu_thermal_unlock(thermal); - goto error_release; - } else { - ret = edgetpu_pm_get(client->etdev->pm); - edgetpu_thermal_unlock(thermal); - } - if (ret) { - etdev_warn(client->etdev, "%s: pm_get failed (%d)", - __func__, ret); - goto error_release; - } - mutex_lock(&client->group_lock); - if (client->group) - ret = edgetpu_group_attach_and_open_mailbox( - client->group); - mutex_unlock(&client->group_lock); - if (ret) { - etdev_warn(client->etdev, - "failed to attach mailbox: %d", ret); - edgetpu_pm_put(client->etdev->pm); - goto error_release; - } + if (!count && client->group) + ret = edgetpu_group_attach_and_open_mailbox(client->group); + if (ret) { + etdev_warn(client->etdev, + "failed to attach mailbox: %d", ret); + edgetpu_pm_put(client->etdev->pm); + edgetpu_wakelock_release(client->wakelock); + edgetpu_wakelock_unlock(client->wakelock); + goto error_unlock; } edgetpu_wakelock_unlock(client->wakelock); + UNLOCK(client); etdev_dbg(client->etdev, "%s: wakelock req count = %u", __func__, count + 1); return 0; -error_release: - edgetpu_wakelock_release(client->wakelock); - edgetpu_wakelock_unlock(client->wakelock); +error_unlock: + UNLOCK(client); etdev_err(client->etdev, "PID: %d failed to acquire wakelock", client->pid); return ret; } @@ -683,10 +680,10 @@ static int edgetpu_ioctl_get_fatal_errors(struct edgetpu_client *client, u32 fatal_errors = 0; int ret = 0; - mutex_lock(&client->group_lock); + LOCK(client); if (client->group) fatal_errors = edgetpu_group_get_fatal_errors(client->group); - mutex_unlock(&client->group_lock); + UNLOCK(client); if (copy_to_user(argp, &fatal_errors, sizeof(fatal_errors))) ret = -EFAULT; return ret; diff --git a/drivers/edgetpu/edgetpu-mailbox.c b/drivers/edgetpu/edgetpu-mailbox.c index a950e2c..03a958b 100644 --- a/drivers/edgetpu/edgetpu-mailbox.c +++ b/drivers/edgetpu/edgetpu-mailbox.c @@ -986,6 +986,13 @@ static int edgetpu_mailbox_external_alloc_enable(struct edgetpu_client *client, group = edgetpu_device_group_get(client->group); mutex_unlock(&client->group_lock); + if (!edgetpu_wakelock_lock(client->wakelock)) { + etdev_err(client->etdev, "Enabling external mailbox needs wakelock acquired\n"); + edgetpu_wakelock_unlock(client->wakelock); + edgetpu_device_group_put(client->group); + return -EAGAIN; + } + mutex_lock(&group->lock); if (!edgetpu_device_group_is_finalized(group)) { ret = -EINVAL; @@ -1028,6 +1035,10 @@ static int edgetpu_mailbox_external_alloc_enable(struct edgetpu_client *client, } unlock: mutex_unlock(&group->lock); + if (!ret) + edgetpu_wakelock_inc_event_locked(client->wakelock, + EDGETPU_WAKELOCK_EVENT_EXT_MAILBOX); + edgetpu_wakelock_unlock(client->wakelock); edgetpu_device_group_put(group); return ret; } @@ -1044,9 +1055,17 @@ static int edgetpu_mailbox_external_disable_free(struct edgetpu_client *client) group = edgetpu_device_group_get(client->group); mutex_unlock(&client->group_lock); + if (!edgetpu_wakelock_lock(client->wakelock)) { + etdev_err(client->etdev, "Disabling external mailbox needs wakelock acquired\n"); + edgetpu_wakelock_unlock(client->wakelock); + edgetpu_device_group_put(client->group); + return -EAGAIN; + } mutex_lock(&group->lock); edgetpu_mailbox_external_disable_free_locked(group); mutex_unlock(&group->lock); + edgetpu_wakelock_dec_event_locked(client->wakelock, EDGETPU_WAKELOCK_EVENT_EXT_MAILBOX); + edgetpu_wakelock_unlock(client->wakelock); edgetpu_device_group_put(group); return 0; } @@ -1073,8 +1092,7 @@ void edgetpu_mailbox_external_disable_free_locked(struct edgetpu_device_group *g edgetpu_mailbox_external_free(group); } -int edgetpu_mailbox_enable_ext(struct edgetpu_client *client, int mailbox_id, - struct edgetpu_external_mailbox_req *ext_mailbox_req) +static int edgetpu_mailbox_external_enable_by_id(struct edgetpu_client *client, int mailbox_id) { int ret; @@ -1085,24 +1103,19 @@ int edgetpu_mailbox_enable_ext(struct edgetpu_client *client, int mailbox_id, return -EAGAIN; } - if (mailbox_id == EDGETPU_MAILBOX_ID_USE_ASSOC) { - ret = edgetpu_mailbox_external_alloc_enable(client, ext_mailbox_req); - goto out; - } etdev_dbg(client->etdev, "Enabling mailbox: %d\n", mailbox_id); ret = edgetpu_mailbox_activate(client->etdev, mailbox_id, -1, false); if (ret) etdev_err(client->etdev, "Activate mailbox %d failed: %d", mailbox_id, ret); -out: - if (!ret) + else edgetpu_wakelock_inc_event_locked(client->wakelock, EDGETPU_WAKELOCK_EVENT_EXT_MAILBOX); edgetpu_wakelock_unlock(client->wakelock); return ret; } -int edgetpu_mailbox_disable_ext(struct edgetpu_client *client, int mailbox_id) +static int edgetpu_mailbox_external_disable_by_id(struct edgetpu_client *client, int mailbox_id) { int ret = 0; @@ -1118,21 +1131,31 @@ int edgetpu_mailbox_disable_ext(struct edgetpu_client *client, int mailbox_id) return -EAGAIN; } - if (mailbox_id == EDGETPU_MAILBOX_ID_USE_ASSOC) { - ret = edgetpu_mailbox_external_disable_free(client); - goto out; - } etdev_dbg(client->etdev, "Disabling mailbox: %d\n", mailbox_id); edgetpu_mailbox_deactivate(client->etdev, mailbox_id); -out: - if (!ret) - edgetpu_wakelock_dec_event_locked(client->wakelock, - EDGETPU_WAKELOCK_EVENT_EXT_MAILBOX); + edgetpu_wakelock_dec_event_locked(client->wakelock, EDGETPU_WAKELOCK_EVENT_EXT_MAILBOX); edgetpu_wakelock_unlock(client->wakelock); return ret; } +int edgetpu_mailbox_enable_ext(struct edgetpu_client *client, int mailbox_id, + struct edgetpu_external_mailbox_req *ext_mailbox_req) +{ + if (mailbox_id == EDGETPU_MAILBOX_ID_USE_ASSOC) + return edgetpu_mailbox_external_alloc_enable(client, ext_mailbox_req); + else + return edgetpu_mailbox_external_enable_by_id(client, mailbox_id); +} + +int edgetpu_mailbox_disable_ext(struct edgetpu_client *client, int mailbox_id) +{ + if (mailbox_id == EDGETPU_MAILBOX_ID_USE_ASSOC) + return edgetpu_mailbox_external_disable_free(client); + else + return edgetpu_mailbox_external_disable_by_id(client, mailbox_id); +} + int edgetpu_mailbox_activate(struct edgetpu_dev *etdev, u32 mailbox_id, s16 vcid, bool first_open) { struct edgetpu_handshake *eh = &etdev->mailbox_manager->open_devices; diff --git a/drivers/edgetpu/edgetpu-mcp.h b/drivers/edgetpu/edgetpu-mcp.h index 4530d79..4d762c4 100644 --- a/drivers/edgetpu/edgetpu-mcp.h +++ b/drivers/edgetpu/edgetpu-mcp.h @@ -9,7 +9,9 @@ #include <linux/init.h> #include <linux/mutex.h> +#include <linux/spinlock.h> #include <linux/types.h> +#include <linux/workqueue.h> #include "edgetpu-config.h" #include "edgetpu-internal.h" @@ -38,6 +40,11 @@ struct edgetpu_mcp { * One should check with !IS_ERR_OR_NULL(etdevs[i]) before accessing. */ struct edgetpu_dev **etdevs; + + /* MCP-wide fatal errors pending runtime notification */ + uint errors_pending_mask; + spinlock_t errors_pending_lock; + struct work_struct errors_pending_work; /* for notify via kworker */ }; #ifdef EDGETPU_HAS_MCP diff --git a/drivers/edgetpu/edgetpu-mobile-platform.c b/drivers/edgetpu/edgetpu-mobile-platform.c index ffa8928..4e9cac0 100644 --- a/drivers/edgetpu/edgetpu-mobile-platform.c +++ b/drivers/edgetpu/edgetpu-mobile-platform.c @@ -125,6 +125,86 @@ static void edgetpu_platform_cleanup_fw_region(struct edgetpu_mobile_platform_de etmdev->shared_mem_vaddr = NULL; } +static int mobile_check_ext_mailbox_args(const char *func, struct edgetpu_dev *etdev, + struct edgetpu_ext_mailbox_ioctl *args) +{ + if (args->type != EDGETPU_EXT_MAILBOX_TYPE_TZ) { + etdev_err(etdev, "%s: Invalid type %d != %d\n", func, args->type, + EDGETPU_EXT_MAILBOX_TYPE_TZ); + return -EINVAL; + } + if (args->count != 1) { + etdev_err(etdev, "%s: Invalid mailbox count: %d != 1\n", func, args->count); + return -EINVAL; + } + return 0; +} + +int edgetpu_chip_acquire_ext_mailbox(struct edgetpu_client *client, + struct edgetpu_ext_mailbox_ioctl *args) +{ + struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(client->etdev); + int ret; + + ret = mobile_check_ext_mailbox_args(__func__, client->etdev, args); + if (ret) + return ret; + + mutex_lock(&etmdev->tz_mailbox_lock); + if (etmdev->secure_client) { + etdev_err(client->etdev, "TZ mailbox already in use by PID %d\n", + etmdev->secure_client->pid); + mutex_unlock(&etmdev->tz_mailbox_lock); + return -EBUSY; + } + ret = edgetpu_mailbox_enable_ext(client, EDGETPU_TZ_MAILBOX_ID, NULL); + if (!ret) + etmdev->secure_client = client; + mutex_unlock(&etmdev->tz_mailbox_lock); + return ret; +} + +int edgetpu_chip_release_ext_mailbox(struct edgetpu_client *client, + struct edgetpu_ext_mailbox_ioctl *args) +{ + struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(client->etdev); + int ret = 0; + + ret = mobile_check_ext_mailbox_args(__func__, client->etdev, + args); + if (ret) + return ret; + + mutex_lock(&etmdev->tz_mailbox_lock); + if (!etmdev->secure_client) { + etdev_warn(client->etdev, "TZ mailbox already released\n"); + mutex_unlock(&etmdev->tz_mailbox_lock); + return 0; + } + if (etmdev->secure_client != client) { + etdev_err(client->etdev, + "TZ mailbox owned by different client\n"); + mutex_unlock(&etmdev->tz_mailbox_lock); + return -EBUSY; + } + etmdev->secure_client = NULL; + ret = edgetpu_mailbox_disable_ext(client, EDGETPU_TZ_MAILBOX_ID); + mutex_unlock(&etmdev->tz_mailbox_lock); + return ret; +} + +void edgetpu_chip_client_remove(struct edgetpu_client *client) +{ + struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(client->etdev); + + mutex_lock(&etmdev->tz_mailbox_lock); + if (etmdev->secure_client == client) { + etmdev->secure_client = NULL; + edgetpu_mailbox_disable_ext(client, EDGETPU_TZ_MAILBOX_ID); + } + mutex_unlock(&etmdev->tz_mailbox_lock); +} + int edgetpu_chip_setup_mmu(struct edgetpu_dev *etdev) { int ret; diff --git a/drivers/edgetpu/edgetpu-mobile-platform.h b/drivers/edgetpu/edgetpu-mobile-platform.h index 65184ae..9d41571 100644 --- a/drivers/edgetpu/edgetpu-mobile-platform.h +++ b/drivers/edgetpu/edgetpu-mobile-platform.h @@ -22,6 +22,7 @@ #include "edgetpu-config.h" #include "edgetpu-internal.h" +#include "mobile-debug-dump.h" #define to_mobile_dev(etdev) container_of(etdev, struct edgetpu_mobile_platform_dev, edgetpu_dev) @@ -93,6 +94,8 @@ struct edgetpu_mobile_platform_dev { #if IS_ENABLED(CONFIG_GOOGLE_BCL) struct bcl_device *bcl_dev; #endif + /* subsystem coredump info struct */ + struct mobile_sscd_info sscd_info; /* Protects TZ Mailbox client pointer */ struct mutex tz_mailbox_lock; /* TZ mailbox client */ diff --git a/drivers/edgetpu/mobile-debug-dump.c b/drivers/edgetpu/mobile-debug-dump.c index 3732fbb..3bb7b3b 100644 --- a/drivers/edgetpu/mobile-debug-dump.c +++ b/drivers/edgetpu/mobile-debug-dump.c @@ -8,16 +8,36 @@ #include <linux/mutex.h> #include <linux/platform_data/sscoredump.h> +#include <linux/platform_device.h> #include <linux/rbtree.h> #include <linux/slab.h> +#include "edgetpu-config.h" #include "edgetpu-device-group.h" #include "edgetpu-mailbox.h" +#include "edgetpu-mobile-platform.h" #include "mobile-debug-dump.h" #include "edgetpu-debug-dump.c" -struct mobile_sscd_mappings_dump * +static void sscd_release(struct device *dev) +{ + pr_debug(DRIVER_NAME " release\n"); +} + +static struct sscd_platform_data sscd_pdata; +static struct platform_device sscd_dev; + +/* + * Collects the mapping information of all the host mapping and dmabuf mapping buffers of all + * @groups as an array of struct mobile_sscd_mappings_dump and populates the @sscd_seg. + * + * Returns the pointer to the first element of the mappings dump array. The allocated array should + * be freed by the caller after the sscd segment is reported. + * Returns a negative errno in case of failure. + * Returns NULL when there is no mapping allocated in groups. + */ +static struct mobile_sscd_mappings_dump * mobile_sscd_collect_mappings_segment(struct edgetpu_device_group **groups, size_t num_groups, struct sscd_segment *sscd_seg) { @@ -25,19 +45,24 @@ mobile_sscd_collect_mappings_segment(struct edgetpu_device_group **groups, size_ struct edgetpu_mapping_root *mappings; struct rb_node *node; void *resized_arr; - size_t idx = 0, mappings_num = 0, new_size = 0; + size_t idx = 0, mappings_num = 0, new_size = 0, count; - mappings_dump = kmalloc(sizeof(struct mobile_sscd_mappings_dump), GFP_KERNEL); + mappings_dump = NULL; for (idx = 0; idx < num_groups; idx++) { mutex_lock(&groups[idx]->lock); - new_size += groups[idx]->host_mappings.count * - sizeof(struct mobile_sscd_mappings_dump); + count = groups[idx]->host_mappings.count + groups[idx]->dmabuf_mappings.count; + if (count == 0) { + mutex_unlock(&groups[idx]->lock); + continue; + } + new_size += count * sizeof(*mappings_dump); resized_arr = krealloc(mappings_dump, new_size, GFP_KERNEL); if (!resized_arr) { kfree(mappings_dump); mutex_unlock(&groups[idx]->lock); - return NULL; + return ERR_PTR(-ENOMEM); } + mappings_dump = resized_arr; mappings = &groups[idx]->host_mappings; for (node = rb_first(&mappings->rb); node; node = rb_next(node)) { @@ -46,19 +71,9 @@ mobile_sscd_collect_mappings_segment(struct edgetpu_device_group **groups, size_ mappings_dump[mappings_num].host_address = map->host_address; mappings_dump[mappings_num].device_address = map->device_address; - mappings_dump[mappings_num].alloc_iova = map->alloc_iova; - mappings_dump[mappings_num].size = (u64)map->alloc_size; + mappings_dump[mappings_num].size = (u64)map->map_size; mappings_num++; } - new_size += groups[idx]->dmabuf_mappings.count * - sizeof(struct mobile_sscd_mappings_dump); - resized_arr = krealloc(mappings_dump, new_size, GFP_KERNEL); - if (!resized_arr) { - kfree(mappings_dump); - mutex_unlock(&groups[idx]->lock); - return NULL; - } - mappings = &groups[idx]->dmabuf_mappings; for (node = rb_first(&mappings->rb); node; node = rb_next(node)) { struct edgetpu_mapping *map = @@ -66,8 +81,7 @@ mobile_sscd_collect_mappings_segment(struct edgetpu_device_group **groups, size_ mappings_dump[mappings_num].host_address = map->host_address; mappings_dump[mappings_num].device_address = map->device_address; - mappings_dump[mappings_num].alloc_iova = map->alloc_iova; - mappings_dump[mappings_num].size = (u64)map->alloc_size; + mappings_dump[mappings_num].size = (u64)map->map_size; mappings_num++; } mutex_unlock(&groups[idx]->lock); @@ -80,40 +94,49 @@ mobile_sscd_collect_mappings_segment(struct edgetpu_device_group **groups, size_ return mappings_dump; } -size_t mobile_sscd_collect_cmd_resp_queues(struct edgetpu_dev *etdev, - struct edgetpu_device_group **groups, size_t num_groups, - struct sscd_segment *sscd_seg_arr) +/* + * Collects the VII cmd and resp queues of all @groups that @etdev belongs to and the KCI cmd and + * resp queues and populates them as @sscd_seg_arr elements. + * + * Returns the total number of queues collected since some queues may have been released for groups + * with detached mailboxes. The return value is less than or equal to the total number of queues + * expected based on @num_groups i.e. (2 * @num_groups +2). + */ +static size_t mobile_sscd_collect_cmd_resp_queues(struct edgetpu_dev *etdev, + struct edgetpu_device_group **groups, + size_t num_groups, + struct sscd_segment *sscd_seg_arr) { struct edgetpu_kci *kci; size_t idx; u16 num_queues = 0; - // Collect VII cmd and resp queues + /* Collect VII cmd and resp queues */ for (idx = 0; idx < num_groups; idx++) { mutex_lock(&groups[idx]->lock); if (!edgetpu_group_mailbox_detached_locked(groups[idx])) { sscd_seg_arr[num_queues].addr = - (void *)groups[idx]->vii.cmd_queue_mem.vaddr; + (void *)groups[idx]->vii.cmd_queue_mem.vaddr; sscd_seg_arr[num_queues].size = groups[idx]->vii.cmd_queue_mem.size; sscd_seg_arr[num_queues].paddr = - (void *)groups[idx]->vii.cmd_queue_mem.tpu_addr; + (void *)groups[idx]->vii.cmd_queue_mem.tpu_addr; sscd_seg_arr[num_queues].vaddr = - (void *)groups[idx]->vii.cmd_queue_mem.vaddr; + (void *)groups[idx]->vii.cmd_queue_mem.vaddr; num_queues++; sscd_seg_arr[num_queues].addr = - (void *)groups[idx]->vii.resp_queue_mem.vaddr; + (void *)groups[idx]->vii.resp_queue_mem.vaddr; sscd_seg_arr[num_queues].size = groups[idx]->vii.resp_queue_mem.size; sscd_seg_arr[num_queues].paddr = - (void *)groups[idx]->vii.resp_queue_mem.tpu_addr; + (void *)groups[idx]->vii.resp_queue_mem.tpu_addr; sscd_seg_arr[num_queues].vaddr = - (void *)groups[idx]->vii.resp_queue_mem.vaddr; + (void *)groups[idx]->vii.resp_queue_mem.vaddr; num_queues++; } mutex_unlock(&groups[idx]->lock); } - // Collect KCI cmd and resp queues + /* Collect KCI cmd and resp queues */ kci = etdev->kci; sscd_seg_arr[num_queues].addr = (void *)kci->cmd_queue_mem.vaddr; sscd_seg_arr[num_queues].size = MAX_QUEUE_SIZE * sizeof(struct edgetpu_command_element); @@ -122,11 +145,201 @@ size_t mobile_sscd_collect_cmd_resp_queues(struct edgetpu_dev *etdev, num_queues++; sscd_seg_arr[num_queues].addr = (void *)kci->resp_queue_mem.vaddr; - sscd_seg_arr[num_queues].size = MAX_QUEUE_SIZE * - sizeof(struct edgetpu_kci_response_element); + sscd_seg_arr[num_queues].size = + MAX_QUEUE_SIZE * sizeof(struct edgetpu_kci_response_element); sscd_seg_arr[num_queues].paddr = (void *)kci->resp_queue_mem.tpu_addr; sscd_seg_arr[num_queues].vaddr = (void *)kci->resp_queue_mem.vaddr; num_queues++; return num_queues; } + +static int mobile_sscd_generate_coredump(void *p_etdev, void *p_dump_setup) +{ + struct edgetpu_dev *etdev; + struct edgetpu_debug_dump_setup *dump_setup; + struct edgetpu_mobile_platform_dev *pdev; + struct sscd_platform_data *pdata; + struct platform_device *sscd_dev; + struct sscd_segment *segs; + struct edgetpu_debug_dump *debug_dump; + struct edgetpu_crash_reason *crash_reason; + struct edgetpu_dump_segment *dump_seg; + struct edgetpu_device_group *group; + struct edgetpu_device_group **groups; + struct edgetpu_list_group *g; + struct mobile_sscd_mappings_dump *mappings_dump = NULL; + char crash_info[128]; + int sscd_dump_segments_num; + int i, ret; + size_t num_groups = 0, num_queues = 0; + u64 offset; + + if (!p_etdev || !p_dump_setup) + return -EINVAL; + + etdev = (struct edgetpu_dev *)p_etdev; + dump_setup = (struct edgetpu_debug_dump_setup *)p_dump_setup; + pdev = to_mobile_dev(etdev); + pdata = (struct sscd_platform_data *)pdev->sscd_info.pdata; + sscd_dev = (struct platform_device *)pdev->sscd_info.dev; + if (!pdata->sscd_report) { + etdev_err(etdev, "failed to generate coredump"); + return -ENOENT; + } + + debug_dump = (struct edgetpu_debug_dump *)(dump_setup + 1); + + /* Populate crash reason */ + crash_reason = + (struct edgetpu_crash_reason *)((u8 *)dump_setup + debug_dump->crash_reason_offset); + scnprintf(crash_info, sizeof(crash_info), "[edgetpu_coredump] error code: %#llx", + crash_reason->code); + + mutex_lock(&etdev->groups_lock); + groups = kmalloc_array(etdev->n_groups, sizeof(*groups), GFP_KERNEL); + if (!groups) { + mutex_unlock(&etdev->groups_lock); + return -ENOMEM; + } + + etdev_for_each_group(etdev, g, group) { + if (edgetpu_device_group_is_disbanded(group)) + continue; + groups[num_groups++] = edgetpu_device_group_get(group); + } + mutex_unlock(&etdev->groups_lock); + + /* Allocate memory for dump segments */ + sscd_dump_segments_num = debug_dump->dump_segments_num; + sscd_dump_segments_num += 2 * num_groups; /* VII cmd and resp queues */ + sscd_dump_segments_num += num_groups ? 1 : 0; /* Mappings info */ + sscd_dump_segments_num += 2; /* KCI cmd and resp queues */ + + segs = kmalloc_array(sscd_dump_segments_num, sizeof(struct sscd_segment), GFP_KERNEL); + if (!segs) { + ret = -ENOMEM; + goto out_sscd_generate_coredump; + } + + /* Populate sscd segments */ + dump_seg = (struct edgetpu_dump_segment *)((u8 *)dump_setup + + debug_dump->dump_segments_offset); + offset = debug_dump->dump_segments_offset; + for (i = 0; i < debug_dump->dump_segments_num; i++) { + segs[i].addr = dump_seg; + segs[i].size = sizeof(struct edgetpu_dump_segment) + dump_seg->size; + segs[i].paddr = (void *)(etdev->debug_dump_mem.tpu_addr + offset); + segs[i].vaddr = (void *)(etdev->debug_dump_mem.vaddr + offset); + offset += sizeof(struct edgetpu_dump_segment) + dump_seg->size; + dump_seg = (struct edgetpu_dump_segment *)((u8 *)dump_setup + + ALIGN(offset, sizeof(uint64_t))); + } + + if (num_groups) { + mappings_dump = mobile_sscd_collect_mappings_segment(groups, num_groups, &segs[i]); + if (IS_ERR(mappings_dump)) { + ret = PTR_ERR(mappings_dump); + goto out_sscd_generate_coredump; + } + /* increase @i if mappings present */ + if (mappings_dump) + i++; + else + sscd_dump_segments_num--; + } + + num_queues = mobile_sscd_collect_cmd_resp_queues(etdev, groups, num_groups, &segs[i]); + + /* + * Adjust num of segments as some groups may have a detached mailbox. + * Subtract number of VII and KCI queues according to num_groups. + */ + sscd_dump_segments_num -= (2 * num_groups + 2); + sscd_dump_segments_num += num_queues; /* Add actual number of valid VII and KCI queues */ + + /* Pass dump data to SSCD daemon */ + etdev_dbg(etdev, "report: %d segments", sscd_dump_segments_num); + ret = pdata->sscd_report(sscd_dev, segs, sscd_dump_segments_num, SSCD_FLAGS_ELFARM64HDR, + crash_info); +out_sscd_generate_coredump: + for (i = 0; i < num_groups; i++) + edgetpu_device_group_put(groups[i]); + kfree(mappings_dump); + kfree(segs); + kfree(groups); + + return ret; +} + +int edgetpu_debug_dump_init(struct edgetpu_dev *etdev) +{ + size_t size; + int ret; + struct edgetpu_debug_dump_setup *dump_setup; + struct edgetpu_mobile_platform_dev *pdev; + + pdev = to_mobile_dev(etdev); + + size = EDGETPU_DEBUG_DUMP_MEM_SIZE; + + sscd_dev = (struct platform_device) { + .name = DRIVER_NAME, + .driver_override = SSCD_NAME, + .id = PLATFORM_DEVID_NONE, + .dev = { + .platform_data = &sscd_pdata, + .release = sscd_release, + }, + }; + /* Register SSCD platform device */ + ret = platform_device_register(&sscd_dev); + if (ret) { + etdev_err(etdev, "SSCD platform device registration failed: %d", ret); + return ret; + } + /* + * Allocate a buffer for various dump segments + */ + ret = edgetpu_alloc_coherent(etdev, size, &etdev->debug_dump_mem, EDGETPU_CONTEXT_KCI); + if (ret) { + etdev_err(etdev, "Debug dump seg alloc failed"); + etdev->debug_dump_mem.vaddr = NULL; + goto out_unregister_platform; + } + dump_setup = (struct edgetpu_debug_dump_setup *)etdev->debug_dump_mem.vaddr; + memset(dump_setup, 0, size); + dump_setup->dump_mem_size = size; + + /* + * Allocate memory for debug dump handlers + */ + etdev->debug_dump_handlers = + kcalloc(DUMP_REASON_NUM, sizeof(*etdev->debug_dump_handlers), GFP_KERNEL); + if (!etdev->debug_dump_handlers) + return -ENOMEM; + etdev->debug_dump_handlers[DUMP_REASON_REQ_BY_USER] = mobile_sscd_generate_coredump; + etdev->debug_dump_handlers[DUMP_REASON_RECOVERABLE_FAULT] = mobile_sscd_generate_coredump; + + pdev->sscd_info.pdata = &sscd_pdata; + pdev->sscd_info.dev = &sscd_dev; + edgetpu_setup_debug_dump_fs(etdev); + return ret; +out_unregister_platform: + platform_device_unregister(&sscd_dev); + return ret; +} + +void edgetpu_debug_dump_exit(struct edgetpu_dev *etdev) +{ + if (!etdev->debug_dump_mem.vaddr) { + etdev_dbg(etdev, "Debug dump not allocated"); + return; + } + /* + * Free the memory assigned for debug dump + */ + edgetpu_free_coherent(etdev, &etdev->debug_dump_mem, EDGETPU_CONTEXT_KCI); + kfree(etdev->debug_dump_handlers); + platform_device_unregister(&sscd_dev); +} diff --git a/drivers/edgetpu/mobile-debug-dump.h b/drivers/edgetpu/mobile-debug-dump.h index 0a9aef9..0ebd5f2 100644 --- a/drivers/edgetpu/mobile-debug-dump.h +++ b/drivers/edgetpu/mobile-debug-dump.h @@ -19,34 +19,7 @@ struct mobile_sscd_info { struct mobile_sscd_mappings_dump { u64 host_address; u64 device_address; - u64 alloc_iova; u64 size; }; -struct sscd_segment; - -/* - * Collects the mapping information of all the host mapping and dmabuf mapping buffers of all - * @groups as an array of struct mobile_sscd_mappings_dump and populates the @sscd_seg. - * - * Returns the pointer to the first element of the mappings dump array. The allocated array should - * be freed by the caller after the sscd segment is reported. - * Returns NULL in case of failure. - */ -struct mobile_sscd_mappings_dump * -mobile_sscd_collect_mappings_segment(struct edgetpu_device_group **groups, size_t num_groups, - struct sscd_segment *sscd_seg); - -/* - * Collects the VII cmd and resp queues of all @groups that @etdev belongs to and the KCI cmd and - * resp queues and populates them as @sscd_seg_arr elements. - * - * Returns the total number of queues collected since some queues may have been released for groups - * with detached mailboxes. The return value is less than or equal to the total number of queues - * expected based on @num_groups i.e. (2 * @num_groups +2). - */ -size_t mobile_sscd_collect_cmd_resp_queues(struct edgetpu_dev *etdev, - struct edgetpu_device_group **groups, size_t num_groups, - struct sscd_segment *sscd_seg_arr); - #endif /* MOBILE_DEBUG_DUMP_H_ */ |