1d87f36a0SRajneesh Bhardwaj // SPDX-License-Identifier: GPL-2.0 OR MIT 2f3a39818SAndrew Lewycky /* 3d87f36a0SRajneesh Bhardwaj * Copyright 2014-2022 Advanced Micro Devices, Inc. 4f3a39818SAndrew Lewycky * 5f3a39818SAndrew Lewycky * Permission is hereby granted, free of charge, to any person obtaining a 6f3a39818SAndrew Lewycky * copy of this software and associated documentation files (the "Software"), 7f3a39818SAndrew Lewycky * to deal in the Software without restriction, including without limitation 8f3a39818SAndrew Lewycky * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9f3a39818SAndrew Lewycky * and/or sell copies of the Software, and to permit persons to whom the 10f3a39818SAndrew Lewycky * Software is furnished to do so, subject to the following conditions: 11f3a39818SAndrew Lewycky * 12f3a39818SAndrew Lewycky * The above copyright notice and this permission notice shall be included in 13f3a39818SAndrew Lewycky * all copies or substantial portions of the Software. 14f3a39818SAndrew Lewycky * 15f3a39818SAndrew Lewycky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16f3a39818SAndrew Lewycky * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17f3a39818SAndrew Lewycky * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18f3a39818SAndrew Lewycky * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 19f3a39818SAndrew Lewycky * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20f3a39818SAndrew Lewycky * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21f3a39818SAndrew Lewycky * OTHER DEALINGS IN THE SOFTWARE. 22f3a39818SAndrew Lewycky */ 23f3a39818SAndrew Lewycky 24f3a39818SAndrew Lewycky #include <linux/mm_types.h> 25f3a39818SAndrew Lewycky #include <linux/slab.h> 26f3a39818SAndrew Lewycky #include <linux/types.h> 273f07c014SIngo Molnar #include <linux/sched/signal.h> 289b56bb11SFelix Kuehling #include <linux/sched/mm.h> 29f3a39818SAndrew Lewycky #include <linux/uaccess.h> 30f3a39818SAndrew Lewycky #include <linux/mman.h> 31f3a39818SAndrew Lewycky #include <linux/memory.h> 32f3a39818SAndrew Lewycky #include "kfd_priv.h" 33f3a39818SAndrew Lewycky #include "kfd_events.h" 3464d1c3a4SFelix Kuehling #include "kfd_iommu.h" 3559d3e8beSAlexey Skidanov #include <linux/device.h> 36f3a39818SAndrew Lewycky 37f3a39818SAndrew Lewycky /* 3874e40716SFelix Kuehling * Wrapper around wait_queue_entry_t 39f3a39818SAndrew Lewycky */ 40f3a39818SAndrew Lewycky struct kfd_event_waiter { 4174e40716SFelix Kuehling wait_queue_entry_t wait; 4274e40716SFelix Kuehling struct kfd_event *event; /* Event to wait for */ 4374e40716SFelix Kuehling bool activated; /* Becomes true when event is signaled */ 44f3a39818SAndrew Lewycky }; 45f3a39818SAndrew Lewycky 46f3a39818SAndrew Lewycky /* 47f3a39818SAndrew Lewycky * Each signal event needs a 64-bit signal slot where the signaler will write 48482f0777SFelix Kuehling * a 1 before sending an interrupt. (This is needed because some interrupts 49f3a39818SAndrew Lewycky * do not contain enough spare data bits to identify an event.) 50482f0777SFelix Kuehling * We get whole pages and map them to the process VA. 51482f0777SFelix Kuehling * Individual signal events use their event_id as slot index. 52f3a39818SAndrew Lewycky */ 5350cb7dd9SFelix Kuehling struct kfd_signal_page { 54f3a39818SAndrew Lewycky uint64_t *kernel_address; 55f3a39818SAndrew Lewycky uint64_t __user *user_address; 560fc8011fSFelix Kuehling bool need_to_free_pages; 57f3a39818SAndrew Lewycky }; 58f3a39818SAndrew Lewycky 5950cb7dd9SFelix Kuehling static uint64_t *page_slots(struct kfd_signal_page *page) 60f3a39818SAndrew Lewycky { 61f3a39818SAndrew Lewycky return page->kernel_address; 62f3a39818SAndrew Lewycky } 63f3a39818SAndrew Lewycky 6450cb7dd9SFelix Kuehling static struct kfd_signal_page *allocate_signal_page(struct kfd_process *p) 65f3a39818SAndrew Lewycky { 66f3a39818SAndrew Lewycky void *backing_store; 6750cb7dd9SFelix Kuehling struct kfd_signal_page *page; 68f3a39818SAndrew Lewycky 6950cb7dd9SFelix Kuehling page = kzalloc(sizeof(*page), GFP_KERNEL); 70f3a39818SAndrew Lewycky if (!page) 7150cb7dd9SFelix Kuehling return NULL; 72f3a39818SAndrew Lewycky 7350cb7dd9SFelix Kuehling backing_store = (void *) __get_free_pages(GFP_KERNEL, 74f3a39818SAndrew Lewycky get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); 75f3a39818SAndrew Lewycky if (!backing_store) 76f3a39818SAndrew Lewycky goto fail_alloc_signal_store; 77f3a39818SAndrew Lewycky 7850cb7dd9SFelix Kuehling /* Initialize all events to unsignaled */ 79f3a39818SAndrew Lewycky memset(backing_store, (uint8_t) UNSIGNALED_EVENT_SLOT, 80f3a39818SAndrew Lewycky KFD_SIGNAL_EVENT_LIMIT * 8); 81f3a39818SAndrew Lewycky 82f3a39818SAndrew Lewycky page->kernel_address = backing_store; 830fc8011fSFelix Kuehling page->need_to_free_pages = true; 8479775b62SKent Russell pr_debug("Allocated new event signal page at %p, for process %p\n", 85f3a39818SAndrew Lewycky page, p); 86f3a39818SAndrew Lewycky 8750cb7dd9SFelix Kuehling return page; 88f3a39818SAndrew Lewycky 89f3a39818SAndrew Lewycky fail_alloc_signal_store: 90f3a39818SAndrew Lewycky kfree(page); 9150cb7dd9SFelix Kuehling return NULL; 9250cb7dd9SFelix Kuehling } 9350cb7dd9SFelix Kuehling 94482f0777SFelix Kuehling static int allocate_event_notification_slot(struct kfd_process *p, 9540e8a766SDavid Yat Sin struct kfd_event *ev, 9640e8a766SDavid Yat Sin const int *restore_id) 9750cb7dd9SFelix Kuehling { 98482f0777SFelix Kuehling int id; 99482f0777SFelix Kuehling 10050cb7dd9SFelix Kuehling if (!p->signal_page) { 10150cb7dd9SFelix Kuehling p->signal_page = allocate_signal_page(p); 10250cb7dd9SFelix Kuehling if (!p->signal_page) 103482f0777SFelix Kuehling return -ENOMEM; 104b9a5d0a5SFelix Kuehling /* Oldest user mode expects 256 event slots */ 105b9a5d0a5SFelix Kuehling p->signal_mapped_size = 256*8; 106f3a39818SAndrew Lewycky } 107f3a39818SAndrew Lewycky 10840e8a766SDavid Yat Sin if (restore_id) { 10940e8a766SDavid Yat Sin id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1, 11040e8a766SDavid Yat Sin GFP_KERNEL); 11140e8a766SDavid Yat Sin } else { 112b9a5d0a5SFelix Kuehling /* 113b9a5d0a5SFelix Kuehling * Compatibility with old user mode: Only use signal slots 114b9a5d0a5SFelix Kuehling * user mode has mapped, may be less than 115b9a5d0a5SFelix Kuehling * KFD_SIGNAL_EVENT_LIMIT. This also allows future increase 116b9a5d0a5SFelix Kuehling * of the event limit without breaking user mode. 117b9a5d0a5SFelix Kuehling */ 118b9a5d0a5SFelix Kuehling id = idr_alloc(&p->event_idr, ev, 0, p->signal_mapped_size / 8, 119482f0777SFelix Kuehling GFP_KERNEL); 12040e8a766SDavid Yat Sin } 121482f0777SFelix Kuehling if (id < 0) 122482f0777SFelix Kuehling return id; 123f3a39818SAndrew Lewycky 124482f0777SFelix Kuehling ev->event_id = id; 125482f0777SFelix Kuehling page_slots(p->signal_page)[id] = UNSIGNALED_EVENT_SLOT; 126f3a39818SAndrew Lewycky 127482f0777SFelix Kuehling return 0; 128f3a39818SAndrew Lewycky } 129f3a39818SAndrew Lewycky 130f3a39818SAndrew Lewycky /* 1315273e82cSFelix Kuehling * Assumes that p->event_mutex or rcu_readlock is held and of course that p is 1325273e82cSFelix Kuehling * not going away. 133f3a39818SAndrew Lewycky */ 134f3a39818SAndrew Lewycky static struct kfd_event *lookup_event_by_id(struct kfd_process *p, uint32_t id) 135f3a39818SAndrew Lewycky { 136482f0777SFelix Kuehling return idr_find(&p->event_idr, id); 137f3a39818SAndrew Lewycky } 138f3a39818SAndrew Lewycky 1393f04f961SFelix Kuehling /** 1403f04f961SFelix Kuehling * lookup_signaled_event_by_partial_id - Lookup signaled event from partial ID 1413f04f961SFelix Kuehling * @p: Pointer to struct kfd_process 1423f04f961SFelix Kuehling * @id: ID to look up 1433f04f961SFelix Kuehling * @bits: Number of valid bits in @id 1443f04f961SFelix Kuehling * 1453f04f961SFelix Kuehling * Finds the first signaled event with a matching partial ID. If no 1463f04f961SFelix Kuehling * matching signaled event is found, returns NULL. In that case the 1473f04f961SFelix Kuehling * caller should assume that the partial ID is invalid and do an 1483f04f961SFelix Kuehling * exhaustive search of all siglaned events. 1493f04f961SFelix Kuehling * 1503f04f961SFelix Kuehling * If multiple events with the same partial ID signal at the same 1513f04f961SFelix Kuehling * time, they will be found one interrupt at a time, not necessarily 1523f04f961SFelix Kuehling * in the same order the interrupts occurred. As long as the number of 1533f04f961SFelix Kuehling * interrupts is correct, all signaled events will be seen by the 1543f04f961SFelix Kuehling * driver. 1553f04f961SFelix Kuehling */ 1563f04f961SFelix Kuehling static struct kfd_event *lookup_signaled_event_by_partial_id( 1573f04f961SFelix Kuehling struct kfd_process *p, uint32_t id, uint32_t bits) 1583f04f961SFelix Kuehling { 1593f04f961SFelix Kuehling struct kfd_event *ev; 1603f04f961SFelix Kuehling 1613f04f961SFelix Kuehling if (!p->signal_page || id >= KFD_SIGNAL_EVENT_LIMIT) 1623f04f961SFelix Kuehling return NULL; 1633f04f961SFelix Kuehling 1643f04f961SFelix Kuehling /* Fast path for the common case that @id is not a partial ID 1653f04f961SFelix Kuehling * and we only need a single lookup. 1663f04f961SFelix Kuehling */ 1673f04f961SFelix Kuehling if (bits > 31 || (1U << bits) >= KFD_SIGNAL_EVENT_LIMIT) { 1683f04f961SFelix Kuehling if (page_slots(p->signal_page)[id] == UNSIGNALED_EVENT_SLOT) 1693f04f961SFelix Kuehling return NULL; 1703f04f961SFelix Kuehling 1713f04f961SFelix Kuehling return idr_find(&p->event_idr, id); 1723f04f961SFelix Kuehling } 1733f04f961SFelix Kuehling 1743f04f961SFelix Kuehling /* General case for partial IDs: Iterate over all matching IDs 1753f04f961SFelix Kuehling * and find the first one that has signaled. 1763f04f961SFelix Kuehling */ 1773f04f961SFelix Kuehling for (ev = NULL; id < KFD_SIGNAL_EVENT_LIMIT && !ev; id += 1U << bits) { 1783f04f961SFelix Kuehling if (page_slots(p->signal_page)[id] == UNSIGNALED_EVENT_SLOT) 1793f04f961SFelix Kuehling continue; 1803f04f961SFelix Kuehling 1813f04f961SFelix Kuehling ev = idr_find(&p->event_idr, id); 1823f04f961SFelix Kuehling } 1833f04f961SFelix Kuehling 1843f04f961SFelix Kuehling return ev; 1853f04f961SFelix Kuehling } 1863f04f961SFelix Kuehling 18740e8a766SDavid Yat Sin static int create_signal_event(struct file *devkfd, struct kfd_process *p, 18840e8a766SDavid Yat Sin struct kfd_event *ev, const int *restore_id) 189f3a39818SAndrew Lewycky { 190482f0777SFelix Kuehling int ret; 191482f0777SFelix Kuehling 192b9a5d0a5SFelix Kuehling if (p->signal_mapped_size && 193b9a5d0a5SFelix Kuehling p->signal_event_count == p->signal_mapped_size / 8) { 194c986169fSFelix Kuehling if (!p->signal_event_limit_reached) { 1958f2e0c03SYong Zhao pr_debug("Signal event wasn't created because limit was reached\n"); 196c986169fSFelix Kuehling p->signal_event_limit_reached = true; 197c986169fSFelix Kuehling } 198482f0777SFelix Kuehling return -ENOSPC; 199f3a39818SAndrew Lewycky } 200f3a39818SAndrew Lewycky 20140e8a766SDavid Yat Sin ret = allocate_event_notification_slot(p, ev, restore_id); 202482f0777SFelix Kuehling if (ret) { 20379775b62SKent Russell pr_warn("Signal event wasn't created because out of kernel memory\n"); 204482f0777SFelix Kuehling return ret; 205f3a39818SAndrew Lewycky } 206f3a39818SAndrew Lewycky 207f3a39818SAndrew Lewycky p->signal_event_count++; 208f3a39818SAndrew Lewycky 209482f0777SFelix Kuehling ev->user_signal_address = &p->signal_page->user_address[ev->event_id]; 21079775b62SKent Russell pr_debug("Signal event number %zu created with id %d, address %p\n", 2116235e15eSOded Gabbay p->signal_event_count, ev->event_id, 2126235e15eSOded Gabbay ev->user_signal_address); 2136235e15eSOded Gabbay 214f3a39818SAndrew Lewycky return 0; 215f3a39818SAndrew Lewycky } 216f3a39818SAndrew Lewycky 21740e8a766SDavid Yat Sin static int create_other_event(struct kfd_process *p, struct kfd_event *ev, const int *restore_id) 218f3a39818SAndrew Lewycky { 21940e8a766SDavid Yat Sin int id; 22040e8a766SDavid Yat Sin 22140e8a766SDavid Yat Sin if (restore_id) 22240e8a766SDavid Yat Sin id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1, 22340e8a766SDavid Yat Sin GFP_KERNEL); 22440e8a766SDavid Yat Sin else 225482f0777SFelix Kuehling /* Cast KFD_LAST_NONSIGNAL_EVENT to uint32_t. This allows an 226482f0777SFelix Kuehling * intentional integer overflow to -1 without a compiler 227482f0777SFelix Kuehling * warning. idr_alloc treats a negative value as "maximum 228482f0777SFelix Kuehling * signed integer". 229482f0777SFelix Kuehling */ 23040e8a766SDavid Yat Sin id = idr_alloc(&p->event_idr, ev, KFD_FIRST_NONSIGNAL_EVENT_ID, 231482f0777SFelix Kuehling (uint32_t)KFD_LAST_NONSIGNAL_EVENT_ID + 1, 232482f0777SFelix Kuehling GFP_KERNEL); 233482f0777SFelix Kuehling 234482f0777SFelix Kuehling if (id < 0) 235482f0777SFelix Kuehling return id; 236482f0777SFelix Kuehling ev->event_id = id; 237f3a39818SAndrew Lewycky 238f3a39818SAndrew Lewycky return 0; 239f3a39818SAndrew Lewycky } 240f3a39818SAndrew Lewycky 241c3eb12dfSFelix Kuehling int kfd_event_init_process(struct kfd_process *p) 242f3a39818SAndrew Lewycky { 243c3eb12dfSFelix Kuehling int id; 244c3eb12dfSFelix Kuehling 245f3a39818SAndrew Lewycky mutex_init(&p->event_mutex); 246482f0777SFelix Kuehling idr_init(&p->event_idr); 24750cb7dd9SFelix Kuehling p->signal_page = NULL; 248c3eb12dfSFelix Kuehling p->signal_event_count = 1; 249c3eb12dfSFelix Kuehling /* Allocate event ID 0. It is used for a fast path to ignore bogus events 250c3eb12dfSFelix Kuehling * that are sent by the CP without a context ID 251c3eb12dfSFelix Kuehling */ 252c3eb12dfSFelix Kuehling id = idr_alloc(&p->event_idr, NULL, 0, 1, GFP_KERNEL); 253c3eb12dfSFelix Kuehling if (id < 0) { 254c3eb12dfSFelix Kuehling idr_destroy(&p->event_idr); 255c3eb12dfSFelix Kuehling mutex_destroy(&p->event_mutex); 256c3eb12dfSFelix Kuehling return id; 257c3eb12dfSFelix Kuehling } 258c3eb12dfSFelix Kuehling return 0; 259f3a39818SAndrew Lewycky } 260f3a39818SAndrew Lewycky 261f3a39818SAndrew Lewycky static void destroy_event(struct kfd_process *p, struct kfd_event *ev) 262f3a39818SAndrew Lewycky { 26374e40716SFelix Kuehling struct kfd_event_waiter *waiter; 264fe528c13SFelix Kuehling 26574e40716SFelix Kuehling /* Wake up pending waiters. They will return failure */ 2665273e82cSFelix Kuehling spin_lock(&ev->lock); 26774e40716SFelix Kuehling list_for_each_entry(waiter, &ev->wq.head, wait.entry) 2685273e82cSFelix Kuehling WRITE_ONCE(waiter->event, NULL); 26974e40716SFelix Kuehling wake_up_all(&ev->wq); 2705273e82cSFelix Kuehling spin_unlock(&ev->lock); 271fe528c13SFelix Kuehling 272482f0777SFelix Kuehling if (ev->type == KFD_EVENT_TYPE_SIGNAL || 273482f0777SFelix Kuehling ev->type == KFD_EVENT_TYPE_DEBUG) 274f3a39818SAndrew Lewycky p->signal_event_count--; 275f3a39818SAndrew Lewycky 276482f0777SFelix Kuehling idr_remove(&p->event_idr, ev->event_id); 27734d292d5SFelix Kuehling kfree_rcu(ev, rcu); 278f3a39818SAndrew Lewycky } 279f3a39818SAndrew Lewycky 280f3a39818SAndrew Lewycky static void destroy_events(struct kfd_process *p) 281f3a39818SAndrew Lewycky { 282f3a39818SAndrew Lewycky struct kfd_event *ev; 283482f0777SFelix Kuehling uint32_t id; 284f3a39818SAndrew Lewycky 285482f0777SFelix Kuehling idr_for_each_entry(&p->event_idr, ev, id) 286c3eb12dfSFelix Kuehling if (ev) 287f3a39818SAndrew Lewycky destroy_event(p, ev); 288482f0777SFelix Kuehling idr_destroy(&p->event_idr); 289c3eb12dfSFelix Kuehling mutex_destroy(&p->event_mutex); 290f3a39818SAndrew Lewycky } 291f3a39818SAndrew Lewycky 292f3a39818SAndrew Lewycky /* 293f3a39818SAndrew Lewycky * We assume that the process is being destroyed and there is no need to 294f3a39818SAndrew Lewycky * unmap the pages or keep bookkeeping data in order. 295f3a39818SAndrew Lewycky */ 29650cb7dd9SFelix Kuehling static void shutdown_signal_page(struct kfd_process *p) 297f3a39818SAndrew Lewycky { 29850cb7dd9SFelix Kuehling struct kfd_signal_page *page = p->signal_page; 299f3a39818SAndrew Lewycky 30050cb7dd9SFelix Kuehling if (page) { 3010fc8011fSFelix Kuehling if (page->need_to_free_pages) 302f3a39818SAndrew Lewycky free_pages((unsigned long)page->kernel_address, 303f3a39818SAndrew Lewycky get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); 304f3a39818SAndrew Lewycky kfree(page); 305f3a39818SAndrew Lewycky } 306f3a39818SAndrew Lewycky } 307f3a39818SAndrew Lewycky 308f3a39818SAndrew Lewycky void kfd_event_free_process(struct kfd_process *p) 309f3a39818SAndrew Lewycky { 310f3a39818SAndrew Lewycky destroy_events(p); 31150cb7dd9SFelix Kuehling shutdown_signal_page(p); 312f3a39818SAndrew Lewycky } 313f3a39818SAndrew Lewycky 314f3a39818SAndrew Lewycky static bool event_can_be_gpu_signaled(const struct kfd_event *ev) 315f3a39818SAndrew Lewycky { 316f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL || 317f3a39818SAndrew Lewycky ev->type == KFD_EVENT_TYPE_DEBUG; 318f3a39818SAndrew Lewycky } 319f3a39818SAndrew Lewycky 320f3a39818SAndrew Lewycky static bool event_can_be_cpu_signaled(const struct kfd_event *ev) 321f3a39818SAndrew Lewycky { 322f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL; 323f3a39818SAndrew Lewycky } 324f3a39818SAndrew Lewycky 32540e8a766SDavid Yat Sin static int kfd_event_page_set(struct kfd_process *p, void *kernel_address, 32640e8a766SDavid Yat Sin uint64_t size, uint64_t user_handle) 3270fc8011fSFelix Kuehling { 3280fc8011fSFelix Kuehling struct kfd_signal_page *page; 3290fc8011fSFelix Kuehling 3300fc8011fSFelix Kuehling if (p->signal_page) 3310fc8011fSFelix Kuehling return -EBUSY; 3320fc8011fSFelix Kuehling 3330fc8011fSFelix Kuehling page = kzalloc(sizeof(*page), GFP_KERNEL); 3340fc8011fSFelix Kuehling if (!page) 3350fc8011fSFelix Kuehling return -ENOMEM; 3360fc8011fSFelix Kuehling 3370fc8011fSFelix Kuehling /* Initialize all events to unsignaled */ 3380fc8011fSFelix Kuehling memset(kernel_address, (uint8_t) UNSIGNALED_EVENT_SLOT, 3390fc8011fSFelix Kuehling KFD_SIGNAL_EVENT_LIMIT * 8); 3400fc8011fSFelix Kuehling 3410fc8011fSFelix Kuehling page->kernel_address = kernel_address; 3420fc8011fSFelix Kuehling 3430fc8011fSFelix Kuehling p->signal_page = page; 3440fc8011fSFelix Kuehling p->signal_mapped_size = size; 34540e8a766SDavid Yat Sin p->signal_handle = user_handle; 3460fc8011fSFelix Kuehling return 0; 3470fc8011fSFelix Kuehling } 3480fc8011fSFelix Kuehling 34940e8a766SDavid Yat Sin int kfd_kmap_event_page(struct kfd_process *p, uint64_t event_page_offset) 35040e8a766SDavid Yat Sin { 3518dc1db31SMukul Joshi struct kfd_node *kfd; 35240e8a766SDavid Yat Sin struct kfd_process_device *pdd; 35340e8a766SDavid Yat Sin void *mem, *kern_addr; 35440e8a766SDavid Yat Sin uint64_t size; 35540e8a766SDavid Yat Sin int err = 0; 35640e8a766SDavid Yat Sin 35740e8a766SDavid Yat Sin if (p->signal_page) { 35840e8a766SDavid Yat Sin pr_err("Event page is already set\n"); 35940e8a766SDavid Yat Sin return -EINVAL; 36040e8a766SDavid Yat Sin } 36140e8a766SDavid Yat Sin 362bef153b7SDavid Yat Sin pdd = kfd_process_device_data_by_id(p, GET_GPU_ID(event_page_offset)); 363bef153b7SDavid Yat Sin if (!pdd) { 36440e8a766SDavid Yat Sin pr_err("Getting device by id failed in %s\n", __func__); 36540e8a766SDavid Yat Sin return -EINVAL; 36640e8a766SDavid Yat Sin } 367bef153b7SDavid Yat Sin kfd = pdd->dev; 36840e8a766SDavid Yat Sin 36940e8a766SDavid Yat Sin pdd = kfd_bind_process_to_device(kfd, p); 37040e8a766SDavid Yat Sin if (IS_ERR(pdd)) 37140e8a766SDavid Yat Sin return PTR_ERR(pdd); 37240e8a766SDavid Yat Sin 37340e8a766SDavid Yat Sin mem = kfd_process_device_translate_handle(pdd, 37440e8a766SDavid Yat Sin GET_IDR_HANDLE(event_page_offset)); 37540e8a766SDavid Yat Sin if (!mem) { 37640e8a766SDavid Yat Sin pr_err("Can't find BO, offset is 0x%llx\n", event_page_offset); 37740e8a766SDavid Yat Sin return -EINVAL; 37840e8a766SDavid Yat Sin } 37940e8a766SDavid Yat Sin 3804e2d1044SFelix Kuehling err = amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(mem, &kern_addr, &size); 38140e8a766SDavid Yat Sin if (err) { 38240e8a766SDavid Yat Sin pr_err("Failed to map event page to kernel\n"); 38340e8a766SDavid Yat Sin return err; 38440e8a766SDavid Yat Sin } 38540e8a766SDavid Yat Sin 38640e8a766SDavid Yat Sin err = kfd_event_page_set(p, kern_addr, size, event_page_offset); 38740e8a766SDavid Yat Sin if (err) { 38840e8a766SDavid Yat Sin pr_err("Failed to set event page\n"); 3894e2d1044SFelix Kuehling amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(mem); 39040e8a766SDavid Yat Sin return err; 39140e8a766SDavid Yat Sin } 39240e8a766SDavid Yat Sin return err; 39340e8a766SDavid Yat Sin } 39440e8a766SDavid Yat Sin 395f3a39818SAndrew Lewycky int kfd_event_create(struct file *devkfd, struct kfd_process *p, 396f3a39818SAndrew Lewycky uint32_t event_type, bool auto_reset, uint32_t node_id, 397f3a39818SAndrew Lewycky uint32_t *event_id, uint32_t *event_trigger_data, 398f3a39818SAndrew Lewycky uint64_t *event_page_offset, uint32_t *event_slot_index) 399f3a39818SAndrew Lewycky { 400f3a39818SAndrew Lewycky int ret = 0; 401f3a39818SAndrew Lewycky struct kfd_event *ev = kzalloc(sizeof(*ev), GFP_KERNEL); 402f3a39818SAndrew Lewycky 403f3a39818SAndrew Lewycky if (!ev) 404f3a39818SAndrew Lewycky return -ENOMEM; 405f3a39818SAndrew Lewycky 406f3a39818SAndrew Lewycky ev->type = event_type; 407f3a39818SAndrew Lewycky ev->auto_reset = auto_reset; 408f3a39818SAndrew Lewycky ev->signaled = false; 409f3a39818SAndrew Lewycky 4105273e82cSFelix Kuehling spin_lock_init(&ev->lock); 41174e40716SFelix Kuehling init_waitqueue_head(&ev->wq); 412f3a39818SAndrew Lewycky 413f3a39818SAndrew Lewycky *event_page_offset = 0; 414f3a39818SAndrew Lewycky 415f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 416f3a39818SAndrew Lewycky 417f3a39818SAndrew Lewycky switch (event_type) { 418f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_SIGNAL: 419f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_DEBUG: 42040e8a766SDavid Yat Sin ret = create_signal_event(devkfd, p, ev, NULL); 421f3a39818SAndrew Lewycky if (!ret) { 422df03ef93SHarish Kasiviswanathan *event_page_offset = KFD_MMAP_TYPE_EVENTS; 423482f0777SFelix Kuehling *event_slot_index = ev->event_id; 424f3a39818SAndrew Lewycky } 425f3a39818SAndrew Lewycky break; 426f3a39818SAndrew Lewycky default: 42740e8a766SDavid Yat Sin ret = create_other_event(p, ev, NULL); 428f3a39818SAndrew Lewycky break; 429f3a39818SAndrew Lewycky } 430f3a39818SAndrew Lewycky 431f3a39818SAndrew Lewycky if (!ret) { 432f3a39818SAndrew Lewycky *event_id = ev->event_id; 433f3a39818SAndrew Lewycky *event_trigger_data = ev->event_id; 434f3a39818SAndrew Lewycky } else { 435f3a39818SAndrew Lewycky kfree(ev); 436f3a39818SAndrew Lewycky } 437f3a39818SAndrew Lewycky 438f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 439f3a39818SAndrew Lewycky 440f3a39818SAndrew Lewycky return ret; 441f3a39818SAndrew Lewycky } 442f3a39818SAndrew Lewycky 44340e8a766SDavid Yat Sin int kfd_criu_restore_event(struct file *devkfd, 44440e8a766SDavid Yat Sin struct kfd_process *p, 44540e8a766SDavid Yat Sin uint8_t __user *user_priv_ptr, 44640e8a766SDavid Yat Sin uint64_t *priv_data_offset, 44740e8a766SDavid Yat Sin uint64_t max_priv_data_size) 44840e8a766SDavid Yat Sin { 44940e8a766SDavid Yat Sin struct kfd_criu_event_priv_data *ev_priv; 45040e8a766SDavid Yat Sin struct kfd_event *ev = NULL; 45140e8a766SDavid Yat Sin int ret = 0; 45240e8a766SDavid Yat Sin 45340e8a766SDavid Yat Sin ev_priv = kmalloc(sizeof(*ev_priv), GFP_KERNEL); 45440e8a766SDavid Yat Sin if (!ev_priv) 45540e8a766SDavid Yat Sin return -ENOMEM; 45640e8a766SDavid Yat Sin 45740e8a766SDavid Yat Sin ev = kzalloc(sizeof(*ev), GFP_KERNEL); 45840e8a766SDavid Yat Sin if (!ev) { 45940e8a766SDavid Yat Sin ret = -ENOMEM; 46040e8a766SDavid Yat Sin goto exit; 46140e8a766SDavid Yat Sin } 46240e8a766SDavid Yat Sin 46340e8a766SDavid Yat Sin if (*priv_data_offset + sizeof(*ev_priv) > max_priv_data_size) { 46440e8a766SDavid Yat Sin ret = -EINVAL; 46540e8a766SDavid Yat Sin goto exit; 46640e8a766SDavid Yat Sin } 46740e8a766SDavid Yat Sin 46840e8a766SDavid Yat Sin ret = copy_from_user(ev_priv, user_priv_ptr + *priv_data_offset, sizeof(*ev_priv)); 46940e8a766SDavid Yat Sin if (ret) { 47040e8a766SDavid Yat Sin ret = -EFAULT; 47140e8a766SDavid Yat Sin goto exit; 47240e8a766SDavid Yat Sin } 47340e8a766SDavid Yat Sin *priv_data_offset += sizeof(*ev_priv); 47440e8a766SDavid Yat Sin 47540e8a766SDavid Yat Sin if (ev_priv->user_handle) { 47640e8a766SDavid Yat Sin ret = kfd_kmap_event_page(p, ev_priv->user_handle); 47740e8a766SDavid Yat Sin if (ret) 47840e8a766SDavid Yat Sin goto exit; 47940e8a766SDavid Yat Sin } 48040e8a766SDavid Yat Sin 48140e8a766SDavid Yat Sin ev->type = ev_priv->type; 48240e8a766SDavid Yat Sin ev->auto_reset = ev_priv->auto_reset; 48340e8a766SDavid Yat Sin ev->signaled = ev_priv->signaled; 48440e8a766SDavid Yat Sin 4855273e82cSFelix Kuehling spin_lock_init(&ev->lock); 48640e8a766SDavid Yat Sin init_waitqueue_head(&ev->wq); 48740e8a766SDavid Yat Sin 48840e8a766SDavid Yat Sin mutex_lock(&p->event_mutex); 48940e8a766SDavid Yat Sin switch (ev->type) { 49040e8a766SDavid Yat Sin case KFD_EVENT_TYPE_SIGNAL: 49140e8a766SDavid Yat Sin case KFD_EVENT_TYPE_DEBUG: 49240e8a766SDavid Yat Sin ret = create_signal_event(devkfd, p, ev, &ev_priv->event_id); 49340e8a766SDavid Yat Sin break; 49440e8a766SDavid Yat Sin case KFD_EVENT_TYPE_MEMORY: 49540e8a766SDavid Yat Sin memcpy(&ev->memory_exception_data, 49640e8a766SDavid Yat Sin &ev_priv->memory_exception_data, 49740e8a766SDavid Yat Sin sizeof(struct kfd_hsa_memory_exception_data)); 49840e8a766SDavid Yat Sin 49940e8a766SDavid Yat Sin ret = create_other_event(p, ev, &ev_priv->event_id); 50040e8a766SDavid Yat Sin break; 50140e8a766SDavid Yat Sin case KFD_EVENT_TYPE_HW_EXCEPTION: 50240e8a766SDavid Yat Sin memcpy(&ev->hw_exception_data, 50340e8a766SDavid Yat Sin &ev_priv->hw_exception_data, 50440e8a766SDavid Yat Sin sizeof(struct kfd_hsa_hw_exception_data)); 50540e8a766SDavid Yat Sin 50640e8a766SDavid Yat Sin ret = create_other_event(p, ev, &ev_priv->event_id); 50740e8a766SDavid Yat Sin break; 50840e8a766SDavid Yat Sin } 50966f79037SFelix Kuehling mutex_unlock(&p->event_mutex); 51040e8a766SDavid Yat Sin 51140e8a766SDavid Yat Sin exit: 51240e8a766SDavid Yat Sin if (ret) 51340e8a766SDavid Yat Sin kfree(ev); 51440e8a766SDavid Yat Sin 51540e8a766SDavid Yat Sin kfree(ev_priv); 51640e8a766SDavid Yat Sin 51740e8a766SDavid Yat Sin return ret; 51840e8a766SDavid Yat Sin } 51940e8a766SDavid Yat Sin 52040e8a766SDavid Yat Sin int kfd_criu_checkpoint_events(struct kfd_process *p, 52140e8a766SDavid Yat Sin uint8_t __user *user_priv_data, 52240e8a766SDavid Yat Sin uint64_t *priv_data_offset) 52340e8a766SDavid Yat Sin { 52440e8a766SDavid Yat Sin struct kfd_criu_event_priv_data *ev_privs; 52540e8a766SDavid Yat Sin int i = 0; 52640e8a766SDavid Yat Sin int ret = 0; 52740e8a766SDavid Yat Sin struct kfd_event *ev; 52840e8a766SDavid Yat Sin uint32_t ev_id; 52940e8a766SDavid Yat Sin 53040e8a766SDavid Yat Sin uint32_t num_events = kfd_get_num_events(p); 53140e8a766SDavid Yat Sin 53240e8a766SDavid Yat Sin if (!num_events) 53340e8a766SDavid Yat Sin return 0; 53440e8a766SDavid Yat Sin 53540e8a766SDavid Yat Sin ev_privs = kvzalloc(num_events * sizeof(*ev_privs), GFP_KERNEL); 53640e8a766SDavid Yat Sin if (!ev_privs) 53740e8a766SDavid Yat Sin return -ENOMEM; 53840e8a766SDavid Yat Sin 53940e8a766SDavid Yat Sin 54040e8a766SDavid Yat Sin idr_for_each_entry(&p->event_idr, ev, ev_id) { 54140e8a766SDavid Yat Sin struct kfd_criu_event_priv_data *ev_priv; 54240e8a766SDavid Yat Sin 54340e8a766SDavid Yat Sin /* 54440e8a766SDavid Yat Sin * Currently, all events have same size of private_data, but the current ioctl's 54540e8a766SDavid Yat Sin * and CRIU plugin supports private_data of variable sizes 54640e8a766SDavid Yat Sin */ 54740e8a766SDavid Yat Sin ev_priv = &ev_privs[i]; 54840e8a766SDavid Yat Sin 54940e8a766SDavid Yat Sin ev_priv->object_type = KFD_CRIU_OBJECT_TYPE_EVENT; 55040e8a766SDavid Yat Sin 55140e8a766SDavid Yat Sin /* We store the user_handle with the first event */ 55240e8a766SDavid Yat Sin if (i == 0 && p->signal_page) 55340e8a766SDavid Yat Sin ev_priv->user_handle = p->signal_handle; 55440e8a766SDavid Yat Sin 55540e8a766SDavid Yat Sin ev_priv->event_id = ev->event_id; 55640e8a766SDavid Yat Sin ev_priv->auto_reset = ev->auto_reset; 55740e8a766SDavid Yat Sin ev_priv->type = ev->type; 55840e8a766SDavid Yat Sin ev_priv->signaled = ev->signaled; 55940e8a766SDavid Yat Sin 56040e8a766SDavid Yat Sin if (ev_priv->type == KFD_EVENT_TYPE_MEMORY) 56140e8a766SDavid Yat Sin memcpy(&ev_priv->memory_exception_data, 56240e8a766SDavid Yat Sin &ev->memory_exception_data, 56340e8a766SDavid Yat Sin sizeof(struct kfd_hsa_memory_exception_data)); 56440e8a766SDavid Yat Sin else if (ev_priv->type == KFD_EVENT_TYPE_HW_EXCEPTION) 56540e8a766SDavid Yat Sin memcpy(&ev_priv->hw_exception_data, 56640e8a766SDavid Yat Sin &ev->hw_exception_data, 56740e8a766SDavid Yat Sin sizeof(struct kfd_hsa_hw_exception_data)); 56840e8a766SDavid Yat Sin 56940e8a766SDavid Yat Sin pr_debug("Checkpointed event[%d] id = 0x%08x auto_reset = %x type = %x signaled = %x\n", 57040e8a766SDavid Yat Sin i, 57140e8a766SDavid Yat Sin ev_priv->event_id, 57240e8a766SDavid Yat Sin ev_priv->auto_reset, 57340e8a766SDavid Yat Sin ev_priv->type, 57440e8a766SDavid Yat Sin ev_priv->signaled); 57540e8a766SDavid Yat Sin i++; 57640e8a766SDavid Yat Sin } 57740e8a766SDavid Yat Sin 57840e8a766SDavid Yat Sin ret = copy_to_user(user_priv_data + *priv_data_offset, 57940e8a766SDavid Yat Sin ev_privs, num_events * sizeof(*ev_privs)); 58040e8a766SDavid Yat Sin if (ret) { 58140e8a766SDavid Yat Sin pr_err("Failed to copy events priv to user\n"); 58240e8a766SDavid Yat Sin ret = -EFAULT; 58340e8a766SDavid Yat Sin } 58440e8a766SDavid Yat Sin 58540e8a766SDavid Yat Sin *priv_data_offset += num_events * sizeof(*ev_privs); 58640e8a766SDavid Yat Sin 58740e8a766SDavid Yat Sin kvfree(ev_privs); 58840e8a766SDavid Yat Sin return ret; 58940e8a766SDavid Yat Sin } 59040e8a766SDavid Yat Sin 59140e8a766SDavid Yat Sin int kfd_get_num_events(struct kfd_process *p) 59240e8a766SDavid Yat Sin { 59340e8a766SDavid Yat Sin struct kfd_event *ev; 59440e8a766SDavid Yat Sin uint32_t id; 59540e8a766SDavid Yat Sin u32 num_events = 0; 59640e8a766SDavid Yat Sin 59740e8a766SDavid Yat Sin idr_for_each_entry(&p->event_idr, ev, id) 59840e8a766SDavid Yat Sin num_events++; 59940e8a766SDavid Yat Sin 60040e8a766SDavid Yat Sin return num_events; 60140e8a766SDavid Yat Sin } 60240e8a766SDavid Yat Sin 603f3a39818SAndrew Lewycky /* Assumes that p is current. */ 604f3a39818SAndrew Lewycky int kfd_event_destroy(struct kfd_process *p, uint32_t event_id) 605f3a39818SAndrew Lewycky { 606f3a39818SAndrew Lewycky struct kfd_event *ev; 607f3a39818SAndrew Lewycky int ret = 0; 608f3a39818SAndrew Lewycky 609f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 610f3a39818SAndrew Lewycky 611f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 612f3a39818SAndrew Lewycky 613f3a39818SAndrew Lewycky if (ev) 614f3a39818SAndrew Lewycky destroy_event(p, ev); 615f3a39818SAndrew Lewycky else 616f3a39818SAndrew Lewycky ret = -EINVAL; 617f3a39818SAndrew Lewycky 618f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 619f3a39818SAndrew Lewycky return ret; 620f3a39818SAndrew Lewycky } 621f3a39818SAndrew Lewycky 622f3a39818SAndrew Lewycky static void set_event(struct kfd_event *ev) 623f3a39818SAndrew Lewycky { 624f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter; 625f3a39818SAndrew Lewycky 62674e40716SFelix Kuehling /* Auto reset if the list is non-empty and we're waking 62774e40716SFelix Kuehling * someone. waitqueue_active is safe here because we're 6285273e82cSFelix Kuehling * protected by the ev->lock, which is also held when 62974e40716SFelix Kuehling * updating the wait queues in kfd_wait_on_events. 63074e40716SFelix Kuehling */ 63174e40716SFelix Kuehling ev->signaled = !ev->auto_reset || !waitqueue_active(&ev->wq); 632f3a39818SAndrew Lewycky 63374e40716SFelix Kuehling list_for_each_entry(waiter, &ev->wq.head, wait.entry) 6345273e82cSFelix Kuehling WRITE_ONCE(waiter->activated, true); 635f3a39818SAndrew Lewycky 63674e40716SFelix Kuehling wake_up_all(&ev->wq); 637f3a39818SAndrew Lewycky } 638f3a39818SAndrew Lewycky 639f3a39818SAndrew Lewycky /* Assumes that p is current. */ 640f3a39818SAndrew Lewycky int kfd_set_event(struct kfd_process *p, uint32_t event_id) 641f3a39818SAndrew Lewycky { 642f3a39818SAndrew Lewycky int ret = 0; 643f3a39818SAndrew Lewycky struct kfd_event *ev; 644f3a39818SAndrew Lewycky 6455273e82cSFelix Kuehling rcu_read_lock(); 646f3a39818SAndrew Lewycky 647f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 648abb5bc59SDan Carpenter if (!ev) { 649abb5bc59SDan Carpenter ret = -EINVAL; 650abb5bc59SDan Carpenter goto unlock_rcu; 651abb5bc59SDan Carpenter } 6525273e82cSFelix Kuehling spin_lock(&ev->lock); 653f3a39818SAndrew Lewycky 654abb5bc59SDan Carpenter if (event_can_be_cpu_signaled(ev)) 655f3a39818SAndrew Lewycky set_event(ev); 656f3a39818SAndrew Lewycky else 657f3a39818SAndrew Lewycky ret = -EINVAL; 658f3a39818SAndrew Lewycky 6595273e82cSFelix Kuehling spin_unlock(&ev->lock); 660abb5bc59SDan Carpenter unlock_rcu: 6615273e82cSFelix Kuehling rcu_read_unlock(); 662f3a39818SAndrew Lewycky return ret; 663f3a39818SAndrew Lewycky } 664f3a39818SAndrew Lewycky 665f3a39818SAndrew Lewycky static void reset_event(struct kfd_event *ev) 666f3a39818SAndrew Lewycky { 667f3a39818SAndrew Lewycky ev->signaled = false; 668f3a39818SAndrew Lewycky } 669f3a39818SAndrew Lewycky 670f3a39818SAndrew Lewycky /* Assumes that p is current. */ 671f3a39818SAndrew Lewycky int kfd_reset_event(struct kfd_process *p, uint32_t event_id) 672f3a39818SAndrew Lewycky { 673f3a39818SAndrew Lewycky int ret = 0; 674f3a39818SAndrew Lewycky struct kfd_event *ev; 675f3a39818SAndrew Lewycky 6765273e82cSFelix Kuehling rcu_read_lock(); 677f3a39818SAndrew Lewycky 678f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 679abb5bc59SDan Carpenter if (!ev) { 680abb5bc59SDan Carpenter ret = -EINVAL; 681abb5bc59SDan Carpenter goto unlock_rcu; 682abb5bc59SDan Carpenter } 6835273e82cSFelix Kuehling spin_lock(&ev->lock); 684f3a39818SAndrew Lewycky 685abb5bc59SDan Carpenter if (event_can_be_cpu_signaled(ev)) 686f3a39818SAndrew Lewycky reset_event(ev); 687f3a39818SAndrew Lewycky else 688f3a39818SAndrew Lewycky ret = -EINVAL; 689f3a39818SAndrew Lewycky 6905273e82cSFelix Kuehling spin_unlock(&ev->lock); 691abb5bc59SDan Carpenter unlock_rcu: 6925273e82cSFelix Kuehling rcu_read_unlock(); 693f3a39818SAndrew Lewycky return ret; 694f3a39818SAndrew Lewycky 695f3a39818SAndrew Lewycky } 696f3a39818SAndrew Lewycky 697f3a39818SAndrew Lewycky static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev) 698f3a39818SAndrew Lewycky { 6995273e82cSFelix Kuehling WRITE_ONCE(page_slots(p->signal_page)[ev->event_id], UNSIGNALED_EVENT_SLOT); 700f3a39818SAndrew Lewycky } 701f3a39818SAndrew Lewycky 702f3a39818SAndrew Lewycky static void set_event_from_interrupt(struct kfd_process *p, 703f3a39818SAndrew Lewycky struct kfd_event *ev) 704f3a39818SAndrew Lewycky { 705f3a39818SAndrew Lewycky if (ev && event_can_be_gpu_signaled(ev)) { 706f3a39818SAndrew Lewycky acknowledge_signal(p, ev); 7075273e82cSFelix Kuehling spin_lock(&ev->lock); 708f3a39818SAndrew Lewycky set_event(ev); 7095273e82cSFelix Kuehling spin_unlock(&ev->lock); 710f3a39818SAndrew Lewycky } 711f3a39818SAndrew Lewycky } 712f3a39818SAndrew Lewycky 713c7b6bac9SFenghua Yu void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id, 714f3a39818SAndrew Lewycky uint32_t valid_id_bits) 715f3a39818SAndrew Lewycky { 7163f04f961SFelix Kuehling struct kfd_event *ev = NULL; 717f3a39818SAndrew Lewycky 718f3a39818SAndrew Lewycky /* 719f3a39818SAndrew Lewycky * Because we are called from arbitrary context (workqueue) as opposed 720f3a39818SAndrew Lewycky * to process context, kfd_process could attempt to exit while we are 721abb208a8SFelix Kuehling * running so the lookup function increments the process ref count. 722f3a39818SAndrew Lewycky */ 723f3a39818SAndrew Lewycky struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 724f3a39818SAndrew Lewycky 725f3a39818SAndrew Lewycky if (!p) 726f3a39818SAndrew Lewycky return; /* Presumably process exited. */ 727f3a39818SAndrew Lewycky 7285273e82cSFelix Kuehling rcu_read_lock(); 729f3a39818SAndrew Lewycky 7303f04f961SFelix Kuehling if (valid_id_bits) 7313f04f961SFelix Kuehling ev = lookup_signaled_event_by_partial_id(p, partial_id, 7323f04f961SFelix Kuehling valid_id_bits); 7333f04f961SFelix Kuehling if (ev) { 734f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 73550cb7dd9SFelix Kuehling } else if (p->signal_page) { 736f3a39818SAndrew Lewycky /* 7373f04f961SFelix Kuehling * Partial ID lookup failed. Assume that the event ID 7383f04f961SFelix Kuehling * in the interrupt payload was invalid and do an 7393f04f961SFelix Kuehling * exhaustive search of signaled events. 740f3a39818SAndrew Lewycky */ 741482f0777SFelix Kuehling uint64_t *slots = page_slots(p->signal_page); 742482f0777SFelix Kuehling uint32_t id; 743f3a39818SAndrew Lewycky 7443f04f961SFelix Kuehling if (valid_id_bits) 7453f04f961SFelix Kuehling pr_debug_ratelimited("Partial ID invalid: %u (%u valid bits)\n", 7463f04f961SFelix Kuehling partial_id, valid_id_bits); 7473f04f961SFelix Kuehling 748eeb27b7eSFelix Kuehling if (p->signal_event_count < KFD_SIGNAL_EVENT_LIMIT / 64) { 749482f0777SFelix Kuehling /* With relatively few events, it's faster to 750482f0777SFelix Kuehling * iterate over the event IDR 751482f0777SFelix Kuehling */ 752482f0777SFelix Kuehling idr_for_each_entry(&p->event_idr, ev, id) { 753482f0777SFelix Kuehling if (id >= KFD_SIGNAL_EVENT_LIMIT) 754482f0777SFelix Kuehling break; 755482f0777SFelix Kuehling 7565273e82cSFelix Kuehling if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT) 757f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 758f3a39818SAndrew Lewycky } 759482f0777SFelix Kuehling } else { 760482f0777SFelix Kuehling /* With relatively many events, it's faster to 761482f0777SFelix Kuehling * iterate over the signal slots and lookup 762482f0777SFelix Kuehling * only signaled events from the IDR. 763482f0777SFelix Kuehling */ 764c3eb12dfSFelix Kuehling for (id = 1; id < KFD_SIGNAL_EVENT_LIMIT; id++) 7655273e82cSFelix Kuehling if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT) { 766482f0777SFelix Kuehling ev = lookup_event_by_id(p, id); 767482f0777SFelix Kuehling set_event_from_interrupt(p, ev); 768482f0777SFelix Kuehling } 769482f0777SFelix Kuehling } 770f3a39818SAndrew Lewycky } 771f3a39818SAndrew Lewycky 7725273e82cSFelix Kuehling rcu_read_unlock(); 773abb208a8SFelix Kuehling kfd_unref_process(p); 774f3a39818SAndrew Lewycky } 775f3a39818SAndrew Lewycky 776f3a39818SAndrew Lewycky static struct kfd_event_waiter *alloc_event_waiters(uint32_t num_events) 777f3a39818SAndrew Lewycky { 778f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters; 779f3a39818SAndrew Lewycky uint32_t i; 780f3a39818SAndrew Lewycky 7814fc8fff3SQu Huang event_waiters = kcalloc(num_events, sizeof(struct kfd_event_waiter), 782f3a39818SAndrew Lewycky GFP_KERNEL); 783ebbb7bb9SQintaoShen if (!event_waiters) 784ebbb7bb9SQintaoShen return NULL; 785f3a39818SAndrew Lewycky 7864fc8fff3SQu Huang for (i = 0; i < num_events; i++) 78774e40716SFelix Kuehling init_wait(&event_waiters[i].wait); 788f3a39818SAndrew Lewycky 789f3a39818SAndrew Lewycky return event_waiters; 790f3a39818SAndrew Lewycky } 791f3a39818SAndrew Lewycky 792250e64a3SFelix Kuehling static int init_event_waiter(struct kfd_process *p, 793f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter, 794ebf947feSFelix Kuehling uint32_t event_id) 795f3a39818SAndrew Lewycky { 796f3a39818SAndrew Lewycky struct kfd_event *ev = lookup_event_by_id(p, event_id); 797f3a39818SAndrew Lewycky 798f3a39818SAndrew Lewycky if (!ev) 799f3a39818SAndrew Lewycky return -EINVAL; 800f3a39818SAndrew Lewycky 8015273e82cSFelix Kuehling spin_lock(&ev->lock); 80259d3e8beSAlexey Skidanov waiter->event = ev; 803f3a39818SAndrew Lewycky waiter->activated = ev->signaled; 804f3a39818SAndrew Lewycky ev->signaled = ev->signaled && !ev->auto_reset; 805250e64a3SFelix Kuehling if (!waiter->activated) 806250e64a3SFelix Kuehling add_wait_queue(&ev->wq, &waiter->wait); 8075273e82cSFelix Kuehling spin_unlock(&ev->lock); 808f3a39818SAndrew Lewycky 809f3a39818SAndrew Lewycky return 0; 810f3a39818SAndrew Lewycky } 811f3a39818SAndrew Lewycky 812fe528c13SFelix Kuehling /* test_event_condition - Test condition of events being waited for 813fe528c13SFelix Kuehling * @all: Return completion only if all events have signaled 814fe528c13SFelix Kuehling * @num_events: Number of events to wait for 815fe528c13SFelix Kuehling * @event_waiters: Array of event waiters, one per event 816fe528c13SFelix Kuehling * 817fe528c13SFelix Kuehling * Returns KFD_IOC_WAIT_RESULT_COMPLETE if all (or one) event(s) have 818fe528c13SFelix Kuehling * signaled. Returns KFD_IOC_WAIT_RESULT_TIMEOUT if no (or not all) 819fe528c13SFelix Kuehling * events have signaled. Returns KFD_IOC_WAIT_RESULT_FAIL if any of 820fe528c13SFelix Kuehling * the events have been destroyed. 821fe528c13SFelix Kuehling */ 822fe528c13SFelix Kuehling static uint32_t test_event_condition(bool all, uint32_t num_events, 823f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters) 824f3a39818SAndrew Lewycky { 825f3a39818SAndrew Lewycky uint32_t i; 826f3a39818SAndrew Lewycky uint32_t activated_count = 0; 827f3a39818SAndrew Lewycky 828f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 8295273e82cSFelix Kuehling if (!READ_ONCE(event_waiters[i].event)) 830fe528c13SFelix Kuehling return KFD_IOC_WAIT_RESULT_FAIL; 831fe528c13SFelix Kuehling 8325273e82cSFelix Kuehling if (READ_ONCE(event_waiters[i].activated)) { 833f3a39818SAndrew Lewycky if (!all) 834fe528c13SFelix Kuehling return KFD_IOC_WAIT_RESULT_COMPLETE; 835f3a39818SAndrew Lewycky 836f3a39818SAndrew Lewycky activated_count++; 837f3a39818SAndrew Lewycky } 838f3a39818SAndrew Lewycky } 839f3a39818SAndrew Lewycky 840fe528c13SFelix Kuehling return activated_count == num_events ? 841fe528c13SFelix Kuehling KFD_IOC_WAIT_RESULT_COMPLETE : KFD_IOC_WAIT_RESULT_TIMEOUT; 842f3a39818SAndrew Lewycky } 843f3a39818SAndrew Lewycky 84459d3e8beSAlexey Skidanov /* 84559d3e8beSAlexey Skidanov * Copy event specific data, if defined. 84659d3e8beSAlexey Skidanov * Currently only memory exception events have additional data to copy to user 84759d3e8beSAlexey Skidanov */ 848fdf0c833SFelix Kuehling static int copy_signaled_event_data(uint32_t num_events, 84959d3e8beSAlexey Skidanov struct kfd_event_waiter *event_waiters, 85059d3e8beSAlexey Skidanov struct kfd_event_data __user *data) 85159d3e8beSAlexey Skidanov { 85259d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *src; 85359d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data __user *dst; 85459d3e8beSAlexey Skidanov struct kfd_event_waiter *waiter; 85559d3e8beSAlexey Skidanov struct kfd_event *event; 85659d3e8beSAlexey Skidanov uint32_t i; 85759d3e8beSAlexey Skidanov 85859d3e8beSAlexey Skidanov for (i = 0; i < num_events; i++) { 85959d3e8beSAlexey Skidanov waiter = &event_waiters[i]; 86059d3e8beSAlexey Skidanov event = waiter->event; 8615273e82cSFelix Kuehling if (!event) 8625273e82cSFelix Kuehling return -EINVAL; /* event was destroyed */ 86359d3e8beSAlexey Skidanov if (waiter->activated && event->type == KFD_EVENT_TYPE_MEMORY) { 864ebf947feSFelix Kuehling dst = &data[i].memory_exception_data; 86559d3e8beSAlexey Skidanov src = &event->memory_exception_data; 86659d3e8beSAlexey Skidanov if (copy_to_user(dst, src, 86759d3e8beSAlexey Skidanov sizeof(struct kfd_hsa_memory_exception_data))) 868fdf0c833SFelix Kuehling return -EFAULT; 86959d3e8beSAlexey Skidanov } 87059d3e8beSAlexey Skidanov } 87159d3e8beSAlexey Skidanov 872fdf0c833SFelix Kuehling return 0; 87359d3e8beSAlexey Skidanov } 87459d3e8beSAlexey Skidanov 875f3a39818SAndrew Lewycky static long user_timeout_to_jiffies(uint32_t user_timeout_ms) 876f3a39818SAndrew Lewycky { 877f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE) 878f3a39818SAndrew Lewycky return 0; 879f3a39818SAndrew Lewycky 880f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_INFINITE) 881f3a39818SAndrew Lewycky return MAX_SCHEDULE_TIMEOUT; 882f3a39818SAndrew Lewycky 883f3a39818SAndrew Lewycky /* 884f3a39818SAndrew Lewycky * msecs_to_jiffies interprets all values above 2^31-1 as infinite, 885f3a39818SAndrew Lewycky * but we consider them finite. 886f3a39818SAndrew Lewycky * This hack is wrong, but nobody is likely to notice. 887f3a39818SAndrew Lewycky */ 888f3a39818SAndrew Lewycky user_timeout_ms = min_t(uint32_t, user_timeout_ms, 0x7FFFFFFF); 889f3a39818SAndrew Lewycky 890f3a39818SAndrew Lewycky return msecs_to_jiffies(user_timeout_ms) + 1; 891f3a39818SAndrew Lewycky } 892f3a39818SAndrew Lewycky 893bea9a56aSFelix Kuehling static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters, 894bea9a56aSFelix Kuehling bool undo_auto_reset) 895f3a39818SAndrew Lewycky { 896f3a39818SAndrew Lewycky uint32_t i; 897f3a39818SAndrew Lewycky 898f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) 8995273e82cSFelix Kuehling if (waiters[i].event) { 9005273e82cSFelix Kuehling spin_lock(&waiters[i].event->lock); 90174e40716SFelix Kuehling remove_wait_queue(&waiters[i].event->wq, 90274e40716SFelix Kuehling &waiters[i].wait); 903bea9a56aSFelix Kuehling if (undo_auto_reset && waiters[i].activated && 904bea9a56aSFelix Kuehling waiters[i].event && waiters[i].event->auto_reset) 905bea9a56aSFelix Kuehling set_event(waiters[i].event); 9065273e82cSFelix Kuehling spin_unlock(&waiters[i].event->lock); 9075273e82cSFelix Kuehling } 908f3a39818SAndrew Lewycky 909f3a39818SAndrew Lewycky kfree(waiters); 910f3a39818SAndrew Lewycky } 911f3a39818SAndrew Lewycky 912f3a39818SAndrew Lewycky int kfd_wait_on_events(struct kfd_process *p, 91359d3e8beSAlexey Skidanov uint32_t num_events, void __user *data, 914bea9a56aSFelix Kuehling bool all, uint32_t *user_timeout_ms, 915fdf0c833SFelix Kuehling uint32_t *wait_result) 916f3a39818SAndrew Lewycky { 91759d3e8beSAlexey Skidanov struct kfd_event_data __user *events = 91859d3e8beSAlexey Skidanov (struct kfd_event_data __user *) data; 919f3a39818SAndrew Lewycky uint32_t i; 920f3a39818SAndrew Lewycky int ret = 0; 9211f9d09beSSean Keely 922f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters = NULL; 923bea9a56aSFelix Kuehling long timeout = user_timeout_to_jiffies(*user_timeout_ms); 924f3a39818SAndrew Lewycky 925fdf0c833SFelix Kuehling event_waiters = alloc_event_waiters(num_events); 926fdf0c833SFelix Kuehling if (!event_waiters) { 927fdf0c833SFelix Kuehling ret = -ENOMEM; 928fdf0c833SFelix Kuehling goto out; 929fdf0c833SFelix Kuehling } 930fdf0c833SFelix Kuehling 9315273e82cSFelix Kuehling /* Use p->event_mutex here to protect against concurrent creation and 9325273e82cSFelix Kuehling * destruction of events while we initialize event_waiters. 9335273e82cSFelix Kuehling */ 934f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 935f3a39818SAndrew Lewycky 936f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 93759d3e8beSAlexey Skidanov struct kfd_event_data event_data; 938f3a39818SAndrew Lewycky 93959d3e8beSAlexey Skidanov if (copy_from_user(&event_data, &events[i], 9408bf79388SPan Bian sizeof(struct kfd_event_data))) { 9418bf79388SPan Bian ret = -EFAULT; 942fdf0c833SFelix Kuehling goto out_unlock; 9438bf79388SPan Bian } 944f3a39818SAndrew Lewycky 945250e64a3SFelix Kuehling ret = init_event_waiter(p, &event_waiters[i], 946ebf947feSFelix Kuehling event_data.event_id); 947f3a39818SAndrew Lewycky if (ret) 948fdf0c833SFelix Kuehling goto out_unlock; 949f3a39818SAndrew Lewycky } 950f3a39818SAndrew Lewycky 9511f9d09beSSean Keely /* Check condition once. */ 952fe528c13SFelix Kuehling *wait_result = test_event_condition(all, num_events, event_waiters); 953fe528c13SFelix Kuehling if (*wait_result == KFD_IOC_WAIT_RESULT_COMPLETE) { 954fdf0c833SFelix Kuehling ret = copy_signaled_event_data(num_events, 955fdf0c833SFelix Kuehling event_waiters, events); 956fdf0c833SFelix Kuehling goto out_unlock; 957fe528c13SFelix Kuehling } else if (WARN_ON(*wait_result == KFD_IOC_WAIT_RESULT_FAIL)) { 958fe528c13SFelix Kuehling /* This should not happen. Events shouldn't be 959fe528c13SFelix Kuehling * destroyed while we're holding the event_mutex 960fe528c13SFelix Kuehling */ 961fe528c13SFelix Kuehling goto out_unlock; 962fe528c13SFelix Kuehling } 963fe528c13SFelix Kuehling 964f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 965f3a39818SAndrew Lewycky 966f3a39818SAndrew Lewycky while (true) { 967f3a39818SAndrew Lewycky if (fatal_signal_pending(current)) { 968f3a39818SAndrew Lewycky ret = -EINTR; 969f3a39818SAndrew Lewycky break; 970f3a39818SAndrew Lewycky } 971f3a39818SAndrew Lewycky 972f3a39818SAndrew Lewycky if (signal_pending(current)) { 973f3a39818SAndrew Lewycky ret = -ERESTARTSYS; 974bea9a56aSFelix Kuehling if (*user_timeout_ms != KFD_EVENT_TIMEOUT_IMMEDIATE && 975bea9a56aSFelix Kuehling *user_timeout_ms != KFD_EVENT_TIMEOUT_INFINITE) 976bea9a56aSFelix Kuehling *user_timeout_ms = jiffies_to_msecs( 977bea9a56aSFelix Kuehling max(0l, timeout-1)); 978f3a39818SAndrew Lewycky break; 979f3a39818SAndrew Lewycky } 980f3a39818SAndrew Lewycky 981d9aeec4cSSean Keely /* Set task state to interruptible sleep before 982d9aeec4cSSean Keely * checking wake-up conditions. A concurrent wake-up 983d9aeec4cSSean Keely * will put the task back into runnable state. In that 984d9aeec4cSSean Keely * case schedule_timeout will not put the task to 985d9aeec4cSSean Keely * sleep and we'll get a chance to re-check the 986d9aeec4cSSean Keely * updated conditions almost immediately. Otherwise, 987d9aeec4cSSean Keely * this race condition would lead to a soft hang or a 988d9aeec4cSSean Keely * very long sleep. 989d9aeec4cSSean Keely */ 990d9aeec4cSSean Keely set_current_state(TASK_INTERRUPTIBLE); 991d9aeec4cSSean Keely 992fe528c13SFelix Kuehling *wait_result = test_event_condition(all, num_events, 993fe528c13SFelix Kuehling event_waiters); 994fe528c13SFelix Kuehling if (*wait_result != KFD_IOC_WAIT_RESULT_TIMEOUT) 995f3a39818SAndrew Lewycky break; 996f3a39818SAndrew Lewycky 997fe528c13SFelix Kuehling if (timeout <= 0) 998f3a39818SAndrew Lewycky break; 999f3a39818SAndrew Lewycky 1000d9aeec4cSSean Keely timeout = schedule_timeout(timeout); 1001f3a39818SAndrew Lewycky } 1002f3a39818SAndrew Lewycky __set_current_state(TASK_RUNNING); 1003f3a39818SAndrew Lewycky 10045273e82cSFelix Kuehling mutex_lock(&p->event_mutex); 1005fdf0c833SFelix Kuehling /* copy_signaled_event_data may sleep. So this has to happen 1006fdf0c833SFelix Kuehling * after the task state is set back to RUNNING. 10075273e82cSFelix Kuehling * 10085273e82cSFelix Kuehling * The event may also have been destroyed after signaling. So 10095273e82cSFelix Kuehling * copy_signaled_event_data also must confirm that the event 10105273e82cSFelix Kuehling * still exists. Therefore this must be under the p->event_mutex 10115273e82cSFelix Kuehling * which is also held when events are destroyed. 1012fdf0c833SFelix Kuehling */ 1013fdf0c833SFelix Kuehling if (!ret && *wait_result == KFD_IOC_WAIT_RESULT_COMPLETE) 1014fdf0c833SFelix Kuehling ret = copy_signaled_event_data(num_events, 1015fdf0c833SFelix Kuehling event_waiters, events); 1016fdf0c833SFelix Kuehling 1017fdf0c833SFelix Kuehling out_unlock: 1018bea9a56aSFelix Kuehling free_waiters(num_events, event_waiters, ret == -ERESTARTSYS); 1019f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 1020fdf0c833SFelix Kuehling out: 1021fdf0c833SFelix Kuehling if (ret) 1022fdf0c833SFelix Kuehling *wait_result = KFD_IOC_WAIT_RESULT_FAIL; 1023fe528c13SFelix Kuehling else if (*wait_result == KFD_IOC_WAIT_RESULT_FAIL) 1024fe528c13SFelix Kuehling ret = -EIO; 1025f3a39818SAndrew Lewycky 1026f3a39818SAndrew Lewycky return ret; 1027f3a39818SAndrew Lewycky } 1028f3a39818SAndrew Lewycky 1029f3a39818SAndrew Lewycky int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma) 1030f3a39818SAndrew Lewycky { 1031f3a39818SAndrew Lewycky unsigned long pfn; 103250cb7dd9SFelix Kuehling struct kfd_signal_page *page; 1033b9a5d0a5SFelix Kuehling int ret; 1034f3a39818SAndrew Lewycky 1035b9a5d0a5SFelix Kuehling /* check required size doesn't exceed the allocated size */ 1036b9a5d0a5SFelix Kuehling if (get_order(KFD_SIGNAL_EVENT_LIMIT * 8) < 1037f3a39818SAndrew Lewycky get_order(vma->vm_end - vma->vm_start)) { 103879775b62SKent Russell pr_err("Event page mmap requested illegal size\n"); 1039f3a39818SAndrew Lewycky return -EINVAL; 1040f3a39818SAndrew Lewycky } 1041f3a39818SAndrew Lewycky 104250cb7dd9SFelix Kuehling page = p->signal_page; 1043f3a39818SAndrew Lewycky if (!page) { 1044f3a39818SAndrew Lewycky /* Probably KFD bug, but mmap is user-accessible. */ 104550cb7dd9SFelix Kuehling pr_debug("Signal page could not be found\n"); 1046f3a39818SAndrew Lewycky return -EINVAL; 1047f3a39818SAndrew Lewycky } 1048f3a39818SAndrew Lewycky 1049f3a39818SAndrew Lewycky pfn = __pa(page->kernel_address); 1050f3a39818SAndrew Lewycky pfn >>= PAGE_SHIFT; 1051f3a39818SAndrew Lewycky 10521c71222eSSuren Baghdasaryan vm_flags_set(vma, VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE 10531c71222eSSuren Baghdasaryan | VM_DONTDUMP | VM_PFNMAP); 1054f3a39818SAndrew Lewycky 105579775b62SKent Russell pr_debug("Mapping signal page\n"); 1056f3a39818SAndrew Lewycky pr_debug(" start user address == 0x%08lx\n", vma->vm_start); 1057f3a39818SAndrew Lewycky pr_debug(" end user address == 0x%08lx\n", vma->vm_end); 1058f3a39818SAndrew Lewycky pr_debug(" pfn == 0x%016lX\n", pfn); 1059f3a39818SAndrew Lewycky pr_debug(" vm_flags == 0x%08lX\n", vma->vm_flags); 1060f3a39818SAndrew Lewycky pr_debug(" size == 0x%08lX\n", 1061f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start); 1062f3a39818SAndrew Lewycky 1063f3a39818SAndrew Lewycky page->user_address = (uint64_t __user *)vma->vm_start; 1064f3a39818SAndrew Lewycky 1065f3a39818SAndrew Lewycky /* mapping the page to user process */ 1066b9a5d0a5SFelix Kuehling ret = remap_pfn_range(vma, vma->vm_start, pfn, 1067f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start, vma->vm_page_prot); 1068b9a5d0a5SFelix Kuehling if (!ret) 1069b9a5d0a5SFelix Kuehling p->signal_mapped_size = vma->vm_end - vma->vm_start; 1070b9a5d0a5SFelix Kuehling 1071b9a5d0a5SFelix Kuehling return ret; 1072f3a39818SAndrew Lewycky } 107359d3e8beSAlexey Skidanov 107459d3e8beSAlexey Skidanov /* 10755273e82cSFelix Kuehling * Assumes that p is not going away. 107659d3e8beSAlexey Skidanov */ 107759d3e8beSAlexey Skidanov static void lookup_events_by_type_and_signal(struct kfd_process *p, 107859d3e8beSAlexey Skidanov int type, void *event_data) 107959d3e8beSAlexey Skidanov { 108059d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *ev_data; 108159d3e8beSAlexey Skidanov struct kfd_event *ev; 1082482f0777SFelix Kuehling uint32_t id; 108359d3e8beSAlexey Skidanov bool send_signal = true; 108459d3e8beSAlexey Skidanov 108559d3e8beSAlexey Skidanov ev_data = (struct kfd_hsa_memory_exception_data *) event_data; 108659d3e8beSAlexey Skidanov 10875273e82cSFelix Kuehling rcu_read_lock(); 10885273e82cSFelix Kuehling 1089482f0777SFelix Kuehling id = KFD_FIRST_NONSIGNAL_EVENT_ID; 1090482f0777SFelix Kuehling idr_for_each_entry_continue(&p->event_idr, ev, id) 109159d3e8beSAlexey Skidanov if (ev->type == type) { 109259d3e8beSAlexey Skidanov send_signal = false; 109359d3e8beSAlexey Skidanov dev_dbg(kfd_device, 109459d3e8beSAlexey Skidanov "Event found: id %X type %d", 109559d3e8beSAlexey Skidanov ev->event_id, ev->type); 10965273e82cSFelix Kuehling spin_lock(&ev->lock); 109759d3e8beSAlexey Skidanov set_event(ev); 109859d3e8beSAlexey Skidanov if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data) 109959d3e8beSAlexey Skidanov ev->memory_exception_data = *ev_data; 11005273e82cSFelix Kuehling spin_unlock(&ev->lock); 110159d3e8beSAlexey Skidanov } 110259d3e8beSAlexey Skidanov 1103101fee63SMoses Reuben if (type == KFD_EVENT_TYPE_MEMORY) { 1104101fee63SMoses Reuben dev_warn(kfd_device, 11056027b1bfSYong Zhao "Sending SIGSEGV to process %d (pasid 0x%x)", 11066027b1bfSYong Zhao p->lead_thread->pid, p->pasid); 1107101fee63SMoses Reuben send_sig(SIGSEGV, p->lead_thread, 0); 1108101fee63SMoses Reuben } 1109101fee63SMoses Reuben 111059d3e8beSAlexey Skidanov /* Send SIGTERM no event of type "type" has been found*/ 111159d3e8beSAlexey Skidanov if (send_signal) { 111281663016SOded Gabbay if (send_sigterm) { 111359d3e8beSAlexey Skidanov dev_warn(kfd_device, 11146027b1bfSYong Zhao "Sending SIGTERM to process %d (pasid 0x%x)", 11156027b1bfSYong Zhao p->lead_thread->pid, p->pasid); 111659d3e8beSAlexey Skidanov send_sig(SIGTERM, p->lead_thread, 0); 111781663016SOded Gabbay } else { 111881663016SOded Gabbay dev_err(kfd_device, 11196027b1bfSYong Zhao "Process %d (pasid 0x%x) got unhandled exception", 11206027b1bfSYong Zhao p->lead_thread->pid, p->pasid); 112181663016SOded Gabbay } 112259d3e8beSAlexey Skidanov } 11235273e82cSFelix Kuehling 11245273e82cSFelix Kuehling rcu_read_unlock(); 112559d3e8beSAlexey Skidanov } 112659d3e8beSAlexey Skidanov 112764d1c3a4SFelix Kuehling #ifdef KFD_SUPPORT_IOMMU_V2 11288dc1db31SMukul Joshi void kfd_signal_iommu_event(struct kfd_node *dev, u32 pasid, 112959d3e8beSAlexey Skidanov unsigned long address, bool is_write_requested, 113059d3e8beSAlexey Skidanov bool is_execute_requested) 113159d3e8beSAlexey Skidanov { 113259d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data memory_exception_data; 113359d3e8beSAlexey Skidanov struct vm_area_struct *vma; 1134bef153b7SDavid Yat Sin int user_gpu_id; 113559d3e8beSAlexey Skidanov 113659d3e8beSAlexey Skidanov /* 113759d3e8beSAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 113859d3e8beSAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 1139abb208a8SFelix Kuehling * running so the lookup function increments the process ref count. 114059d3e8beSAlexey Skidanov */ 114159d3e8beSAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 11429b56bb11SFelix Kuehling struct mm_struct *mm; 114359d3e8beSAlexey Skidanov 114459d3e8beSAlexey Skidanov if (!p) 114559d3e8beSAlexey Skidanov return; /* Presumably process exited. */ 114659d3e8beSAlexey Skidanov 11479b56bb11SFelix Kuehling /* Take a safe reference to the mm_struct, which may otherwise 11489b56bb11SFelix Kuehling * disappear even while the kfd_process is still referenced. 11499b56bb11SFelix Kuehling */ 11509b56bb11SFelix Kuehling mm = get_task_mm(p->lead_thread); 11519b56bb11SFelix Kuehling if (!mm) { 1152abb208a8SFelix Kuehling kfd_unref_process(p); 11539b56bb11SFelix Kuehling return; /* Process is exiting */ 11549b56bb11SFelix Kuehling } 11559b56bb11SFelix Kuehling 1156bef153b7SDavid Yat Sin user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); 1157bef153b7SDavid Yat Sin if (unlikely(user_gpu_id == -EINVAL)) { 1158bef153b7SDavid Yat Sin WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); 1159bef153b7SDavid Yat Sin return; 1160bef153b7SDavid Yat Sin } 116159d3e8beSAlexey Skidanov memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 116259d3e8beSAlexey Skidanov 1163d8ed45c5SMichel Lespinasse mmap_read_lock(mm); 11649b56bb11SFelix Kuehling vma = find_vma(mm, address); 116559d3e8beSAlexey Skidanov 1166bef153b7SDavid Yat Sin memory_exception_data.gpu_id = user_gpu_id; 116759d3e8beSAlexey Skidanov memory_exception_data.va = address; 116859d3e8beSAlexey Skidanov /* Set failure reason */ 116959d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 117059d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 117159d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 1172359cecddSYong Zhao if (vma && address >= vma->vm_start) { 117359d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 0; 1174359cecddSYong Zhao 117559d3e8beSAlexey Skidanov if (is_write_requested && !(vma->vm_flags & VM_WRITE)) 117659d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 1; 117759d3e8beSAlexey Skidanov else 117859d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 1179359cecddSYong Zhao 118059d3e8beSAlexey Skidanov if (is_execute_requested && !(vma->vm_flags & VM_EXEC)) 118159d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 1; 118259d3e8beSAlexey Skidanov else 118359d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 118459d3e8beSAlexey Skidanov } 118559d3e8beSAlexey Skidanov 1186d8ed45c5SMichel Lespinasse mmap_read_unlock(mm); 11879b56bb11SFelix Kuehling mmput(mm); 118859d3e8beSAlexey Skidanov 11898725aecaSYong Zhao pr_debug("notpresent %d, noexecute %d, readonly %d\n", 11908725aecaSYong Zhao memory_exception_data.failure.NotPresent, 11918725aecaSYong Zhao memory_exception_data.failure.NoExecute, 11928725aecaSYong Zhao memory_exception_data.failure.ReadOnly); 11938725aecaSYong Zhao 11948725aecaSYong Zhao /* Workaround on Raven to not kill the process when memory is freed 11958725aecaSYong Zhao * before IOMMU is able to finish processing all the excessive PPRs 11968725aecaSYong Zhao */ 1197046e674bSGraham Sider 1198046e674bSGraham Sider if (KFD_GC_VERSION(dev) != IP_VERSION(9, 1, 0) && 1199046e674bSGraham Sider KFD_GC_VERSION(dev) != IP_VERSION(9, 2, 2) && 12005273e82cSFelix Kuehling KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0)) 120159d3e8beSAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY, 120259d3e8beSAlexey Skidanov &memory_exception_data); 120359d3e8beSAlexey Skidanov 1204abb208a8SFelix Kuehling kfd_unref_process(p); 120559d3e8beSAlexey Skidanov } 120664d1c3a4SFelix Kuehling #endif /* KFD_SUPPORT_IOMMU_V2 */ 1207930c5ff4SAlexey Skidanov 1208c7b6bac9SFenghua Yu void kfd_signal_hw_exception_event(u32 pasid) 1209930c5ff4SAlexey Skidanov { 1210930c5ff4SAlexey Skidanov /* 1211930c5ff4SAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 1212930c5ff4SAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 1213abb208a8SFelix Kuehling * running so the lookup function increments the process ref count. 1214930c5ff4SAlexey Skidanov */ 1215930c5ff4SAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 1216930c5ff4SAlexey Skidanov 1217930c5ff4SAlexey Skidanov if (!p) 1218930c5ff4SAlexey Skidanov return; /* Presumably process exited. */ 1219930c5ff4SAlexey Skidanov 1220930c5ff4SAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_HW_EXCEPTION, NULL); 1221abb208a8SFelix Kuehling kfd_unref_process(p); 1222930c5ff4SAlexey Skidanov } 12232640c3faSshaoyunl 12248dc1db31SMukul Joshi void kfd_signal_vm_fault_event(struct kfd_node *dev, u32 pasid, 1225c2d2588cSJonathan Kim struct kfd_vm_fault_info *info, 1226c2d2588cSJonathan Kim struct kfd_hsa_memory_exception_data *data) 12272640c3faSshaoyunl { 12282640c3faSshaoyunl struct kfd_event *ev; 12292640c3faSshaoyunl uint32_t id; 12302640c3faSshaoyunl struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 12312640c3faSshaoyunl struct kfd_hsa_memory_exception_data memory_exception_data; 1232bef153b7SDavid Yat Sin int user_gpu_id; 12332640c3faSshaoyunl 12342640c3faSshaoyunl if (!p) 12352640c3faSshaoyunl return; /* Presumably process exited. */ 1236bef153b7SDavid Yat Sin 1237bef153b7SDavid Yat Sin user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); 1238bef153b7SDavid Yat Sin if (unlikely(user_gpu_id == -EINVAL)) { 1239bef153b7SDavid Yat Sin WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); 1240bef153b7SDavid Yat Sin return; 1241bef153b7SDavid Yat Sin } 1242bef153b7SDavid Yat Sin 1243*7386f88aSJonathan Kim /* SoC15 chips and onwards will pass in data from now on. */ 1244*7386f88aSJonathan Kim if (!data) { 12452640c3faSshaoyunl memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 1246bef153b7SDavid Yat Sin memory_exception_data.gpu_id = user_gpu_id; 12470d87c9cfSKent Russell memory_exception_data.failure.imprecise = true; 1248*7386f88aSJonathan Kim 12492640c3faSshaoyunl /* Set failure reason */ 12502640c3faSshaoyunl if (info) { 1251*7386f88aSJonathan Kim memory_exception_data.va = (info->page_addr) << 1252*7386f88aSJonathan Kim PAGE_SHIFT; 12532640c3faSshaoyunl memory_exception_data.failure.NotPresent = 12542640c3faSshaoyunl info->prot_valid ? 1 : 0; 12552640c3faSshaoyunl memory_exception_data.failure.NoExecute = 12562640c3faSshaoyunl info->prot_exec ? 1 : 0; 12572640c3faSshaoyunl memory_exception_data.failure.ReadOnly = 12582640c3faSshaoyunl info->prot_write ? 1 : 0; 12592640c3faSshaoyunl memory_exception_data.failure.imprecise = 0; 12602640c3faSshaoyunl } 1261*7386f88aSJonathan Kim } 12625273e82cSFelix Kuehling 12635273e82cSFelix Kuehling rcu_read_lock(); 12642640c3faSshaoyunl 12652640c3faSshaoyunl id = KFD_FIRST_NONSIGNAL_EVENT_ID; 12662640c3faSshaoyunl idr_for_each_entry_continue(&p->event_idr, ev, id) 12672640c3faSshaoyunl if (ev->type == KFD_EVENT_TYPE_MEMORY) { 12685273e82cSFelix Kuehling spin_lock(&ev->lock); 1269*7386f88aSJonathan Kim ev->memory_exception_data = data ? *data : 1270*7386f88aSJonathan Kim memory_exception_data; 12712640c3faSshaoyunl set_event(ev); 12725273e82cSFelix Kuehling spin_unlock(&ev->lock); 12732640c3faSshaoyunl } 12742640c3faSshaoyunl 12755273e82cSFelix Kuehling rcu_read_unlock(); 12762640c3faSshaoyunl kfd_unref_process(p); 12772640c3faSshaoyunl } 1278e42051d2SShaoyun Liu 12798dc1db31SMukul Joshi void kfd_signal_reset_event(struct kfd_node *dev) 1280e42051d2SShaoyun Liu { 1281e42051d2SShaoyun Liu struct kfd_hsa_hw_exception_data hw_exception_data; 12829b54d201SEric Huang struct kfd_hsa_memory_exception_data memory_exception_data; 1283e42051d2SShaoyun Liu struct kfd_process *p; 1284e42051d2SShaoyun Liu struct kfd_event *ev; 1285e42051d2SShaoyun Liu unsigned int temp; 1286e42051d2SShaoyun Liu uint32_t id, idx; 12879b54d201SEric Huang int reset_cause = atomic_read(&dev->sram_ecc_flag) ? 12889b54d201SEric Huang KFD_HW_EXCEPTION_ECC : 12899b54d201SEric Huang KFD_HW_EXCEPTION_GPU_HANG; 1290e42051d2SShaoyun Liu 1291e42051d2SShaoyun Liu /* Whole gpu reset caused by GPU hang and memory is lost */ 1292e42051d2SShaoyun Liu memset(&hw_exception_data, 0, sizeof(hw_exception_data)); 1293e42051d2SShaoyun Liu hw_exception_data.memory_lost = 1; 12949b54d201SEric Huang hw_exception_data.reset_cause = reset_cause; 12959b54d201SEric Huang 12969b54d201SEric Huang memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 12979b54d201SEric Huang memory_exception_data.ErrorType = KFD_MEM_ERR_SRAM_ECC; 12989b54d201SEric Huang memory_exception_data.failure.imprecise = true; 1299e42051d2SShaoyun Liu 1300e42051d2SShaoyun Liu idx = srcu_read_lock(&kfd_processes_srcu); 1301e42051d2SShaoyun Liu hash_for_each_rcu(kfd_processes_table, temp, p, kfd_processes) { 1302bef153b7SDavid Yat Sin int user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); 1303bef153b7SDavid Yat Sin 1304bef153b7SDavid Yat Sin if (unlikely(user_gpu_id == -EINVAL)) { 1305bef153b7SDavid Yat Sin WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); 1306bef153b7SDavid Yat Sin continue; 1307bef153b7SDavid Yat Sin } 1308bef153b7SDavid Yat Sin 13095273e82cSFelix Kuehling rcu_read_lock(); 13105273e82cSFelix Kuehling 1311e42051d2SShaoyun Liu id = KFD_FIRST_NONSIGNAL_EVENT_ID; 13129b54d201SEric Huang idr_for_each_entry_continue(&p->event_idr, ev, id) { 1313e42051d2SShaoyun Liu if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) { 13145273e82cSFelix Kuehling spin_lock(&ev->lock); 1315e42051d2SShaoyun Liu ev->hw_exception_data = hw_exception_data; 1316bef153b7SDavid Yat Sin ev->hw_exception_data.gpu_id = user_gpu_id; 1317e42051d2SShaoyun Liu set_event(ev); 13185273e82cSFelix Kuehling spin_unlock(&ev->lock); 1319e42051d2SShaoyun Liu } 13209b54d201SEric Huang if (ev->type == KFD_EVENT_TYPE_MEMORY && 13219b54d201SEric Huang reset_cause == KFD_HW_EXCEPTION_ECC) { 13225273e82cSFelix Kuehling spin_lock(&ev->lock); 13239b54d201SEric Huang ev->memory_exception_data = memory_exception_data; 1324bef153b7SDavid Yat Sin ev->memory_exception_data.gpu_id = user_gpu_id; 13259b54d201SEric Huang set_event(ev); 13265273e82cSFelix Kuehling spin_unlock(&ev->lock); 13279b54d201SEric Huang } 13289b54d201SEric Huang } 13295273e82cSFelix Kuehling 13305273e82cSFelix Kuehling rcu_read_unlock(); 1331e42051d2SShaoyun Liu } 1332e42051d2SShaoyun Liu srcu_read_unlock(&kfd_processes_srcu, idx); 1333e42051d2SShaoyun Liu } 1334e2b1f9f5SDennis Li 13358dc1db31SMukul Joshi void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid) 1336e2b1f9f5SDennis Li { 1337e2b1f9f5SDennis Li struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 1338e2b1f9f5SDennis Li struct kfd_hsa_memory_exception_data memory_exception_data; 1339e2b1f9f5SDennis Li struct kfd_hsa_hw_exception_data hw_exception_data; 1340e2b1f9f5SDennis Li struct kfd_event *ev; 1341e2b1f9f5SDennis Li uint32_t id = KFD_FIRST_NONSIGNAL_EVENT_ID; 1342bef153b7SDavid Yat Sin int user_gpu_id; 1343e2b1f9f5SDennis Li 1344e2b1f9f5SDennis Li if (!p) 1345e2b1f9f5SDennis Li return; /* Presumably process exited. */ 1346e2b1f9f5SDennis Li 1347bef153b7SDavid Yat Sin user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); 1348bef153b7SDavid Yat Sin if (unlikely(user_gpu_id == -EINVAL)) { 1349bef153b7SDavid Yat Sin WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); 1350bef153b7SDavid Yat Sin return; 1351bef153b7SDavid Yat Sin } 1352bef153b7SDavid Yat Sin 1353e2b1f9f5SDennis Li memset(&hw_exception_data, 0, sizeof(hw_exception_data)); 1354bef153b7SDavid Yat Sin hw_exception_data.gpu_id = user_gpu_id; 1355e2b1f9f5SDennis Li hw_exception_data.memory_lost = 1; 1356e2b1f9f5SDennis Li hw_exception_data.reset_cause = KFD_HW_EXCEPTION_ECC; 1357e2b1f9f5SDennis Li 1358e2b1f9f5SDennis Li memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 1359e2b1f9f5SDennis Li memory_exception_data.ErrorType = KFD_MEM_ERR_POISON_CONSUMED; 1360bef153b7SDavid Yat Sin memory_exception_data.gpu_id = user_gpu_id; 1361e2b1f9f5SDennis Li memory_exception_data.failure.imprecise = true; 1362e2b1f9f5SDennis Li 13635273e82cSFelix Kuehling rcu_read_lock(); 13645273e82cSFelix Kuehling 1365e2b1f9f5SDennis Li idr_for_each_entry_continue(&p->event_idr, ev, id) { 1366e2b1f9f5SDennis Li if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) { 13675273e82cSFelix Kuehling spin_lock(&ev->lock); 1368e2b1f9f5SDennis Li ev->hw_exception_data = hw_exception_data; 1369e2b1f9f5SDennis Li set_event(ev); 13705273e82cSFelix Kuehling spin_unlock(&ev->lock); 1371e2b1f9f5SDennis Li } 1372e2b1f9f5SDennis Li 1373e2b1f9f5SDennis Li if (ev->type == KFD_EVENT_TYPE_MEMORY) { 13745273e82cSFelix Kuehling spin_lock(&ev->lock); 1375e2b1f9f5SDennis Li ev->memory_exception_data = memory_exception_data; 1376e2b1f9f5SDennis Li set_event(ev); 13775273e82cSFelix Kuehling spin_unlock(&ev->lock); 1378e2b1f9f5SDennis Li } 1379e2b1f9f5SDennis Li } 13805273e82cSFelix Kuehling 13815273e82cSFelix Kuehling rcu_read_unlock(); 1382e2b1f9f5SDennis Li 1383e2b1f9f5SDennis Li /* user application will handle SIGBUS signal */ 1384e2b1f9f5SDennis Li send_sig(SIGBUS, p->lead_thread, 0); 138596b62c8aSDennis Li 138696b62c8aSDennis Li kfd_unref_process(p); 1387e2b1f9f5SDennis Li } 1388