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 241f3a39818SAndrew Lewycky void kfd_event_init_process(struct kfd_process *p) 242f3a39818SAndrew Lewycky { 243f3a39818SAndrew Lewycky mutex_init(&p->event_mutex); 244482f0777SFelix Kuehling idr_init(&p->event_idr); 24550cb7dd9SFelix Kuehling p->signal_page = NULL; 246f3a39818SAndrew Lewycky p->signal_event_count = 0; 247f3a39818SAndrew Lewycky } 248f3a39818SAndrew Lewycky 249f3a39818SAndrew Lewycky static void destroy_event(struct kfd_process *p, struct kfd_event *ev) 250f3a39818SAndrew Lewycky { 25174e40716SFelix Kuehling struct kfd_event_waiter *waiter; 252fe528c13SFelix Kuehling 25374e40716SFelix Kuehling /* Wake up pending waiters. They will return failure */ 2545273e82cSFelix Kuehling spin_lock(&ev->lock); 25574e40716SFelix Kuehling list_for_each_entry(waiter, &ev->wq.head, wait.entry) 2565273e82cSFelix Kuehling WRITE_ONCE(waiter->event, NULL); 25774e40716SFelix Kuehling wake_up_all(&ev->wq); 2585273e82cSFelix Kuehling spin_unlock(&ev->lock); 259fe528c13SFelix Kuehling 260482f0777SFelix Kuehling if (ev->type == KFD_EVENT_TYPE_SIGNAL || 261482f0777SFelix Kuehling ev->type == KFD_EVENT_TYPE_DEBUG) 262f3a39818SAndrew Lewycky p->signal_event_count--; 263f3a39818SAndrew Lewycky 264482f0777SFelix Kuehling idr_remove(&p->event_idr, ev->event_id); 265*34d292d5SFelix Kuehling kfree_rcu(ev, rcu); 266f3a39818SAndrew Lewycky } 267f3a39818SAndrew Lewycky 268f3a39818SAndrew Lewycky static void destroy_events(struct kfd_process *p) 269f3a39818SAndrew Lewycky { 270f3a39818SAndrew Lewycky struct kfd_event *ev; 271482f0777SFelix Kuehling uint32_t id; 272f3a39818SAndrew Lewycky 273482f0777SFelix Kuehling idr_for_each_entry(&p->event_idr, ev, id) 274f3a39818SAndrew Lewycky destroy_event(p, ev); 275482f0777SFelix Kuehling idr_destroy(&p->event_idr); 276f3a39818SAndrew Lewycky } 277f3a39818SAndrew Lewycky 278f3a39818SAndrew Lewycky /* 279f3a39818SAndrew Lewycky * We assume that the process is being destroyed and there is no need to 280f3a39818SAndrew Lewycky * unmap the pages or keep bookkeeping data in order. 281f3a39818SAndrew Lewycky */ 28250cb7dd9SFelix Kuehling static void shutdown_signal_page(struct kfd_process *p) 283f3a39818SAndrew Lewycky { 28450cb7dd9SFelix Kuehling struct kfd_signal_page *page = p->signal_page; 285f3a39818SAndrew Lewycky 28650cb7dd9SFelix Kuehling if (page) { 2870fc8011fSFelix Kuehling if (page->need_to_free_pages) 288f3a39818SAndrew Lewycky free_pages((unsigned long)page->kernel_address, 289f3a39818SAndrew Lewycky get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); 290f3a39818SAndrew Lewycky kfree(page); 291f3a39818SAndrew Lewycky } 292f3a39818SAndrew Lewycky } 293f3a39818SAndrew Lewycky 294f3a39818SAndrew Lewycky void kfd_event_free_process(struct kfd_process *p) 295f3a39818SAndrew Lewycky { 296f3a39818SAndrew Lewycky destroy_events(p); 29750cb7dd9SFelix Kuehling shutdown_signal_page(p); 298f3a39818SAndrew Lewycky } 299f3a39818SAndrew Lewycky 300f3a39818SAndrew Lewycky static bool event_can_be_gpu_signaled(const struct kfd_event *ev) 301f3a39818SAndrew Lewycky { 302f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL || 303f3a39818SAndrew Lewycky ev->type == KFD_EVENT_TYPE_DEBUG; 304f3a39818SAndrew Lewycky } 305f3a39818SAndrew Lewycky 306f3a39818SAndrew Lewycky static bool event_can_be_cpu_signaled(const struct kfd_event *ev) 307f3a39818SAndrew Lewycky { 308f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL; 309f3a39818SAndrew Lewycky } 310f3a39818SAndrew Lewycky 31140e8a766SDavid Yat Sin static int kfd_event_page_set(struct kfd_process *p, void *kernel_address, 31240e8a766SDavid Yat Sin uint64_t size, uint64_t user_handle) 3130fc8011fSFelix Kuehling { 3140fc8011fSFelix Kuehling struct kfd_signal_page *page; 3150fc8011fSFelix Kuehling 3160fc8011fSFelix Kuehling if (p->signal_page) 3170fc8011fSFelix Kuehling return -EBUSY; 3180fc8011fSFelix Kuehling 3190fc8011fSFelix Kuehling page = kzalloc(sizeof(*page), GFP_KERNEL); 3200fc8011fSFelix Kuehling if (!page) 3210fc8011fSFelix Kuehling return -ENOMEM; 3220fc8011fSFelix Kuehling 3230fc8011fSFelix Kuehling /* Initialize all events to unsignaled */ 3240fc8011fSFelix Kuehling memset(kernel_address, (uint8_t) UNSIGNALED_EVENT_SLOT, 3250fc8011fSFelix Kuehling KFD_SIGNAL_EVENT_LIMIT * 8); 3260fc8011fSFelix Kuehling 3270fc8011fSFelix Kuehling page->kernel_address = kernel_address; 3280fc8011fSFelix Kuehling 3290fc8011fSFelix Kuehling p->signal_page = page; 3300fc8011fSFelix Kuehling p->signal_mapped_size = size; 33140e8a766SDavid Yat Sin p->signal_handle = user_handle; 3320fc8011fSFelix Kuehling return 0; 3330fc8011fSFelix Kuehling } 3340fc8011fSFelix Kuehling 33540e8a766SDavid Yat Sin int kfd_kmap_event_page(struct kfd_process *p, uint64_t event_page_offset) 33640e8a766SDavid Yat Sin { 33740e8a766SDavid Yat Sin struct kfd_dev *kfd; 33840e8a766SDavid Yat Sin struct kfd_process_device *pdd; 33940e8a766SDavid Yat Sin void *mem, *kern_addr; 34040e8a766SDavid Yat Sin uint64_t size; 34140e8a766SDavid Yat Sin int err = 0; 34240e8a766SDavid Yat Sin 34340e8a766SDavid Yat Sin if (p->signal_page) { 34440e8a766SDavid Yat Sin pr_err("Event page is already set\n"); 34540e8a766SDavid Yat Sin return -EINVAL; 34640e8a766SDavid Yat Sin } 34740e8a766SDavid Yat Sin 348bef153b7SDavid Yat Sin pdd = kfd_process_device_data_by_id(p, GET_GPU_ID(event_page_offset)); 349bef153b7SDavid Yat Sin if (!pdd) { 35040e8a766SDavid Yat Sin pr_err("Getting device by id failed in %s\n", __func__); 35140e8a766SDavid Yat Sin return -EINVAL; 35240e8a766SDavid Yat Sin } 353bef153b7SDavid Yat Sin kfd = pdd->dev; 35440e8a766SDavid Yat Sin 35540e8a766SDavid Yat Sin pdd = kfd_bind_process_to_device(kfd, p); 35640e8a766SDavid Yat Sin if (IS_ERR(pdd)) 35740e8a766SDavid Yat Sin return PTR_ERR(pdd); 35840e8a766SDavid Yat Sin 35940e8a766SDavid Yat Sin mem = kfd_process_device_translate_handle(pdd, 36040e8a766SDavid Yat Sin GET_IDR_HANDLE(event_page_offset)); 36140e8a766SDavid Yat Sin if (!mem) { 36240e8a766SDavid Yat Sin pr_err("Can't find BO, offset is 0x%llx\n", event_page_offset); 36340e8a766SDavid Yat Sin return -EINVAL; 36440e8a766SDavid Yat Sin } 36540e8a766SDavid Yat Sin 36640e8a766SDavid Yat Sin err = amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(kfd->adev, 36740e8a766SDavid Yat Sin mem, &kern_addr, &size); 36840e8a766SDavid Yat Sin if (err) { 36940e8a766SDavid Yat Sin pr_err("Failed to map event page to kernel\n"); 37040e8a766SDavid Yat Sin return err; 37140e8a766SDavid Yat Sin } 37240e8a766SDavid Yat Sin 37340e8a766SDavid Yat Sin err = kfd_event_page_set(p, kern_addr, size, event_page_offset); 37440e8a766SDavid Yat Sin if (err) { 37540e8a766SDavid Yat Sin pr_err("Failed to set event page\n"); 37640e8a766SDavid Yat Sin amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(kfd->adev, mem); 37740e8a766SDavid Yat Sin return err; 37840e8a766SDavid Yat Sin } 37940e8a766SDavid Yat Sin return err; 38040e8a766SDavid Yat Sin } 38140e8a766SDavid Yat Sin 382f3a39818SAndrew Lewycky int kfd_event_create(struct file *devkfd, struct kfd_process *p, 383f3a39818SAndrew Lewycky uint32_t event_type, bool auto_reset, uint32_t node_id, 384f3a39818SAndrew Lewycky uint32_t *event_id, uint32_t *event_trigger_data, 385f3a39818SAndrew Lewycky uint64_t *event_page_offset, uint32_t *event_slot_index) 386f3a39818SAndrew Lewycky { 387f3a39818SAndrew Lewycky int ret = 0; 388f3a39818SAndrew Lewycky struct kfd_event *ev = kzalloc(sizeof(*ev), GFP_KERNEL); 389f3a39818SAndrew Lewycky 390f3a39818SAndrew Lewycky if (!ev) 391f3a39818SAndrew Lewycky return -ENOMEM; 392f3a39818SAndrew Lewycky 393f3a39818SAndrew Lewycky ev->type = event_type; 394f3a39818SAndrew Lewycky ev->auto_reset = auto_reset; 395f3a39818SAndrew Lewycky ev->signaled = false; 396f3a39818SAndrew Lewycky 3975273e82cSFelix Kuehling spin_lock_init(&ev->lock); 39874e40716SFelix Kuehling init_waitqueue_head(&ev->wq); 399f3a39818SAndrew Lewycky 400f3a39818SAndrew Lewycky *event_page_offset = 0; 401f3a39818SAndrew Lewycky 402f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 403f3a39818SAndrew Lewycky 404f3a39818SAndrew Lewycky switch (event_type) { 405f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_SIGNAL: 406f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_DEBUG: 40740e8a766SDavid Yat Sin ret = create_signal_event(devkfd, p, ev, NULL); 408f3a39818SAndrew Lewycky if (!ret) { 409df03ef93SHarish Kasiviswanathan *event_page_offset = KFD_MMAP_TYPE_EVENTS; 410482f0777SFelix Kuehling *event_slot_index = ev->event_id; 411f3a39818SAndrew Lewycky } 412f3a39818SAndrew Lewycky break; 413f3a39818SAndrew Lewycky default: 41440e8a766SDavid Yat Sin ret = create_other_event(p, ev, NULL); 415f3a39818SAndrew Lewycky break; 416f3a39818SAndrew Lewycky } 417f3a39818SAndrew Lewycky 418f3a39818SAndrew Lewycky if (!ret) { 419f3a39818SAndrew Lewycky *event_id = ev->event_id; 420f3a39818SAndrew Lewycky *event_trigger_data = ev->event_id; 421f3a39818SAndrew Lewycky } else { 422f3a39818SAndrew Lewycky kfree(ev); 423f3a39818SAndrew Lewycky } 424f3a39818SAndrew Lewycky 425f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 426f3a39818SAndrew Lewycky 427f3a39818SAndrew Lewycky return ret; 428f3a39818SAndrew Lewycky } 429f3a39818SAndrew Lewycky 43040e8a766SDavid Yat Sin int kfd_criu_restore_event(struct file *devkfd, 43140e8a766SDavid Yat Sin struct kfd_process *p, 43240e8a766SDavid Yat Sin uint8_t __user *user_priv_ptr, 43340e8a766SDavid Yat Sin uint64_t *priv_data_offset, 43440e8a766SDavid Yat Sin uint64_t max_priv_data_size) 43540e8a766SDavid Yat Sin { 43640e8a766SDavid Yat Sin struct kfd_criu_event_priv_data *ev_priv; 43740e8a766SDavid Yat Sin struct kfd_event *ev = NULL; 43840e8a766SDavid Yat Sin int ret = 0; 43940e8a766SDavid Yat Sin 44040e8a766SDavid Yat Sin ev_priv = kmalloc(sizeof(*ev_priv), GFP_KERNEL); 44140e8a766SDavid Yat Sin if (!ev_priv) 44240e8a766SDavid Yat Sin return -ENOMEM; 44340e8a766SDavid Yat Sin 44440e8a766SDavid Yat Sin ev = kzalloc(sizeof(*ev), GFP_KERNEL); 44540e8a766SDavid Yat Sin if (!ev) { 44640e8a766SDavid Yat Sin ret = -ENOMEM; 44740e8a766SDavid Yat Sin goto exit; 44840e8a766SDavid Yat Sin } 44940e8a766SDavid Yat Sin 45040e8a766SDavid Yat Sin if (*priv_data_offset + sizeof(*ev_priv) > max_priv_data_size) { 45140e8a766SDavid Yat Sin ret = -EINVAL; 45240e8a766SDavid Yat Sin goto exit; 45340e8a766SDavid Yat Sin } 45440e8a766SDavid Yat Sin 45540e8a766SDavid Yat Sin ret = copy_from_user(ev_priv, user_priv_ptr + *priv_data_offset, sizeof(*ev_priv)); 45640e8a766SDavid Yat Sin if (ret) { 45740e8a766SDavid Yat Sin ret = -EFAULT; 45840e8a766SDavid Yat Sin goto exit; 45940e8a766SDavid Yat Sin } 46040e8a766SDavid Yat Sin *priv_data_offset += sizeof(*ev_priv); 46140e8a766SDavid Yat Sin 46240e8a766SDavid Yat Sin if (ev_priv->user_handle) { 46340e8a766SDavid Yat Sin ret = kfd_kmap_event_page(p, ev_priv->user_handle); 46440e8a766SDavid Yat Sin if (ret) 46540e8a766SDavid Yat Sin goto exit; 46640e8a766SDavid Yat Sin } 46740e8a766SDavid Yat Sin 46840e8a766SDavid Yat Sin ev->type = ev_priv->type; 46940e8a766SDavid Yat Sin ev->auto_reset = ev_priv->auto_reset; 47040e8a766SDavid Yat Sin ev->signaled = ev_priv->signaled; 47140e8a766SDavid Yat Sin 4725273e82cSFelix Kuehling spin_lock_init(&ev->lock); 47340e8a766SDavid Yat Sin init_waitqueue_head(&ev->wq); 47440e8a766SDavid Yat Sin 47540e8a766SDavid Yat Sin mutex_lock(&p->event_mutex); 47640e8a766SDavid Yat Sin switch (ev->type) { 47740e8a766SDavid Yat Sin case KFD_EVENT_TYPE_SIGNAL: 47840e8a766SDavid Yat Sin case KFD_EVENT_TYPE_DEBUG: 47940e8a766SDavid Yat Sin ret = create_signal_event(devkfd, p, ev, &ev_priv->event_id); 48040e8a766SDavid Yat Sin break; 48140e8a766SDavid Yat Sin case KFD_EVENT_TYPE_MEMORY: 48240e8a766SDavid Yat Sin memcpy(&ev->memory_exception_data, 48340e8a766SDavid Yat Sin &ev_priv->memory_exception_data, 48440e8a766SDavid Yat Sin sizeof(struct kfd_hsa_memory_exception_data)); 48540e8a766SDavid Yat Sin 48640e8a766SDavid Yat Sin ret = create_other_event(p, ev, &ev_priv->event_id); 48740e8a766SDavid Yat Sin break; 48840e8a766SDavid Yat Sin case KFD_EVENT_TYPE_HW_EXCEPTION: 48940e8a766SDavid Yat Sin memcpy(&ev->hw_exception_data, 49040e8a766SDavid Yat Sin &ev_priv->hw_exception_data, 49140e8a766SDavid Yat Sin sizeof(struct kfd_hsa_hw_exception_data)); 49240e8a766SDavid Yat Sin 49340e8a766SDavid Yat Sin ret = create_other_event(p, ev, &ev_priv->event_id); 49440e8a766SDavid Yat Sin break; 49540e8a766SDavid Yat Sin } 49640e8a766SDavid Yat Sin 49740e8a766SDavid Yat Sin exit: 49840e8a766SDavid Yat Sin if (ret) 49940e8a766SDavid Yat Sin kfree(ev); 50040e8a766SDavid Yat Sin 50140e8a766SDavid Yat Sin kfree(ev_priv); 50240e8a766SDavid Yat Sin 50340e8a766SDavid Yat Sin mutex_unlock(&p->event_mutex); 50440e8a766SDavid Yat Sin 50540e8a766SDavid Yat Sin return ret; 50640e8a766SDavid Yat Sin } 50740e8a766SDavid Yat Sin 50840e8a766SDavid Yat Sin int kfd_criu_checkpoint_events(struct kfd_process *p, 50940e8a766SDavid Yat Sin uint8_t __user *user_priv_data, 51040e8a766SDavid Yat Sin uint64_t *priv_data_offset) 51140e8a766SDavid Yat Sin { 51240e8a766SDavid Yat Sin struct kfd_criu_event_priv_data *ev_privs; 51340e8a766SDavid Yat Sin int i = 0; 51440e8a766SDavid Yat Sin int ret = 0; 51540e8a766SDavid Yat Sin struct kfd_event *ev; 51640e8a766SDavid Yat Sin uint32_t ev_id; 51740e8a766SDavid Yat Sin 51840e8a766SDavid Yat Sin uint32_t num_events = kfd_get_num_events(p); 51940e8a766SDavid Yat Sin 52040e8a766SDavid Yat Sin if (!num_events) 52140e8a766SDavid Yat Sin return 0; 52240e8a766SDavid Yat Sin 52340e8a766SDavid Yat Sin ev_privs = kvzalloc(num_events * sizeof(*ev_privs), GFP_KERNEL); 52440e8a766SDavid Yat Sin if (!ev_privs) 52540e8a766SDavid Yat Sin return -ENOMEM; 52640e8a766SDavid Yat Sin 52740e8a766SDavid Yat Sin 52840e8a766SDavid Yat Sin idr_for_each_entry(&p->event_idr, ev, ev_id) { 52940e8a766SDavid Yat Sin struct kfd_criu_event_priv_data *ev_priv; 53040e8a766SDavid Yat Sin 53140e8a766SDavid Yat Sin /* 53240e8a766SDavid Yat Sin * Currently, all events have same size of private_data, but the current ioctl's 53340e8a766SDavid Yat Sin * and CRIU plugin supports private_data of variable sizes 53440e8a766SDavid Yat Sin */ 53540e8a766SDavid Yat Sin ev_priv = &ev_privs[i]; 53640e8a766SDavid Yat Sin 53740e8a766SDavid Yat Sin ev_priv->object_type = KFD_CRIU_OBJECT_TYPE_EVENT; 53840e8a766SDavid Yat Sin 53940e8a766SDavid Yat Sin /* We store the user_handle with the first event */ 54040e8a766SDavid Yat Sin if (i == 0 && p->signal_page) 54140e8a766SDavid Yat Sin ev_priv->user_handle = p->signal_handle; 54240e8a766SDavid Yat Sin 54340e8a766SDavid Yat Sin ev_priv->event_id = ev->event_id; 54440e8a766SDavid Yat Sin ev_priv->auto_reset = ev->auto_reset; 54540e8a766SDavid Yat Sin ev_priv->type = ev->type; 54640e8a766SDavid Yat Sin ev_priv->signaled = ev->signaled; 54740e8a766SDavid Yat Sin 54840e8a766SDavid Yat Sin if (ev_priv->type == KFD_EVENT_TYPE_MEMORY) 54940e8a766SDavid Yat Sin memcpy(&ev_priv->memory_exception_data, 55040e8a766SDavid Yat Sin &ev->memory_exception_data, 55140e8a766SDavid Yat Sin sizeof(struct kfd_hsa_memory_exception_data)); 55240e8a766SDavid Yat Sin else if (ev_priv->type == KFD_EVENT_TYPE_HW_EXCEPTION) 55340e8a766SDavid Yat Sin memcpy(&ev_priv->hw_exception_data, 55440e8a766SDavid Yat Sin &ev->hw_exception_data, 55540e8a766SDavid Yat Sin sizeof(struct kfd_hsa_hw_exception_data)); 55640e8a766SDavid Yat Sin 55740e8a766SDavid Yat Sin pr_debug("Checkpointed event[%d] id = 0x%08x auto_reset = %x type = %x signaled = %x\n", 55840e8a766SDavid Yat Sin i, 55940e8a766SDavid Yat Sin ev_priv->event_id, 56040e8a766SDavid Yat Sin ev_priv->auto_reset, 56140e8a766SDavid Yat Sin ev_priv->type, 56240e8a766SDavid Yat Sin ev_priv->signaled); 56340e8a766SDavid Yat Sin i++; 56440e8a766SDavid Yat Sin } 56540e8a766SDavid Yat Sin 56640e8a766SDavid Yat Sin ret = copy_to_user(user_priv_data + *priv_data_offset, 56740e8a766SDavid Yat Sin ev_privs, num_events * sizeof(*ev_privs)); 56840e8a766SDavid Yat Sin if (ret) { 56940e8a766SDavid Yat Sin pr_err("Failed to copy events priv to user\n"); 57040e8a766SDavid Yat Sin ret = -EFAULT; 57140e8a766SDavid Yat Sin } 57240e8a766SDavid Yat Sin 57340e8a766SDavid Yat Sin *priv_data_offset += num_events * sizeof(*ev_privs); 57440e8a766SDavid Yat Sin 57540e8a766SDavid Yat Sin kvfree(ev_privs); 57640e8a766SDavid Yat Sin return ret; 57740e8a766SDavid Yat Sin } 57840e8a766SDavid Yat Sin 57940e8a766SDavid Yat Sin int kfd_get_num_events(struct kfd_process *p) 58040e8a766SDavid Yat Sin { 58140e8a766SDavid Yat Sin struct kfd_event *ev; 58240e8a766SDavid Yat Sin uint32_t id; 58340e8a766SDavid Yat Sin u32 num_events = 0; 58440e8a766SDavid Yat Sin 58540e8a766SDavid Yat Sin idr_for_each_entry(&p->event_idr, ev, id) 58640e8a766SDavid Yat Sin num_events++; 58740e8a766SDavid Yat Sin 58840e8a766SDavid Yat Sin return num_events; 58940e8a766SDavid Yat Sin } 59040e8a766SDavid Yat Sin 591f3a39818SAndrew Lewycky /* Assumes that p is current. */ 592f3a39818SAndrew Lewycky int kfd_event_destroy(struct kfd_process *p, uint32_t event_id) 593f3a39818SAndrew Lewycky { 594f3a39818SAndrew Lewycky struct kfd_event *ev; 595f3a39818SAndrew Lewycky int ret = 0; 596f3a39818SAndrew Lewycky 597f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 598f3a39818SAndrew Lewycky 599f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 600f3a39818SAndrew Lewycky 601f3a39818SAndrew Lewycky if (ev) 602f3a39818SAndrew Lewycky destroy_event(p, ev); 603f3a39818SAndrew Lewycky else 604f3a39818SAndrew Lewycky ret = -EINVAL; 605f3a39818SAndrew Lewycky 606f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 607f3a39818SAndrew Lewycky return ret; 608f3a39818SAndrew Lewycky } 609f3a39818SAndrew Lewycky 610f3a39818SAndrew Lewycky static void set_event(struct kfd_event *ev) 611f3a39818SAndrew Lewycky { 612f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter; 613f3a39818SAndrew Lewycky 61474e40716SFelix Kuehling /* Auto reset if the list is non-empty and we're waking 61574e40716SFelix Kuehling * someone. waitqueue_active is safe here because we're 6165273e82cSFelix Kuehling * protected by the ev->lock, which is also held when 61774e40716SFelix Kuehling * updating the wait queues in kfd_wait_on_events. 61874e40716SFelix Kuehling */ 61974e40716SFelix Kuehling ev->signaled = !ev->auto_reset || !waitqueue_active(&ev->wq); 620f3a39818SAndrew Lewycky 62174e40716SFelix Kuehling list_for_each_entry(waiter, &ev->wq.head, wait.entry) 6225273e82cSFelix Kuehling WRITE_ONCE(waiter->activated, true); 623f3a39818SAndrew Lewycky 62474e40716SFelix Kuehling wake_up_all(&ev->wq); 625f3a39818SAndrew Lewycky } 626f3a39818SAndrew Lewycky 627f3a39818SAndrew Lewycky /* Assumes that p is current. */ 628f3a39818SAndrew Lewycky int kfd_set_event(struct kfd_process *p, uint32_t event_id) 629f3a39818SAndrew Lewycky { 630f3a39818SAndrew Lewycky int ret = 0; 631f3a39818SAndrew Lewycky struct kfd_event *ev; 632f3a39818SAndrew Lewycky 6335273e82cSFelix Kuehling rcu_read_lock(); 634f3a39818SAndrew Lewycky 635f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 6365273e82cSFelix Kuehling spin_lock(&ev->lock); 637f3a39818SAndrew Lewycky 638f3a39818SAndrew Lewycky if (ev && event_can_be_cpu_signaled(ev)) 639f3a39818SAndrew Lewycky set_event(ev); 640f3a39818SAndrew Lewycky else 641f3a39818SAndrew Lewycky ret = -EINVAL; 642f3a39818SAndrew Lewycky 6435273e82cSFelix Kuehling spin_unlock(&ev->lock); 6445273e82cSFelix Kuehling rcu_read_unlock(); 645f3a39818SAndrew Lewycky return ret; 646f3a39818SAndrew Lewycky } 647f3a39818SAndrew Lewycky 648f3a39818SAndrew Lewycky static void reset_event(struct kfd_event *ev) 649f3a39818SAndrew Lewycky { 650f3a39818SAndrew Lewycky ev->signaled = false; 651f3a39818SAndrew Lewycky } 652f3a39818SAndrew Lewycky 653f3a39818SAndrew Lewycky /* Assumes that p is current. */ 654f3a39818SAndrew Lewycky int kfd_reset_event(struct kfd_process *p, uint32_t event_id) 655f3a39818SAndrew Lewycky { 656f3a39818SAndrew Lewycky int ret = 0; 657f3a39818SAndrew Lewycky struct kfd_event *ev; 658f3a39818SAndrew Lewycky 6595273e82cSFelix Kuehling rcu_read_lock(); 660f3a39818SAndrew Lewycky 661f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 6625273e82cSFelix Kuehling spin_lock(&ev->lock); 663f3a39818SAndrew Lewycky 664f3a39818SAndrew Lewycky if (ev && event_can_be_cpu_signaled(ev)) 665f3a39818SAndrew Lewycky reset_event(ev); 666f3a39818SAndrew Lewycky else 667f3a39818SAndrew Lewycky ret = -EINVAL; 668f3a39818SAndrew Lewycky 6695273e82cSFelix Kuehling spin_unlock(&ev->lock); 6705273e82cSFelix Kuehling rcu_read_unlock(); 671f3a39818SAndrew Lewycky return ret; 672f3a39818SAndrew Lewycky 673f3a39818SAndrew Lewycky } 674f3a39818SAndrew Lewycky 675f3a39818SAndrew Lewycky static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev) 676f3a39818SAndrew Lewycky { 6775273e82cSFelix Kuehling WRITE_ONCE(page_slots(p->signal_page)[ev->event_id], UNSIGNALED_EVENT_SLOT); 678f3a39818SAndrew Lewycky } 679f3a39818SAndrew Lewycky 680f3a39818SAndrew Lewycky static void set_event_from_interrupt(struct kfd_process *p, 681f3a39818SAndrew Lewycky struct kfd_event *ev) 682f3a39818SAndrew Lewycky { 683f3a39818SAndrew Lewycky if (ev && event_can_be_gpu_signaled(ev)) { 684f3a39818SAndrew Lewycky acknowledge_signal(p, ev); 6855273e82cSFelix Kuehling spin_lock(&ev->lock); 686f3a39818SAndrew Lewycky set_event(ev); 6875273e82cSFelix Kuehling spin_unlock(&ev->lock); 688f3a39818SAndrew Lewycky } 689f3a39818SAndrew Lewycky } 690f3a39818SAndrew Lewycky 691c7b6bac9SFenghua Yu void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id, 692f3a39818SAndrew Lewycky uint32_t valid_id_bits) 693f3a39818SAndrew Lewycky { 6943f04f961SFelix Kuehling struct kfd_event *ev = NULL; 695f3a39818SAndrew Lewycky 696f3a39818SAndrew Lewycky /* 697f3a39818SAndrew Lewycky * Because we are called from arbitrary context (workqueue) as opposed 698f3a39818SAndrew Lewycky * to process context, kfd_process could attempt to exit while we are 699abb208a8SFelix Kuehling * running so the lookup function increments the process ref count. 700f3a39818SAndrew Lewycky */ 701f3a39818SAndrew Lewycky struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 702f3a39818SAndrew Lewycky 703f3a39818SAndrew Lewycky if (!p) 704f3a39818SAndrew Lewycky return; /* Presumably process exited. */ 705f3a39818SAndrew Lewycky 7065273e82cSFelix Kuehling rcu_read_lock(); 707f3a39818SAndrew Lewycky 7083f04f961SFelix Kuehling if (valid_id_bits) 7093f04f961SFelix Kuehling ev = lookup_signaled_event_by_partial_id(p, partial_id, 7103f04f961SFelix Kuehling valid_id_bits); 7113f04f961SFelix Kuehling if (ev) { 712f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 71350cb7dd9SFelix Kuehling } else if (p->signal_page) { 714f3a39818SAndrew Lewycky /* 7153f04f961SFelix Kuehling * Partial ID lookup failed. Assume that the event ID 7163f04f961SFelix Kuehling * in the interrupt payload was invalid and do an 7173f04f961SFelix Kuehling * exhaustive search of signaled events. 718f3a39818SAndrew Lewycky */ 719482f0777SFelix Kuehling uint64_t *slots = page_slots(p->signal_page); 720482f0777SFelix Kuehling uint32_t id; 721f3a39818SAndrew Lewycky 7223f04f961SFelix Kuehling if (valid_id_bits) 7233f04f961SFelix Kuehling pr_debug_ratelimited("Partial ID invalid: %u (%u valid bits)\n", 7243f04f961SFelix Kuehling partial_id, valid_id_bits); 7253f04f961SFelix Kuehling 726eeb27b7eSFelix Kuehling if (p->signal_event_count < KFD_SIGNAL_EVENT_LIMIT / 64) { 727482f0777SFelix Kuehling /* With relatively few events, it's faster to 728482f0777SFelix Kuehling * iterate over the event IDR 729482f0777SFelix Kuehling */ 730482f0777SFelix Kuehling idr_for_each_entry(&p->event_idr, ev, id) { 731482f0777SFelix Kuehling if (id >= KFD_SIGNAL_EVENT_LIMIT) 732482f0777SFelix Kuehling break; 733482f0777SFelix Kuehling 7345273e82cSFelix Kuehling if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT) 735f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 736f3a39818SAndrew Lewycky } 737482f0777SFelix Kuehling } else { 738482f0777SFelix Kuehling /* With relatively many events, it's faster to 739482f0777SFelix Kuehling * iterate over the signal slots and lookup 740482f0777SFelix Kuehling * only signaled events from the IDR. 741482f0777SFelix Kuehling */ 742482f0777SFelix Kuehling for (id = 0; id < KFD_SIGNAL_EVENT_LIMIT; id++) 7435273e82cSFelix Kuehling if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT) { 744482f0777SFelix Kuehling ev = lookup_event_by_id(p, id); 745482f0777SFelix Kuehling set_event_from_interrupt(p, ev); 746482f0777SFelix Kuehling } 747482f0777SFelix Kuehling } 748f3a39818SAndrew Lewycky } 749f3a39818SAndrew Lewycky 7505273e82cSFelix Kuehling rcu_read_unlock(); 751abb208a8SFelix Kuehling kfd_unref_process(p); 752f3a39818SAndrew Lewycky } 753f3a39818SAndrew Lewycky 754f3a39818SAndrew Lewycky static struct kfd_event_waiter *alloc_event_waiters(uint32_t num_events) 755f3a39818SAndrew Lewycky { 756f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters; 757f3a39818SAndrew Lewycky uint32_t i; 758f3a39818SAndrew Lewycky 759f3a39818SAndrew Lewycky event_waiters = kmalloc_array(num_events, 760f3a39818SAndrew Lewycky sizeof(struct kfd_event_waiter), 761f3a39818SAndrew Lewycky GFP_KERNEL); 762ebbb7bb9SQintaoShen if (!event_waiters) 763ebbb7bb9SQintaoShen return NULL; 764f3a39818SAndrew Lewycky 765f3a39818SAndrew Lewycky for (i = 0; (event_waiters) && (i < num_events) ; i++) { 76674e40716SFelix Kuehling init_wait(&event_waiters[i].wait); 767f3a39818SAndrew Lewycky event_waiters[i].activated = false; 768f3a39818SAndrew Lewycky } 769f3a39818SAndrew Lewycky 770f3a39818SAndrew Lewycky return event_waiters; 771f3a39818SAndrew Lewycky } 772f3a39818SAndrew Lewycky 7731f9d09beSSean Keely static int init_event_waiter_get_status(struct kfd_process *p, 774f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter, 775ebf947feSFelix Kuehling uint32_t event_id) 776f3a39818SAndrew Lewycky { 777f3a39818SAndrew Lewycky struct kfd_event *ev = lookup_event_by_id(p, event_id); 778f3a39818SAndrew Lewycky 779f3a39818SAndrew Lewycky if (!ev) 780f3a39818SAndrew Lewycky return -EINVAL; 781f3a39818SAndrew Lewycky 7825273e82cSFelix Kuehling spin_lock(&ev->lock); 78359d3e8beSAlexey Skidanov waiter->event = ev; 784f3a39818SAndrew Lewycky waiter->activated = ev->signaled; 785f3a39818SAndrew Lewycky ev->signaled = ev->signaled && !ev->auto_reset; 7865273e82cSFelix Kuehling spin_unlock(&ev->lock); 787f3a39818SAndrew Lewycky 788f3a39818SAndrew Lewycky return 0; 789f3a39818SAndrew Lewycky } 790f3a39818SAndrew Lewycky 7911f9d09beSSean Keely static void init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter) 7921f9d09beSSean Keely { 7931f9d09beSSean Keely struct kfd_event *ev = waiter->event; 7941f9d09beSSean Keely 7951f9d09beSSean Keely /* Only add to the wait list if we actually need to 7961f9d09beSSean Keely * wait on this event. 7971f9d09beSSean Keely */ 7985273e82cSFelix Kuehling if (!waiter->activated) { 7995273e82cSFelix Kuehling spin_lock(&ev->lock); 80074e40716SFelix Kuehling add_wait_queue(&ev->wq, &waiter->wait); 8015273e82cSFelix Kuehling spin_unlock(&ev->lock); 8025273e82cSFelix Kuehling } 8031f9d09beSSean Keely } 8041f9d09beSSean Keely 805fe528c13SFelix Kuehling /* test_event_condition - Test condition of events being waited for 806fe528c13SFelix Kuehling * @all: Return completion only if all events have signaled 807fe528c13SFelix Kuehling * @num_events: Number of events to wait for 808fe528c13SFelix Kuehling * @event_waiters: Array of event waiters, one per event 809fe528c13SFelix Kuehling * 810fe528c13SFelix Kuehling * Returns KFD_IOC_WAIT_RESULT_COMPLETE if all (or one) event(s) have 811fe528c13SFelix Kuehling * signaled. Returns KFD_IOC_WAIT_RESULT_TIMEOUT if no (or not all) 812fe528c13SFelix Kuehling * events have signaled. Returns KFD_IOC_WAIT_RESULT_FAIL if any of 813fe528c13SFelix Kuehling * the events have been destroyed. 814fe528c13SFelix Kuehling */ 815fe528c13SFelix Kuehling static uint32_t test_event_condition(bool all, uint32_t num_events, 816f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters) 817f3a39818SAndrew Lewycky { 818f3a39818SAndrew Lewycky uint32_t i; 819f3a39818SAndrew Lewycky uint32_t activated_count = 0; 820f3a39818SAndrew Lewycky 821f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 8225273e82cSFelix Kuehling if (!READ_ONCE(event_waiters[i].event)) 823fe528c13SFelix Kuehling return KFD_IOC_WAIT_RESULT_FAIL; 824fe528c13SFelix Kuehling 8255273e82cSFelix Kuehling if (READ_ONCE(event_waiters[i].activated)) { 826f3a39818SAndrew Lewycky if (!all) 827fe528c13SFelix Kuehling return KFD_IOC_WAIT_RESULT_COMPLETE; 828f3a39818SAndrew Lewycky 829f3a39818SAndrew Lewycky activated_count++; 830f3a39818SAndrew Lewycky } 831f3a39818SAndrew Lewycky } 832f3a39818SAndrew Lewycky 833fe528c13SFelix Kuehling return activated_count == num_events ? 834fe528c13SFelix Kuehling KFD_IOC_WAIT_RESULT_COMPLETE : KFD_IOC_WAIT_RESULT_TIMEOUT; 835f3a39818SAndrew Lewycky } 836f3a39818SAndrew Lewycky 83759d3e8beSAlexey Skidanov /* 83859d3e8beSAlexey Skidanov * Copy event specific data, if defined. 83959d3e8beSAlexey Skidanov * Currently only memory exception events have additional data to copy to user 84059d3e8beSAlexey Skidanov */ 841fdf0c833SFelix Kuehling static int copy_signaled_event_data(uint32_t num_events, 84259d3e8beSAlexey Skidanov struct kfd_event_waiter *event_waiters, 84359d3e8beSAlexey Skidanov struct kfd_event_data __user *data) 84459d3e8beSAlexey Skidanov { 84559d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *src; 84659d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data __user *dst; 84759d3e8beSAlexey Skidanov struct kfd_event_waiter *waiter; 84859d3e8beSAlexey Skidanov struct kfd_event *event; 84959d3e8beSAlexey Skidanov uint32_t i; 85059d3e8beSAlexey Skidanov 85159d3e8beSAlexey Skidanov for (i = 0; i < num_events; i++) { 85259d3e8beSAlexey Skidanov waiter = &event_waiters[i]; 85359d3e8beSAlexey Skidanov event = waiter->event; 8545273e82cSFelix Kuehling if (!event) 8555273e82cSFelix Kuehling return -EINVAL; /* event was destroyed */ 85659d3e8beSAlexey Skidanov if (waiter->activated && event->type == KFD_EVENT_TYPE_MEMORY) { 857ebf947feSFelix Kuehling dst = &data[i].memory_exception_data; 85859d3e8beSAlexey Skidanov src = &event->memory_exception_data; 85959d3e8beSAlexey Skidanov if (copy_to_user(dst, src, 86059d3e8beSAlexey Skidanov sizeof(struct kfd_hsa_memory_exception_data))) 861fdf0c833SFelix Kuehling return -EFAULT; 86259d3e8beSAlexey Skidanov } 86359d3e8beSAlexey Skidanov } 86459d3e8beSAlexey Skidanov 865fdf0c833SFelix Kuehling return 0; 86659d3e8beSAlexey Skidanov } 86759d3e8beSAlexey Skidanov 868f3a39818SAndrew Lewycky static long user_timeout_to_jiffies(uint32_t user_timeout_ms) 869f3a39818SAndrew Lewycky { 870f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE) 871f3a39818SAndrew Lewycky return 0; 872f3a39818SAndrew Lewycky 873f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_INFINITE) 874f3a39818SAndrew Lewycky return MAX_SCHEDULE_TIMEOUT; 875f3a39818SAndrew Lewycky 876f3a39818SAndrew Lewycky /* 877f3a39818SAndrew Lewycky * msecs_to_jiffies interprets all values above 2^31-1 as infinite, 878f3a39818SAndrew Lewycky * but we consider them finite. 879f3a39818SAndrew Lewycky * This hack is wrong, but nobody is likely to notice. 880f3a39818SAndrew Lewycky */ 881f3a39818SAndrew Lewycky user_timeout_ms = min_t(uint32_t, user_timeout_ms, 0x7FFFFFFF); 882f3a39818SAndrew Lewycky 883f3a39818SAndrew Lewycky return msecs_to_jiffies(user_timeout_ms) + 1; 884f3a39818SAndrew Lewycky } 885f3a39818SAndrew Lewycky 886f3a39818SAndrew Lewycky static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters) 887f3a39818SAndrew Lewycky { 888f3a39818SAndrew Lewycky uint32_t i; 889f3a39818SAndrew Lewycky 890f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) 8915273e82cSFelix Kuehling if (waiters[i].event) { 8925273e82cSFelix Kuehling spin_lock(&waiters[i].event->lock); 89374e40716SFelix Kuehling remove_wait_queue(&waiters[i].event->wq, 89474e40716SFelix Kuehling &waiters[i].wait); 8955273e82cSFelix Kuehling spin_unlock(&waiters[i].event->lock); 8965273e82cSFelix Kuehling } 897f3a39818SAndrew Lewycky 898f3a39818SAndrew Lewycky kfree(waiters); 899f3a39818SAndrew Lewycky } 900f3a39818SAndrew Lewycky 901f3a39818SAndrew Lewycky int kfd_wait_on_events(struct kfd_process *p, 90259d3e8beSAlexey Skidanov uint32_t num_events, void __user *data, 903f3a39818SAndrew Lewycky bool all, uint32_t user_timeout_ms, 904fdf0c833SFelix Kuehling uint32_t *wait_result) 905f3a39818SAndrew Lewycky { 90659d3e8beSAlexey Skidanov struct kfd_event_data __user *events = 90759d3e8beSAlexey Skidanov (struct kfd_event_data __user *) data; 908f3a39818SAndrew Lewycky uint32_t i; 909f3a39818SAndrew Lewycky int ret = 0; 9101f9d09beSSean Keely 911f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters = NULL; 912f3a39818SAndrew Lewycky long timeout = user_timeout_to_jiffies(user_timeout_ms); 913f3a39818SAndrew Lewycky 914fdf0c833SFelix Kuehling event_waiters = alloc_event_waiters(num_events); 915fdf0c833SFelix Kuehling if (!event_waiters) { 916fdf0c833SFelix Kuehling ret = -ENOMEM; 917fdf0c833SFelix Kuehling goto out; 918fdf0c833SFelix Kuehling } 919fdf0c833SFelix Kuehling 9205273e82cSFelix Kuehling /* Use p->event_mutex here to protect against concurrent creation and 9215273e82cSFelix Kuehling * destruction of events while we initialize event_waiters. 9225273e82cSFelix Kuehling */ 923f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 924f3a39818SAndrew Lewycky 925f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 92659d3e8beSAlexey Skidanov struct kfd_event_data event_data; 927f3a39818SAndrew Lewycky 92859d3e8beSAlexey Skidanov if (copy_from_user(&event_data, &events[i], 9298bf79388SPan Bian sizeof(struct kfd_event_data))) { 9308bf79388SPan Bian ret = -EFAULT; 931fdf0c833SFelix Kuehling goto out_unlock; 9328bf79388SPan Bian } 933f3a39818SAndrew Lewycky 9341f9d09beSSean Keely ret = init_event_waiter_get_status(p, &event_waiters[i], 935ebf947feSFelix Kuehling event_data.event_id); 936f3a39818SAndrew Lewycky if (ret) 937fdf0c833SFelix Kuehling goto out_unlock; 938f3a39818SAndrew Lewycky } 939f3a39818SAndrew Lewycky 9401f9d09beSSean Keely /* Check condition once. */ 941fe528c13SFelix Kuehling *wait_result = test_event_condition(all, num_events, event_waiters); 942fe528c13SFelix Kuehling if (*wait_result == KFD_IOC_WAIT_RESULT_COMPLETE) { 943fdf0c833SFelix Kuehling ret = copy_signaled_event_data(num_events, 944fdf0c833SFelix Kuehling event_waiters, events); 945fdf0c833SFelix Kuehling goto out_unlock; 946fe528c13SFelix Kuehling } else if (WARN_ON(*wait_result == KFD_IOC_WAIT_RESULT_FAIL)) { 947fe528c13SFelix Kuehling /* This should not happen. Events shouldn't be 948fe528c13SFelix Kuehling * destroyed while we're holding the event_mutex 949fe528c13SFelix Kuehling */ 950fe528c13SFelix Kuehling goto out_unlock; 951fe528c13SFelix Kuehling } 952fe528c13SFelix Kuehling 9531f9d09beSSean Keely /* Add to wait lists if we need to wait. */ 9541f9d09beSSean Keely for (i = 0; i < num_events; i++) 9551f9d09beSSean Keely init_event_waiter_add_to_waitlist(&event_waiters[i]); 9561f9d09beSSean Keely 957f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 958f3a39818SAndrew Lewycky 959f3a39818SAndrew Lewycky while (true) { 960f3a39818SAndrew Lewycky if (fatal_signal_pending(current)) { 961f3a39818SAndrew Lewycky ret = -EINTR; 962f3a39818SAndrew Lewycky break; 963f3a39818SAndrew Lewycky } 964f3a39818SAndrew Lewycky 965f3a39818SAndrew Lewycky if (signal_pending(current)) { 966f3a39818SAndrew Lewycky /* 967f3a39818SAndrew Lewycky * This is wrong when a nonzero, non-infinite timeout 968f3a39818SAndrew Lewycky * is specified. We need to use 969f3a39818SAndrew Lewycky * ERESTARTSYS_RESTARTBLOCK, but struct restart_block 970f3a39818SAndrew Lewycky * contains a union with data for each user and it's 971f3a39818SAndrew Lewycky * in generic kernel code that I don't want to 972f3a39818SAndrew Lewycky * touch yet. 973f3a39818SAndrew Lewycky */ 974f3a39818SAndrew Lewycky ret = -ERESTARTSYS; 975f3a39818SAndrew Lewycky break; 976f3a39818SAndrew Lewycky } 977f3a39818SAndrew Lewycky 978d9aeec4cSSean Keely /* Set task state to interruptible sleep before 979d9aeec4cSSean Keely * checking wake-up conditions. A concurrent wake-up 980d9aeec4cSSean Keely * will put the task back into runnable state. In that 981d9aeec4cSSean Keely * case schedule_timeout will not put the task to 982d9aeec4cSSean Keely * sleep and we'll get a chance to re-check the 983d9aeec4cSSean Keely * updated conditions almost immediately. Otherwise, 984d9aeec4cSSean Keely * this race condition would lead to a soft hang or a 985d9aeec4cSSean Keely * very long sleep. 986d9aeec4cSSean Keely */ 987d9aeec4cSSean Keely set_current_state(TASK_INTERRUPTIBLE); 988d9aeec4cSSean Keely 989fe528c13SFelix Kuehling *wait_result = test_event_condition(all, num_events, 990fe528c13SFelix Kuehling event_waiters); 991fe528c13SFelix Kuehling if (*wait_result != KFD_IOC_WAIT_RESULT_TIMEOUT) 992f3a39818SAndrew Lewycky break; 993f3a39818SAndrew Lewycky 994fe528c13SFelix Kuehling if (timeout <= 0) 995f3a39818SAndrew Lewycky break; 996f3a39818SAndrew Lewycky 997d9aeec4cSSean Keely timeout = schedule_timeout(timeout); 998f3a39818SAndrew Lewycky } 999f3a39818SAndrew Lewycky __set_current_state(TASK_RUNNING); 1000f3a39818SAndrew Lewycky 10015273e82cSFelix Kuehling mutex_lock(&p->event_mutex); 1002fdf0c833SFelix Kuehling /* copy_signaled_event_data may sleep. So this has to happen 1003fdf0c833SFelix Kuehling * after the task state is set back to RUNNING. 10045273e82cSFelix Kuehling * 10055273e82cSFelix Kuehling * The event may also have been destroyed after signaling. So 10065273e82cSFelix Kuehling * copy_signaled_event_data also must confirm that the event 10075273e82cSFelix Kuehling * still exists. Therefore this must be under the p->event_mutex 10085273e82cSFelix Kuehling * which is also held when events are destroyed. 1009fdf0c833SFelix Kuehling */ 1010fdf0c833SFelix Kuehling if (!ret && *wait_result == KFD_IOC_WAIT_RESULT_COMPLETE) 1011fdf0c833SFelix Kuehling ret = copy_signaled_event_data(num_events, 1012fdf0c833SFelix Kuehling event_waiters, events); 1013fdf0c833SFelix Kuehling 1014fdf0c833SFelix Kuehling out_unlock: 1015f3a39818SAndrew Lewycky free_waiters(num_events, event_waiters); 1016f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 1017fdf0c833SFelix Kuehling out: 1018fdf0c833SFelix Kuehling if (ret) 1019fdf0c833SFelix Kuehling *wait_result = KFD_IOC_WAIT_RESULT_FAIL; 1020fe528c13SFelix Kuehling else if (*wait_result == KFD_IOC_WAIT_RESULT_FAIL) 1021fe528c13SFelix Kuehling ret = -EIO; 1022f3a39818SAndrew Lewycky 1023f3a39818SAndrew Lewycky return ret; 1024f3a39818SAndrew Lewycky } 1025f3a39818SAndrew Lewycky 1026f3a39818SAndrew Lewycky int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma) 1027f3a39818SAndrew Lewycky { 1028f3a39818SAndrew Lewycky unsigned long pfn; 102950cb7dd9SFelix Kuehling struct kfd_signal_page *page; 1030b9a5d0a5SFelix Kuehling int ret; 1031f3a39818SAndrew Lewycky 1032b9a5d0a5SFelix Kuehling /* check required size doesn't exceed the allocated size */ 1033b9a5d0a5SFelix Kuehling if (get_order(KFD_SIGNAL_EVENT_LIMIT * 8) < 1034f3a39818SAndrew Lewycky get_order(vma->vm_end - vma->vm_start)) { 103579775b62SKent Russell pr_err("Event page mmap requested illegal size\n"); 1036f3a39818SAndrew Lewycky return -EINVAL; 1037f3a39818SAndrew Lewycky } 1038f3a39818SAndrew Lewycky 103950cb7dd9SFelix Kuehling page = p->signal_page; 1040f3a39818SAndrew Lewycky if (!page) { 1041f3a39818SAndrew Lewycky /* Probably KFD bug, but mmap is user-accessible. */ 104250cb7dd9SFelix Kuehling pr_debug("Signal page could not be found\n"); 1043f3a39818SAndrew Lewycky return -EINVAL; 1044f3a39818SAndrew Lewycky } 1045f3a39818SAndrew Lewycky 1046f3a39818SAndrew Lewycky pfn = __pa(page->kernel_address); 1047f3a39818SAndrew Lewycky pfn >>= PAGE_SHIFT; 1048f3a39818SAndrew Lewycky 1049f3a39818SAndrew Lewycky vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE 1050f3a39818SAndrew Lewycky | VM_DONTDUMP | VM_PFNMAP; 1051f3a39818SAndrew Lewycky 105279775b62SKent Russell pr_debug("Mapping signal page\n"); 1053f3a39818SAndrew Lewycky pr_debug(" start user address == 0x%08lx\n", vma->vm_start); 1054f3a39818SAndrew Lewycky pr_debug(" end user address == 0x%08lx\n", vma->vm_end); 1055f3a39818SAndrew Lewycky pr_debug(" pfn == 0x%016lX\n", pfn); 1056f3a39818SAndrew Lewycky pr_debug(" vm_flags == 0x%08lX\n", vma->vm_flags); 1057f3a39818SAndrew Lewycky pr_debug(" size == 0x%08lX\n", 1058f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start); 1059f3a39818SAndrew Lewycky 1060f3a39818SAndrew Lewycky page->user_address = (uint64_t __user *)vma->vm_start; 1061f3a39818SAndrew Lewycky 1062f3a39818SAndrew Lewycky /* mapping the page to user process */ 1063b9a5d0a5SFelix Kuehling ret = remap_pfn_range(vma, vma->vm_start, pfn, 1064f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start, vma->vm_page_prot); 1065b9a5d0a5SFelix Kuehling if (!ret) 1066b9a5d0a5SFelix Kuehling p->signal_mapped_size = vma->vm_end - vma->vm_start; 1067b9a5d0a5SFelix Kuehling 1068b9a5d0a5SFelix Kuehling return ret; 1069f3a39818SAndrew Lewycky } 107059d3e8beSAlexey Skidanov 107159d3e8beSAlexey Skidanov /* 10725273e82cSFelix Kuehling * Assumes that p is not going away. 107359d3e8beSAlexey Skidanov */ 107459d3e8beSAlexey Skidanov static void lookup_events_by_type_and_signal(struct kfd_process *p, 107559d3e8beSAlexey Skidanov int type, void *event_data) 107659d3e8beSAlexey Skidanov { 107759d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *ev_data; 107859d3e8beSAlexey Skidanov struct kfd_event *ev; 1079482f0777SFelix Kuehling uint32_t id; 108059d3e8beSAlexey Skidanov bool send_signal = true; 108159d3e8beSAlexey Skidanov 108259d3e8beSAlexey Skidanov ev_data = (struct kfd_hsa_memory_exception_data *) event_data; 108359d3e8beSAlexey Skidanov 10845273e82cSFelix Kuehling rcu_read_lock(); 10855273e82cSFelix Kuehling 1086482f0777SFelix Kuehling id = KFD_FIRST_NONSIGNAL_EVENT_ID; 1087482f0777SFelix Kuehling idr_for_each_entry_continue(&p->event_idr, ev, id) 108859d3e8beSAlexey Skidanov if (ev->type == type) { 108959d3e8beSAlexey Skidanov send_signal = false; 109059d3e8beSAlexey Skidanov dev_dbg(kfd_device, 109159d3e8beSAlexey Skidanov "Event found: id %X type %d", 109259d3e8beSAlexey Skidanov ev->event_id, ev->type); 10935273e82cSFelix Kuehling spin_lock(&ev->lock); 109459d3e8beSAlexey Skidanov set_event(ev); 109559d3e8beSAlexey Skidanov if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data) 109659d3e8beSAlexey Skidanov ev->memory_exception_data = *ev_data; 10975273e82cSFelix Kuehling spin_unlock(&ev->lock); 109859d3e8beSAlexey Skidanov } 109959d3e8beSAlexey Skidanov 1100101fee63SMoses Reuben if (type == KFD_EVENT_TYPE_MEMORY) { 1101101fee63SMoses Reuben dev_warn(kfd_device, 11026027b1bfSYong Zhao "Sending SIGSEGV to process %d (pasid 0x%x)", 11036027b1bfSYong Zhao p->lead_thread->pid, p->pasid); 1104101fee63SMoses Reuben send_sig(SIGSEGV, p->lead_thread, 0); 1105101fee63SMoses Reuben } 1106101fee63SMoses Reuben 110759d3e8beSAlexey Skidanov /* Send SIGTERM no event of type "type" has been found*/ 110859d3e8beSAlexey Skidanov if (send_signal) { 110981663016SOded Gabbay if (send_sigterm) { 111059d3e8beSAlexey Skidanov dev_warn(kfd_device, 11116027b1bfSYong Zhao "Sending SIGTERM to process %d (pasid 0x%x)", 11126027b1bfSYong Zhao p->lead_thread->pid, p->pasid); 111359d3e8beSAlexey Skidanov send_sig(SIGTERM, p->lead_thread, 0); 111481663016SOded Gabbay } else { 111581663016SOded Gabbay dev_err(kfd_device, 11166027b1bfSYong Zhao "Process %d (pasid 0x%x) got unhandled exception", 11176027b1bfSYong Zhao p->lead_thread->pid, p->pasid); 111881663016SOded Gabbay } 111959d3e8beSAlexey Skidanov } 11205273e82cSFelix Kuehling 11215273e82cSFelix Kuehling rcu_read_unlock(); 112259d3e8beSAlexey Skidanov } 112359d3e8beSAlexey Skidanov 112464d1c3a4SFelix Kuehling #ifdef KFD_SUPPORT_IOMMU_V2 1125c7b6bac9SFenghua Yu void kfd_signal_iommu_event(struct kfd_dev *dev, u32 pasid, 112659d3e8beSAlexey Skidanov unsigned long address, bool is_write_requested, 112759d3e8beSAlexey Skidanov bool is_execute_requested) 112859d3e8beSAlexey Skidanov { 112959d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data memory_exception_data; 113059d3e8beSAlexey Skidanov struct vm_area_struct *vma; 1131bef153b7SDavid Yat Sin int user_gpu_id; 113259d3e8beSAlexey Skidanov 113359d3e8beSAlexey Skidanov /* 113459d3e8beSAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 113559d3e8beSAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 1136abb208a8SFelix Kuehling * running so the lookup function increments the process ref count. 113759d3e8beSAlexey Skidanov */ 113859d3e8beSAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 11399b56bb11SFelix Kuehling struct mm_struct *mm; 114059d3e8beSAlexey Skidanov 114159d3e8beSAlexey Skidanov if (!p) 114259d3e8beSAlexey Skidanov return; /* Presumably process exited. */ 114359d3e8beSAlexey Skidanov 11449b56bb11SFelix Kuehling /* Take a safe reference to the mm_struct, which may otherwise 11459b56bb11SFelix Kuehling * disappear even while the kfd_process is still referenced. 11469b56bb11SFelix Kuehling */ 11479b56bb11SFelix Kuehling mm = get_task_mm(p->lead_thread); 11489b56bb11SFelix Kuehling if (!mm) { 1149abb208a8SFelix Kuehling kfd_unref_process(p); 11509b56bb11SFelix Kuehling return; /* Process is exiting */ 11519b56bb11SFelix Kuehling } 11529b56bb11SFelix Kuehling 1153bef153b7SDavid Yat Sin user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); 1154bef153b7SDavid Yat Sin if (unlikely(user_gpu_id == -EINVAL)) { 1155bef153b7SDavid Yat Sin WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); 1156bef153b7SDavid Yat Sin return; 1157bef153b7SDavid Yat Sin } 115859d3e8beSAlexey Skidanov memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 115959d3e8beSAlexey Skidanov 1160d8ed45c5SMichel Lespinasse mmap_read_lock(mm); 11619b56bb11SFelix Kuehling vma = find_vma(mm, address); 116259d3e8beSAlexey Skidanov 1163bef153b7SDavid Yat Sin memory_exception_data.gpu_id = user_gpu_id; 116459d3e8beSAlexey Skidanov memory_exception_data.va = address; 116559d3e8beSAlexey Skidanov /* Set failure reason */ 116659d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 116759d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 116859d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 1169359cecddSYong Zhao if (vma && address >= vma->vm_start) { 117059d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 0; 1171359cecddSYong Zhao 117259d3e8beSAlexey Skidanov if (is_write_requested && !(vma->vm_flags & VM_WRITE)) 117359d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 1; 117459d3e8beSAlexey Skidanov else 117559d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 1176359cecddSYong Zhao 117759d3e8beSAlexey Skidanov if (is_execute_requested && !(vma->vm_flags & VM_EXEC)) 117859d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 1; 117959d3e8beSAlexey Skidanov else 118059d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 118159d3e8beSAlexey Skidanov } 118259d3e8beSAlexey Skidanov 1183d8ed45c5SMichel Lespinasse mmap_read_unlock(mm); 11849b56bb11SFelix Kuehling mmput(mm); 118559d3e8beSAlexey Skidanov 11868725aecaSYong Zhao pr_debug("notpresent %d, noexecute %d, readonly %d\n", 11878725aecaSYong Zhao memory_exception_data.failure.NotPresent, 11888725aecaSYong Zhao memory_exception_data.failure.NoExecute, 11898725aecaSYong Zhao memory_exception_data.failure.ReadOnly); 11908725aecaSYong Zhao 11918725aecaSYong Zhao /* Workaround on Raven to not kill the process when memory is freed 11928725aecaSYong Zhao * before IOMMU is able to finish processing all the excessive PPRs 11938725aecaSYong Zhao */ 1194046e674bSGraham Sider 1195046e674bSGraham Sider if (KFD_GC_VERSION(dev) != IP_VERSION(9, 1, 0) && 1196046e674bSGraham Sider KFD_GC_VERSION(dev) != IP_VERSION(9, 2, 2) && 11975273e82cSFelix Kuehling KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0)) 119859d3e8beSAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY, 119959d3e8beSAlexey Skidanov &memory_exception_data); 120059d3e8beSAlexey Skidanov 1201abb208a8SFelix Kuehling kfd_unref_process(p); 120259d3e8beSAlexey Skidanov } 120364d1c3a4SFelix Kuehling #endif /* KFD_SUPPORT_IOMMU_V2 */ 1204930c5ff4SAlexey Skidanov 1205c7b6bac9SFenghua Yu void kfd_signal_hw_exception_event(u32 pasid) 1206930c5ff4SAlexey Skidanov { 1207930c5ff4SAlexey Skidanov /* 1208930c5ff4SAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 1209930c5ff4SAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 1210abb208a8SFelix Kuehling * running so the lookup function increments the process ref count. 1211930c5ff4SAlexey Skidanov */ 1212930c5ff4SAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 1213930c5ff4SAlexey Skidanov 1214930c5ff4SAlexey Skidanov if (!p) 1215930c5ff4SAlexey Skidanov return; /* Presumably process exited. */ 1216930c5ff4SAlexey Skidanov 1217930c5ff4SAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_HW_EXCEPTION, NULL); 1218abb208a8SFelix Kuehling kfd_unref_process(p); 1219930c5ff4SAlexey Skidanov } 12202640c3faSshaoyunl 1221c7b6bac9SFenghua Yu void kfd_signal_vm_fault_event(struct kfd_dev *dev, u32 pasid, 12222640c3faSshaoyunl struct kfd_vm_fault_info *info) 12232640c3faSshaoyunl { 12242640c3faSshaoyunl struct kfd_event *ev; 12252640c3faSshaoyunl uint32_t id; 12262640c3faSshaoyunl struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 12272640c3faSshaoyunl struct kfd_hsa_memory_exception_data memory_exception_data; 1228bef153b7SDavid Yat Sin int user_gpu_id; 12292640c3faSshaoyunl 12302640c3faSshaoyunl if (!p) 12312640c3faSshaoyunl return; /* Presumably process exited. */ 1232bef153b7SDavid Yat Sin 1233bef153b7SDavid Yat Sin user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); 1234bef153b7SDavid Yat Sin if (unlikely(user_gpu_id == -EINVAL)) { 1235bef153b7SDavid Yat Sin WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); 1236bef153b7SDavid Yat Sin return; 1237bef153b7SDavid Yat Sin } 1238bef153b7SDavid Yat Sin 12392640c3faSshaoyunl memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 1240bef153b7SDavid Yat Sin memory_exception_data.gpu_id = user_gpu_id; 12410d87c9cfSKent Russell memory_exception_data.failure.imprecise = true; 12422640c3faSshaoyunl /* Set failure reason */ 12432640c3faSshaoyunl if (info) { 12442640c3faSshaoyunl memory_exception_data.va = (info->page_addr) << PAGE_SHIFT; 12452640c3faSshaoyunl memory_exception_data.failure.NotPresent = 12462640c3faSshaoyunl info->prot_valid ? 1 : 0; 12472640c3faSshaoyunl memory_exception_data.failure.NoExecute = 12482640c3faSshaoyunl info->prot_exec ? 1 : 0; 12492640c3faSshaoyunl memory_exception_data.failure.ReadOnly = 12502640c3faSshaoyunl info->prot_write ? 1 : 0; 12512640c3faSshaoyunl memory_exception_data.failure.imprecise = 0; 12522640c3faSshaoyunl } 12535273e82cSFelix Kuehling 12545273e82cSFelix Kuehling rcu_read_lock(); 12552640c3faSshaoyunl 12562640c3faSshaoyunl id = KFD_FIRST_NONSIGNAL_EVENT_ID; 12572640c3faSshaoyunl idr_for_each_entry_continue(&p->event_idr, ev, id) 12582640c3faSshaoyunl if (ev->type == KFD_EVENT_TYPE_MEMORY) { 12595273e82cSFelix Kuehling spin_lock(&ev->lock); 12602640c3faSshaoyunl ev->memory_exception_data = memory_exception_data; 12612640c3faSshaoyunl set_event(ev); 12625273e82cSFelix Kuehling spin_unlock(&ev->lock); 12632640c3faSshaoyunl } 12642640c3faSshaoyunl 12655273e82cSFelix Kuehling rcu_read_unlock(); 12662640c3faSshaoyunl kfd_unref_process(p); 12672640c3faSshaoyunl } 1268e42051d2SShaoyun Liu 1269e42051d2SShaoyun Liu void kfd_signal_reset_event(struct kfd_dev *dev) 1270e42051d2SShaoyun Liu { 1271e42051d2SShaoyun Liu struct kfd_hsa_hw_exception_data hw_exception_data; 12729b54d201SEric Huang struct kfd_hsa_memory_exception_data memory_exception_data; 1273e42051d2SShaoyun Liu struct kfd_process *p; 1274e42051d2SShaoyun Liu struct kfd_event *ev; 1275e42051d2SShaoyun Liu unsigned int temp; 1276e42051d2SShaoyun Liu uint32_t id, idx; 12779b54d201SEric Huang int reset_cause = atomic_read(&dev->sram_ecc_flag) ? 12789b54d201SEric Huang KFD_HW_EXCEPTION_ECC : 12799b54d201SEric Huang KFD_HW_EXCEPTION_GPU_HANG; 1280e42051d2SShaoyun Liu 1281e42051d2SShaoyun Liu /* Whole gpu reset caused by GPU hang and memory is lost */ 1282e42051d2SShaoyun Liu memset(&hw_exception_data, 0, sizeof(hw_exception_data)); 1283e42051d2SShaoyun Liu hw_exception_data.memory_lost = 1; 12849b54d201SEric Huang hw_exception_data.reset_cause = reset_cause; 12859b54d201SEric Huang 12869b54d201SEric Huang memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 12879b54d201SEric Huang memory_exception_data.ErrorType = KFD_MEM_ERR_SRAM_ECC; 12889b54d201SEric Huang memory_exception_data.failure.imprecise = true; 1289e42051d2SShaoyun Liu 1290e42051d2SShaoyun Liu idx = srcu_read_lock(&kfd_processes_srcu); 1291e42051d2SShaoyun Liu hash_for_each_rcu(kfd_processes_table, temp, p, kfd_processes) { 1292bef153b7SDavid Yat Sin int user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); 1293bef153b7SDavid Yat Sin 1294bef153b7SDavid Yat Sin if (unlikely(user_gpu_id == -EINVAL)) { 1295bef153b7SDavid Yat Sin WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); 1296bef153b7SDavid Yat Sin continue; 1297bef153b7SDavid Yat Sin } 1298bef153b7SDavid Yat Sin 12995273e82cSFelix Kuehling rcu_read_lock(); 13005273e82cSFelix Kuehling 1301e42051d2SShaoyun Liu id = KFD_FIRST_NONSIGNAL_EVENT_ID; 13029b54d201SEric Huang idr_for_each_entry_continue(&p->event_idr, ev, id) { 1303e42051d2SShaoyun Liu if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) { 13045273e82cSFelix Kuehling spin_lock(&ev->lock); 1305e42051d2SShaoyun Liu ev->hw_exception_data = hw_exception_data; 1306bef153b7SDavid Yat Sin ev->hw_exception_data.gpu_id = user_gpu_id; 1307e42051d2SShaoyun Liu set_event(ev); 13085273e82cSFelix Kuehling spin_unlock(&ev->lock); 1309e42051d2SShaoyun Liu } 13109b54d201SEric Huang if (ev->type == KFD_EVENT_TYPE_MEMORY && 13119b54d201SEric Huang reset_cause == KFD_HW_EXCEPTION_ECC) { 13125273e82cSFelix Kuehling spin_lock(&ev->lock); 13139b54d201SEric Huang ev->memory_exception_data = memory_exception_data; 1314bef153b7SDavid Yat Sin ev->memory_exception_data.gpu_id = user_gpu_id; 13159b54d201SEric Huang set_event(ev); 13165273e82cSFelix Kuehling spin_unlock(&ev->lock); 13179b54d201SEric Huang } 13189b54d201SEric Huang } 13195273e82cSFelix Kuehling 13205273e82cSFelix Kuehling rcu_read_unlock(); 1321e42051d2SShaoyun Liu } 1322e42051d2SShaoyun Liu srcu_read_unlock(&kfd_processes_srcu, idx); 1323e42051d2SShaoyun Liu } 1324e2b1f9f5SDennis Li 1325e2b1f9f5SDennis Li void kfd_signal_poison_consumed_event(struct kfd_dev *dev, u32 pasid) 1326e2b1f9f5SDennis Li { 1327e2b1f9f5SDennis Li struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 1328e2b1f9f5SDennis Li struct kfd_hsa_memory_exception_data memory_exception_data; 1329e2b1f9f5SDennis Li struct kfd_hsa_hw_exception_data hw_exception_data; 1330e2b1f9f5SDennis Li struct kfd_event *ev; 1331e2b1f9f5SDennis Li uint32_t id = KFD_FIRST_NONSIGNAL_EVENT_ID; 1332bef153b7SDavid Yat Sin int user_gpu_id; 1333e2b1f9f5SDennis Li 1334e2b1f9f5SDennis Li if (!p) 1335e2b1f9f5SDennis Li return; /* Presumably process exited. */ 1336e2b1f9f5SDennis Li 1337bef153b7SDavid Yat Sin user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); 1338bef153b7SDavid Yat Sin if (unlikely(user_gpu_id == -EINVAL)) { 1339bef153b7SDavid Yat Sin WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); 1340bef153b7SDavid Yat Sin return; 1341bef153b7SDavid Yat Sin } 1342bef153b7SDavid Yat Sin 1343e2b1f9f5SDennis Li memset(&hw_exception_data, 0, sizeof(hw_exception_data)); 1344bef153b7SDavid Yat Sin hw_exception_data.gpu_id = user_gpu_id; 1345e2b1f9f5SDennis Li hw_exception_data.memory_lost = 1; 1346e2b1f9f5SDennis Li hw_exception_data.reset_cause = KFD_HW_EXCEPTION_ECC; 1347e2b1f9f5SDennis Li 1348e2b1f9f5SDennis Li memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 1349e2b1f9f5SDennis Li memory_exception_data.ErrorType = KFD_MEM_ERR_POISON_CONSUMED; 1350bef153b7SDavid Yat Sin memory_exception_data.gpu_id = user_gpu_id; 1351e2b1f9f5SDennis Li memory_exception_data.failure.imprecise = true; 1352e2b1f9f5SDennis Li 13535273e82cSFelix Kuehling rcu_read_lock(); 13545273e82cSFelix Kuehling 1355e2b1f9f5SDennis Li idr_for_each_entry_continue(&p->event_idr, ev, id) { 1356e2b1f9f5SDennis Li if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) { 13575273e82cSFelix Kuehling spin_lock(&ev->lock); 1358e2b1f9f5SDennis Li ev->hw_exception_data = hw_exception_data; 1359e2b1f9f5SDennis Li set_event(ev); 13605273e82cSFelix Kuehling spin_unlock(&ev->lock); 1361e2b1f9f5SDennis Li } 1362e2b1f9f5SDennis Li 1363e2b1f9f5SDennis Li if (ev->type == KFD_EVENT_TYPE_MEMORY) { 13645273e82cSFelix Kuehling spin_lock(&ev->lock); 1365e2b1f9f5SDennis Li ev->memory_exception_data = memory_exception_data; 1366e2b1f9f5SDennis Li set_event(ev); 13675273e82cSFelix Kuehling spin_unlock(&ev->lock); 1368e2b1f9f5SDennis Li } 1369e2b1f9f5SDennis Li } 13705273e82cSFelix Kuehling 13715273e82cSFelix Kuehling rcu_read_unlock(); 1372e2b1f9f5SDennis Li 1373e2b1f9f5SDennis Li /* user application will handle SIGBUS signal */ 1374e2b1f9f5SDennis Li send_sig(SIGBUS, p->lead_thread, 0); 137596b62c8aSDennis Li 137696b62c8aSDennis Li kfd_unref_process(p); 1377e2b1f9f5SDennis Li } 1378