1f3a39818SAndrew Lewycky /* 2f3a39818SAndrew Lewycky * Copyright 2014 Advanced Micro Devices, Inc. 3f3a39818SAndrew Lewycky * 4f3a39818SAndrew Lewycky * Permission is hereby granted, free of charge, to any person obtaining a 5f3a39818SAndrew Lewycky * copy of this software and associated documentation files (the "Software"), 6f3a39818SAndrew Lewycky * to deal in the Software without restriction, including without limitation 7f3a39818SAndrew Lewycky * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8f3a39818SAndrew Lewycky * and/or sell copies of the Software, and to permit persons to whom the 9f3a39818SAndrew Lewycky * Software is furnished to do so, subject to the following conditions: 10f3a39818SAndrew Lewycky * 11f3a39818SAndrew Lewycky * The above copyright notice and this permission notice shall be included in 12f3a39818SAndrew Lewycky * all copies or substantial portions of the Software. 13f3a39818SAndrew Lewycky * 14f3a39818SAndrew Lewycky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15f3a39818SAndrew Lewycky * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16f3a39818SAndrew Lewycky * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17f3a39818SAndrew Lewycky * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18f3a39818SAndrew Lewycky * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19f3a39818SAndrew Lewycky * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20f3a39818SAndrew Lewycky * OTHER DEALINGS IN THE SOFTWARE. 21f3a39818SAndrew Lewycky */ 22f3a39818SAndrew Lewycky 23f3a39818SAndrew Lewycky #include <linux/mm_types.h> 24f3a39818SAndrew Lewycky #include <linux/slab.h> 25f3a39818SAndrew Lewycky #include <linux/types.h> 26f3a39818SAndrew Lewycky #include <linux/sched.h> 27f3a39818SAndrew Lewycky #include <linux/uaccess.h> 28f3a39818SAndrew Lewycky #include <linux/mm.h> 29f3a39818SAndrew Lewycky #include <linux/mman.h> 30f3a39818SAndrew Lewycky #include <linux/memory.h> 31f3a39818SAndrew Lewycky #include "kfd_priv.h" 32f3a39818SAndrew Lewycky #include "kfd_events.h" 3359d3e8beSAlexey Skidanov #include <linux/device.h> 34f3a39818SAndrew Lewycky 35f3a39818SAndrew Lewycky /* 36f3a39818SAndrew Lewycky * A task can only be on a single wait_queue at a time, but we need to support 37f3a39818SAndrew Lewycky * waiting on multiple events (any/all). 38f3a39818SAndrew Lewycky * Instead of each event simply having a wait_queue with sleeping tasks, it 39f3a39818SAndrew Lewycky * has a singly-linked list of tasks. 40f3a39818SAndrew Lewycky * A thread that wants to sleep creates an array of these, one for each event 41f3a39818SAndrew Lewycky * and adds one to each event's waiter chain. 42f3a39818SAndrew Lewycky */ 43f3a39818SAndrew Lewycky struct kfd_event_waiter { 44f3a39818SAndrew Lewycky struct list_head waiters; 45f3a39818SAndrew Lewycky struct task_struct *sleeping_task; 46f3a39818SAndrew Lewycky 47f3a39818SAndrew Lewycky /* Transitions to true when the event this belongs to is signaled. */ 48f3a39818SAndrew Lewycky bool activated; 4959d3e8beSAlexey Skidanov 5059d3e8beSAlexey Skidanov /* Event */ 5159d3e8beSAlexey Skidanov struct kfd_event *event; 5259d3e8beSAlexey Skidanov uint32_t input_index; 53f3a39818SAndrew Lewycky }; 54f3a39818SAndrew Lewycky 55f3a39818SAndrew Lewycky /* 56f3a39818SAndrew Lewycky * Over-complicated pooled allocator for event notification slots. 57f3a39818SAndrew Lewycky * 58f3a39818SAndrew Lewycky * Each signal event needs a 64-bit signal slot where the signaler will write 59f3a39818SAndrew Lewycky * a 1 before sending an interrupt.l (This is needed because some interrupts 60f3a39818SAndrew Lewycky * do not contain enough spare data bits to identify an event.) 61f3a39818SAndrew Lewycky * We get whole pages from vmalloc and map them to the process VA. 62f3a39818SAndrew Lewycky * Individual signal events are then allocated a slot in a page. 63f3a39818SAndrew Lewycky */ 64f3a39818SAndrew Lewycky 65f3a39818SAndrew Lewycky struct signal_page { 66f3a39818SAndrew Lewycky struct list_head event_pages; /* kfd_process.signal_event_pages */ 67f3a39818SAndrew Lewycky uint64_t *kernel_address; 68f3a39818SAndrew Lewycky uint64_t __user *user_address; 69f3a39818SAndrew Lewycky uint32_t page_index; /* Index into the mmap aperture. */ 70f3a39818SAndrew Lewycky unsigned int free_slots; 71f3a39818SAndrew Lewycky unsigned long used_slot_bitmap[0]; 72f3a39818SAndrew Lewycky }; 73f3a39818SAndrew Lewycky 74f3a39818SAndrew Lewycky #define SLOTS_PER_PAGE KFD_SIGNAL_EVENT_LIMIT 75f3a39818SAndrew Lewycky #define SLOT_BITMAP_SIZE BITS_TO_LONGS(SLOTS_PER_PAGE) 76f3a39818SAndrew Lewycky #define BITS_PER_PAGE (ilog2(SLOTS_PER_PAGE)+1) 77f3a39818SAndrew Lewycky #define SIGNAL_PAGE_SIZE (sizeof(struct signal_page) + \ 78f3a39818SAndrew Lewycky SLOT_BITMAP_SIZE * sizeof(long)) 79f3a39818SAndrew Lewycky 80f3a39818SAndrew Lewycky /* 81f3a39818SAndrew Lewycky * For signal events, the event ID is used as the interrupt user data. 82f3a39818SAndrew Lewycky * For SQ s_sendmsg interrupts, this is limited to 8 bits. 83f3a39818SAndrew Lewycky */ 84f3a39818SAndrew Lewycky 85f3a39818SAndrew Lewycky #define INTERRUPT_DATA_BITS 8 86f3a39818SAndrew Lewycky #define SIGNAL_EVENT_ID_SLOT_SHIFT 0 87f3a39818SAndrew Lewycky 88f3a39818SAndrew Lewycky static uint64_t *page_slots(struct signal_page *page) 89f3a39818SAndrew Lewycky { 90f3a39818SAndrew Lewycky return page->kernel_address; 91f3a39818SAndrew Lewycky } 92f3a39818SAndrew Lewycky 93f3a39818SAndrew Lewycky static bool allocate_free_slot(struct kfd_process *process, 94f3a39818SAndrew Lewycky struct signal_page **out_page, 95f3a39818SAndrew Lewycky unsigned int *out_slot_index) 96f3a39818SAndrew Lewycky { 97f3a39818SAndrew Lewycky struct signal_page *page; 98f3a39818SAndrew Lewycky 99f3a39818SAndrew Lewycky list_for_each_entry(page, &process->signal_event_pages, event_pages) { 100f3a39818SAndrew Lewycky if (page->free_slots > 0) { 101f3a39818SAndrew Lewycky unsigned int slot = 102f3a39818SAndrew Lewycky find_first_zero_bit(page->used_slot_bitmap, 103f3a39818SAndrew Lewycky SLOTS_PER_PAGE); 104f3a39818SAndrew Lewycky 105f3a39818SAndrew Lewycky __set_bit(slot, page->used_slot_bitmap); 106f3a39818SAndrew Lewycky page->free_slots--; 107f3a39818SAndrew Lewycky 108f3a39818SAndrew Lewycky page_slots(page)[slot] = UNSIGNALED_EVENT_SLOT; 109f3a39818SAndrew Lewycky 110f3a39818SAndrew Lewycky *out_page = page; 111f3a39818SAndrew Lewycky *out_slot_index = slot; 112f3a39818SAndrew Lewycky 113f3a39818SAndrew Lewycky pr_debug("allocated event signal slot in page %p, slot %d\n", 114f3a39818SAndrew Lewycky page, slot); 115f3a39818SAndrew Lewycky 116f3a39818SAndrew Lewycky return true; 117f3a39818SAndrew Lewycky } 118f3a39818SAndrew Lewycky } 119f3a39818SAndrew Lewycky 120f3a39818SAndrew Lewycky pr_debug("No free event signal slots were found for process %p\n", 121f3a39818SAndrew Lewycky process); 122f3a39818SAndrew Lewycky 123f3a39818SAndrew Lewycky return false; 124f3a39818SAndrew Lewycky } 125f3a39818SAndrew Lewycky 126f3a39818SAndrew Lewycky #define list_tail_entry(head, type, member) \ 127f3a39818SAndrew Lewycky list_entry((head)->prev, type, member) 128f3a39818SAndrew Lewycky 129f3a39818SAndrew Lewycky static bool allocate_signal_page(struct file *devkfd, struct kfd_process *p) 130f3a39818SAndrew Lewycky { 131f3a39818SAndrew Lewycky void *backing_store; 132f3a39818SAndrew Lewycky struct signal_page *page; 133f3a39818SAndrew Lewycky 134f3a39818SAndrew Lewycky page = kzalloc(SIGNAL_PAGE_SIZE, GFP_KERNEL); 135f3a39818SAndrew Lewycky if (!page) 136f3a39818SAndrew Lewycky goto fail_alloc_signal_page; 137f3a39818SAndrew Lewycky 138f3a39818SAndrew Lewycky page->free_slots = SLOTS_PER_PAGE; 139f3a39818SAndrew Lewycky 140f3a39818SAndrew Lewycky backing_store = (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO, 141f3a39818SAndrew Lewycky get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); 142f3a39818SAndrew Lewycky if (!backing_store) 143f3a39818SAndrew Lewycky goto fail_alloc_signal_store; 144f3a39818SAndrew Lewycky 145f3a39818SAndrew Lewycky /* prevent user-mode info leaks */ 146f3a39818SAndrew Lewycky memset(backing_store, (uint8_t) UNSIGNALED_EVENT_SLOT, 147f3a39818SAndrew Lewycky KFD_SIGNAL_EVENT_LIMIT * 8); 148f3a39818SAndrew Lewycky 149f3a39818SAndrew Lewycky page->kernel_address = backing_store; 150f3a39818SAndrew Lewycky 151f3a39818SAndrew Lewycky if (list_empty(&p->signal_event_pages)) 152f3a39818SAndrew Lewycky page->page_index = 0; 153f3a39818SAndrew Lewycky else 154f3a39818SAndrew Lewycky page->page_index = list_tail_entry(&p->signal_event_pages, 155f3a39818SAndrew Lewycky struct signal_page, 156f3a39818SAndrew Lewycky event_pages)->page_index + 1; 157f3a39818SAndrew Lewycky 158f3a39818SAndrew Lewycky pr_debug("allocated new event signal page at %p, for process %p\n", 159f3a39818SAndrew Lewycky page, p); 160f3a39818SAndrew Lewycky pr_debug("page index is %d\n", page->page_index); 161f3a39818SAndrew Lewycky 162f3a39818SAndrew Lewycky list_add(&page->event_pages, &p->signal_event_pages); 163f3a39818SAndrew Lewycky 164f3a39818SAndrew Lewycky return true; 165f3a39818SAndrew Lewycky 166f3a39818SAndrew Lewycky fail_alloc_signal_store: 167f3a39818SAndrew Lewycky kfree(page); 168f3a39818SAndrew Lewycky fail_alloc_signal_page: 169f3a39818SAndrew Lewycky return false; 170f3a39818SAndrew Lewycky } 171f3a39818SAndrew Lewycky 172f3a39818SAndrew Lewycky static bool allocate_event_notification_slot(struct file *devkfd, 173f3a39818SAndrew Lewycky struct kfd_process *p, 174f3a39818SAndrew Lewycky struct signal_page **page, 175f3a39818SAndrew Lewycky unsigned int *signal_slot_index) 176f3a39818SAndrew Lewycky { 177f3a39818SAndrew Lewycky bool ret; 178f3a39818SAndrew Lewycky 179f3a39818SAndrew Lewycky ret = allocate_free_slot(p, page, signal_slot_index); 180f3a39818SAndrew Lewycky if (ret == false) { 181f3a39818SAndrew Lewycky ret = allocate_signal_page(devkfd, p); 182f3a39818SAndrew Lewycky if (ret == true) 183f3a39818SAndrew Lewycky ret = allocate_free_slot(p, page, signal_slot_index); 184f3a39818SAndrew Lewycky } 185f3a39818SAndrew Lewycky 186f3a39818SAndrew Lewycky return ret; 187f3a39818SAndrew Lewycky } 188f3a39818SAndrew Lewycky 189f3a39818SAndrew Lewycky /* Assumes that the process's event_mutex is locked. */ 190f3a39818SAndrew Lewycky static void release_event_notification_slot(struct signal_page *page, 191f3a39818SAndrew Lewycky size_t slot_index) 192f3a39818SAndrew Lewycky { 193f3a39818SAndrew Lewycky __clear_bit(slot_index, page->used_slot_bitmap); 194f3a39818SAndrew Lewycky page->free_slots++; 195f3a39818SAndrew Lewycky 196f3a39818SAndrew Lewycky /* We don't free signal pages, they are retained by the process 197f3a39818SAndrew Lewycky * and reused until it exits. */ 198f3a39818SAndrew Lewycky } 199f3a39818SAndrew Lewycky 200f3a39818SAndrew Lewycky static struct signal_page *lookup_signal_page_by_index(struct kfd_process *p, 201f3a39818SAndrew Lewycky unsigned int page_index) 202f3a39818SAndrew Lewycky { 203f3a39818SAndrew Lewycky struct signal_page *page; 204f3a39818SAndrew Lewycky 205f3a39818SAndrew Lewycky /* 206f3a39818SAndrew Lewycky * This is safe because we don't delete signal pages until the 207f3a39818SAndrew Lewycky * process exits. 208f3a39818SAndrew Lewycky */ 209f3a39818SAndrew Lewycky list_for_each_entry(page, &p->signal_event_pages, event_pages) 210f3a39818SAndrew Lewycky if (page->page_index == page_index) 211f3a39818SAndrew Lewycky return page; 212f3a39818SAndrew Lewycky 213f3a39818SAndrew Lewycky return NULL; 214f3a39818SAndrew Lewycky } 215f3a39818SAndrew Lewycky 216f3a39818SAndrew Lewycky /* 217f3a39818SAndrew Lewycky * Assumes that p->event_mutex is held and of course that p is not going 218f3a39818SAndrew Lewycky * away (current or locked). 219f3a39818SAndrew Lewycky */ 220f3a39818SAndrew Lewycky static struct kfd_event *lookup_event_by_id(struct kfd_process *p, uint32_t id) 221f3a39818SAndrew Lewycky { 222f3a39818SAndrew Lewycky struct kfd_event *ev; 223f3a39818SAndrew Lewycky 224f3a39818SAndrew Lewycky hash_for_each_possible(p->events, ev, events, id) 225f3a39818SAndrew Lewycky if (ev->event_id == id) 226f3a39818SAndrew Lewycky return ev; 227f3a39818SAndrew Lewycky 228f3a39818SAndrew Lewycky return NULL; 229f3a39818SAndrew Lewycky } 230f3a39818SAndrew Lewycky 231f3a39818SAndrew Lewycky static u32 make_signal_event_id(struct signal_page *page, 232f3a39818SAndrew Lewycky unsigned int signal_slot_index) 233f3a39818SAndrew Lewycky { 234f3a39818SAndrew Lewycky return page->page_index | 235f3a39818SAndrew Lewycky (signal_slot_index << SIGNAL_EVENT_ID_SLOT_SHIFT); 236f3a39818SAndrew Lewycky } 237f3a39818SAndrew Lewycky 238f3a39818SAndrew Lewycky /* 239f3a39818SAndrew Lewycky * Produce a kfd event id for a nonsignal event. 240f3a39818SAndrew Lewycky * These are arbitrary numbers, so we do a sequential search through 241f3a39818SAndrew Lewycky * the hash table for an unused number. 242f3a39818SAndrew Lewycky */ 243f3a39818SAndrew Lewycky static u32 make_nonsignal_event_id(struct kfd_process *p) 244f3a39818SAndrew Lewycky { 245f3a39818SAndrew Lewycky u32 id; 246f3a39818SAndrew Lewycky 247f3a39818SAndrew Lewycky for (id = p->next_nonsignal_event_id; 248f3a39818SAndrew Lewycky id < KFD_LAST_NONSIGNAL_EVENT_ID && 249f3a39818SAndrew Lewycky lookup_event_by_id(p, id) != NULL; 250f3a39818SAndrew Lewycky id++) 251f3a39818SAndrew Lewycky ; 252f3a39818SAndrew Lewycky 253f3a39818SAndrew Lewycky if (id < KFD_LAST_NONSIGNAL_EVENT_ID) { 254f3a39818SAndrew Lewycky 255f3a39818SAndrew Lewycky /* 256f3a39818SAndrew Lewycky * What if id == LAST_NONSIGNAL_EVENT_ID - 1? 257f3a39818SAndrew Lewycky * Then next_nonsignal_event_id = LAST_NONSIGNAL_EVENT_ID so 258f3a39818SAndrew Lewycky * the first loop fails immediately and we proceed with the 259f3a39818SAndrew Lewycky * wraparound loop below. 260f3a39818SAndrew Lewycky */ 261f3a39818SAndrew Lewycky p->next_nonsignal_event_id = id + 1; 262f3a39818SAndrew Lewycky 263f3a39818SAndrew Lewycky return id; 264f3a39818SAndrew Lewycky } 265f3a39818SAndrew Lewycky 266f3a39818SAndrew Lewycky for (id = KFD_FIRST_NONSIGNAL_EVENT_ID; 267f3a39818SAndrew Lewycky id < KFD_LAST_NONSIGNAL_EVENT_ID && 268f3a39818SAndrew Lewycky lookup_event_by_id(p, id) != NULL; 269f3a39818SAndrew Lewycky id++) 270f3a39818SAndrew Lewycky ; 271f3a39818SAndrew Lewycky 272f3a39818SAndrew Lewycky 273f3a39818SAndrew Lewycky if (id < KFD_LAST_NONSIGNAL_EVENT_ID) { 274f3a39818SAndrew Lewycky p->next_nonsignal_event_id = id + 1; 275f3a39818SAndrew Lewycky return id; 276f3a39818SAndrew Lewycky } 277f3a39818SAndrew Lewycky 278f3a39818SAndrew Lewycky p->next_nonsignal_event_id = KFD_FIRST_NONSIGNAL_EVENT_ID; 279f3a39818SAndrew Lewycky return 0; 280f3a39818SAndrew Lewycky } 281f3a39818SAndrew Lewycky 282f3a39818SAndrew Lewycky static struct kfd_event *lookup_event_by_page_slot(struct kfd_process *p, 283f3a39818SAndrew Lewycky struct signal_page *page, 284f3a39818SAndrew Lewycky unsigned int signal_slot) 285f3a39818SAndrew Lewycky { 286f3a39818SAndrew Lewycky return lookup_event_by_id(p, make_signal_event_id(page, signal_slot)); 287f3a39818SAndrew Lewycky } 288f3a39818SAndrew Lewycky 289f3a39818SAndrew Lewycky static int create_signal_event(struct file *devkfd, 290f3a39818SAndrew Lewycky struct kfd_process *p, 291f3a39818SAndrew Lewycky struct kfd_event *ev) 292f3a39818SAndrew Lewycky { 293f3a39818SAndrew Lewycky if (p->signal_event_count == KFD_SIGNAL_EVENT_LIMIT) { 294f3a39818SAndrew Lewycky pr_warn("amdkfd: Signal event wasn't created because limit was reached\n"); 295f3a39818SAndrew Lewycky return -ENOMEM; 296f3a39818SAndrew Lewycky } 297f3a39818SAndrew Lewycky 298f3a39818SAndrew Lewycky if (!allocate_event_notification_slot(devkfd, p, &ev->signal_page, 299f3a39818SAndrew Lewycky &ev->signal_slot_index)) { 300f3a39818SAndrew Lewycky pr_warn("amdkfd: Signal event wasn't created because out of kernel memory\n"); 301f3a39818SAndrew Lewycky return -ENOMEM; 302f3a39818SAndrew Lewycky } 303f3a39818SAndrew Lewycky 304f3a39818SAndrew Lewycky p->signal_event_count++; 305f3a39818SAndrew Lewycky 306f3a39818SAndrew Lewycky ev->user_signal_address = 307f3a39818SAndrew Lewycky &ev->signal_page->user_address[ev->signal_slot_index]; 308f3a39818SAndrew Lewycky 309f3a39818SAndrew Lewycky ev->event_id = make_signal_event_id(ev->signal_page, 310f3a39818SAndrew Lewycky ev->signal_slot_index); 311f3a39818SAndrew Lewycky 312f3a39818SAndrew Lewycky pr_debug("signal event number %zu created with id %d, address %p\n", 313f3a39818SAndrew Lewycky p->signal_event_count, ev->event_id, 314f3a39818SAndrew Lewycky ev->user_signal_address); 315f3a39818SAndrew Lewycky 316f3a39818SAndrew Lewycky return 0; 317f3a39818SAndrew Lewycky } 318f3a39818SAndrew Lewycky 319f3a39818SAndrew Lewycky /* 320f3a39818SAndrew Lewycky * No non-signal events are supported yet. 321f3a39818SAndrew Lewycky * We create them as events that never signal. 322f3a39818SAndrew Lewycky * Set event calls from user-mode are failed. 323f3a39818SAndrew Lewycky */ 324f3a39818SAndrew Lewycky static int create_other_event(struct kfd_process *p, struct kfd_event *ev) 325f3a39818SAndrew Lewycky { 326f3a39818SAndrew Lewycky ev->event_id = make_nonsignal_event_id(p); 327f3a39818SAndrew Lewycky if (ev->event_id == 0) 328f3a39818SAndrew Lewycky return -ENOMEM; 329f3a39818SAndrew Lewycky 330f3a39818SAndrew Lewycky return 0; 331f3a39818SAndrew Lewycky } 332f3a39818SAndrew Lewycky 333f3a39818SAndrew Lewycky void kfd_event_init_process(struct kfd_process *p) 334f3a39818SAndrew Lewycky { 335f3a39818SAndrew Lewycky mutex_init(&p->event_mutex); 336f3a39818SAndrew Lewycky hash_init(p->events); 337f3a39818SAndrew Lewycky INIT_LIST_HEAD(&p->signal_event_pages); 338f3a39818SAndrew Lewycky p->next_nonsignal_event_id = KFD_FIRST_NONSIGNAL_EVENT_ID; 339f3a39818SAndrew Lewycky p->signal_event_count = 0; 340f3a39818SAndrew Lewycky } 341f3a39818SAndrew Lewycky 342f3a39818SAndrew Lewycky static void destroy_event(struct kfd_process *p, struct kfd_event *ev) 343f3a39818SAndrew Lewycky { 344f3a39818SAndrew Lewycky if (ev->signal_page != NULL) { 345f3a39818SAndrew Lewycky release_event_notification_slot(ev->signal_page, 346f3a39818SAndrew Lewycky ev->signal_slot_index); 347f3a39818SAndrew Lewycky p->signal_event_count--; 348f3a39818SAndrew Lewycky } 349f3a39818SAndrew Lewycky 350f3a39818SAndrew Lewycky /* 351f3a39818SAndrew Lewycky * Abandon the list of waiters. Individual waiting threads will 352f3a39818SAndrew Lewycky * clean up their own data. 353f3a39818SAndrew Lewycky */ 354f3a39818SAndrew Lewycky list_del(&ev->waiters); 355f3a39818SAndrew Lewycky 356f3a39818SAndrew Lewycky hash_del(&ev->events); 357f3a39818SAndrew Lewycky kfree(ev); 358f3a39818SAndrew Lewycky } 359f3a39818SAndrew Lewycky 360f3a39818SAndrew Lewycky static void destroy_events(struct kfd_process *p) 361f3a39818SAndrew Lewycky { 362f3a39818SAndrew Lewycky struct kfd_event *ev; 363f3a39818SAndrew Lewycky struct hlist_node *tmp; 364f3a39818SAndrew Lewycky unsigned int hash_bkt; 365f3a39818SAndrew Lewycky 366f3a39818SAndrew Lewycky hash_for_each_safe(p->events, hash_bkt, tmp, ev, events) 367f3a39818SAndrew Lewycky destroy_event(p, ev); 368f3a39818SAndrew Lewycky } 369f3a39818SAndrew Lewycky 370f3a39818SAndrew Lewycky /* 371f3a39818SAndrew Lewycky * We assume that the process is being destroyed and there is no need to 372f3a39818SAndrew Lewycky * unmap the pages or keep bookkeeping data in order. 373f3a39818SAndrew Lewycky */ 374f3a39818SAndrew Lewycky static void shutdown_signal_pages(struct kfd_process *p) 375f3a39818SAndrew Lewycky { 376f3a39818SAndrew Lewycky struct signal_page *page, *tmp; 377f3a39818SAndrew Lewycky 378f3a39818SAndrew Lewycky list_for_each_entry_safe(page, tmp, &p->signal_event_pages, 379f3a39818SAndrew Lewycky event_pages) { 380f3a39818SAndrew Lewycky free_pages((unsigned long)page->kernel_address, 381f3a39818SAndrew Lewycky get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); 382f3a39818SAndrew Lewycky kfree(page); 383f3a39818SAndrew Lewycky } 384f3a39818SAndrew Lewycky } 385f3a39818SAndrew Lewycky 386f3a39818SAndrew Lewycky void kfd_event_free_process(struct kfd_process *p) 387f3a39818SAndrew Lewycky { 388f3a39818SAndrew Lewycky destroy_events(p); 389f3a39818SAndrew Lewycky shutdown_signal_pages(p); 390f3a39818SAndrew Lewycky } 391f3a39818SAndrew Lewycky 392f3a39818SAndrew Lewycky static bool event_can_be_gpu_signaled(const struct kfd_event *ev) 393f3a39818SAndrew Lewycky { 394f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL || 395f3a39818SAndrew Lewycky ev->type == KFD_EVENT_TYPE_DEBUG; 396f3a39818SAndrew Lewycky } 397f3a39818SAndrew Lewycky 398f3a39818SAndrew Lewycky static bool event_can_be_cpu_signaled(const struct kfd_event *ev) 399f3a39818SAndrew Lewycky { 400f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL; 401f3a39818SAndrew Lewycky } 402f3a39818SAndrew Lewycky 403f3a39818SAndrew Lewycky int kfd_event_create(struct file *devkfd, struct kfd_process *p, 404f3a39818SAndrew Lewycky uint32_t event_type, bool auto_reset, uint32_t node_id, 405f3a39818SAndrew Lewycky uint32_t *event_id, uint32_t *event_trigger_data, 406f3a39818SAndrew Lewycky uint64_t *event_page_offset, uint32_t *event_slot_index) 407f3a39818SAndrew Lewycky { 408f3a39818SAndrew Lewycky int ret = 0; 409f3a39818SAndrew Lewycky struct kfd_event *ev = kzalloc(sizeof(*ev), GFP_KERNEL); 410f3a39818SAndrew Lewycky 411f3a39818SAndrew Lewycky if (!ev) 412f3a39818SAndrew Lewycky return -ENOMEM; 413f3a39818SAndrew Lewycky 414f3a39818SAndrew Lewycky ev->type = event_type; 415f3a39818SAndrew Lewycky ev->auto_reset = auto_reset; 416f3a39818SAndrew Lewycky ev->signaled = false; 417f3a39818SAndrew Lewycky 418f3a39818SAndrew Lewycky INIT_LIST_HEAD(&ev->waiters); 419f3a39818SAndrew Lewycky 420f3a39818SAndrew Lewycky *event_page_offset = 0; 421f3a39818SAndrew Lewycky 422f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 423f3a39818SAndrew Lewycky 424f3a39818SAndrew Lewycky switch (event_type) { 425f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_SIGNAL: 426f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_DEBUG: 427f3a39818SAndrew Lewycky ret = create_signal_event(devkfd, p, ev); 428f3a39818SAndrew Lewycky if (!ret) { 429f3a39818SAndrew Lewycky *event_page_offset = (ev->signal_page->page_index | 430f3a39818SAndrew Lewycky KFD_MMAP_EVENTS_MASK); 431f3a39818SAndrew Lewycky *event_page_offset <<= PAGE_SHIFT; 432f3a39818SAndrew Lewycky *event_slot_index = ev->signal_slot_index; 433f3a39818SAndrew Lewycky } 434f3a39818SAndrew Lewycky break; 435f3a39818SAndrew Lewycky default: 436f3a39818SAndrew Lewycky ret = create_other_event(p, ev); 437f3a39818SAndrew Lewycky break; 438f3a39818SAndrew Lewycky } 439f3a39818SAndrew Lewycky 440f3a39818SAndrew Lewycky if (!ret) { 441f3a39818SAndrew Lewycky hash_add(p->events, &ev->events, ev->event_id); 442f3a39818SAndrew Lewycky 443f3a39818SAndrew Lewycky *event_id = ev->event_id; 444f3a39818SAndrew Lewycky *event_trigger_data = ev->event_id; 445f3a39818SAndrew Lewycky } else { 446f3a39818SAndrew Lewycky kfree(ev); 447f3a39818SAndrew Lewycky } 448f3a39818SAndrew Lewycky 449f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 450f3a39818SAndrew Lewycky 451f3a39818SAndrew Lewycky return ret; 452f3a39818SAndrew Lewycky } 453f3a39818SAndrew Lewycky 454f3a39818SAndrew Lewycky /* Assumes that p is current. */ 455f3a39818SAndrew Lewycky int kfd_event_destroy(struct kfd_process *p, uint32_t event_id) 456f3a39818SAndrew Lewycky { 457f3a39818SAndrew Lewycky struct kfd_event *ev; 458f3a39818SAndrew Lewycky int ret = 0; 459f3a39818SAndrew Lewycky 460f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 461f3a39818SAndrew Lewycky 462f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 463f3a39818SAndrew Lewycky 464f3a39818SAndrew Lewycky if (ev) 465f3a39818SAndrew Lewycky destroy_event(p, ev); 466f3a39818SAndrew Lewycky else 467f3a39818SAndrew Lewycky ret = -EINVAL; 468f3a39818SAndrew Lewycky 469f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 470f3a39818SAndrew Lewycky return ret; 471f3a39818SAndrew Lewycky } 472f3a39818SAndrew Lewycky 473f3a39818SAndrew Lewycky static void set_event(struct kfd_event *ev) 474f3a39818SAndrew Lewycky { 475f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter; 476f3a39818SAndrew Lewycky struct kfd_event_waiter *next; 477f3a39818SAndrew Lewycky 478f3a39818SAndrew Lewycky /* Auto reset if the list is non-empty and we're waking someone. */ 479f3a39818SAndrew Lewycky ev->signaled = !ev->auto_reset || list_empty(&ev->waiters); 480f3a39818SAndrew Lewycky 481f3a39818SAndrew Lewycky list_for_each_entry_safe(waiter, next, &ev->waiters, waiters) { 482f3a39818SAndrew Lewycky waiter->activated = true; 483f3a39818SAndrew Lewycky 484f3a39818SAndrew Lewycky /* _init because free_waiters will call list_del */ 485f3a39818SAndrew Lewycky list_del_init(&waiter->waiters); 486f3a39818SAndrew Lewycky 487f3a39818SAndrew Lewycky wake_up_process(waiter->sleeping_task); 488f3a39818SAndrew Lewycky } 489f3a39818SAndrew Lewycky } 490f3a39818SAndrew Lewycky 491f3a39818SAndrew Lewycky /* Assumes that p is current. */ 492f3a39818SAndrew Lewycky int kfd_set_event(struct kfd_process *p, uint32_t event_id) 493f3a39818SAndrew Lewycky { 494f3a39818SAndrew Lewycky int ret = 0; 495f3a39818SAndrew Lewycky struct kfd_event *ev; 496f3a39818SAndrew Lewycky 497f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 498f3a39818SAndrew Lewycky 499f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 500f3a39818SAndrew Lewycky 501f3a39818SAndrew Lewycky if (ev && event_can_be_cpu_signaled(ev)) 502f3a39818SAndrew Lewycky set_event(ev); 503f3a39818SAndrew Lewycky else 504f3a39818SAndrew Lewycky ret = -EINVAL; 505f3a39818SAndrew Lewycky 506f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 507f3a39818SAndrew Lewycky return ret; 508f3a39818SAndrew Lewycky } 509f3a39818SAndrew Lewycky 510f3a39818SAndrew Lewycky static void reset_event(struct kfd_event *ev) 511f3a39818SAndrew Lewycky { 512f3a39818SAndrew Lewycky ev->signaled = false; 513f3a39818SAndrew Lewycky } 514f3a39818SAndrew Lewycky 515f3a39818SAndrew Lewycky /* Assumes that p is current. */ 516f3a39818SAndrew Lewycky int kfd_reset_event(struct kfd_process *p, uint32_t event_id) 517f3a39818SAndrew Lewycky { 518f3a39818SAndrew Lewycky int ret = 0; 519f3a39818SAndrew Lewycky struct kfd_event *ev; 520f3a39818SAndrew Lewycky 521f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 522f3a39818SAndrew Lewycky 523f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 524f3a39818SAndrew Lewycky 525f3a39818SAndrew Lewycky if (ev && event_can_be_cpu_signaled(ev)) 526f3a39818SAndrew Lewycky reset_event(ev); 527f3a39818SAndrew Lewycky else 528f3a39818SAndrew Lewycky ret = -EINVAL; 529f3a39818SAndrew Lewycky 530f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 531f3a39818SAndrew Lewycky return ret; 532f3a39818SAndrew Lewycky 533f3a39818SAndrew Lewycky } 534f3a39818SAndrew Lewycky 535f3a39818SAndrew Lewycky static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev) 536f3a39818SAndrew Lewycky { 537f3a39818SAndrew Lewycky page_slots(ev->signal_page)[ev->signal_slot_index] = 538f3a39818SAndrew Lewycky UNSIGNALED_EVENT_SLOT; 539f3a39818SAndrew Lewycky } 540f3a39818SAndrew Lewycky 541f3a39818SAndrew Lewycky static bool is_slot_signaled(struct signal_page *page, unsigned int index) 542f3a39818SAndrew Lewycky { 543f3a39818SAndrew Lewycky return page_slots(page)[index] != UNSIGNALED_EVENT_SLOT; 544f3a39818SAndrew Lewycky } 545f3a39818SAndrew Lewycky 546f3a39818SAndrew Lewycky static void set_event_from_interrupt(struct kfd_process *p, 547f3a39818SAndrew Lewycky struct kfd_event *ev) 548f3a39818SAndrew Lewycky { 549f3a39818SAndrew Lewycky if (ev && event_can_be_gpu_signaled(ev)) { 550f3a39818SAndrew Lewycky acknowledge_signal(p, ev); 551f3a39818SAndrew Lewycky set_event(ev); 552f3a39818SAndrew Lewycky } 553f3a39818SAndrew Lewycky } 554f3a39818SAndrew Lewycky 555f3a39818SAndrew Lewycky void kfd_signal_event_interrupt(unsigned int pasid, uint32_t partial_id, 556f3a39818SAndrew Lewycky uint32_t valid_id_bits) 557f3a39818SAndrew Lewycky { 558f3a39818SAndrew Lewycky struct kfd_event *ev; 559f3a39818SAndrew Lewycky 560f3a39818SAndrew Lewycky /* 561f3a39818SAndrew Lewycky * Because we are called from arbitrary context (workqueue) as opposed 562f3a39818SAndrew Lewycky * to process context, kfd_process could attempt to exit while we are 563f3a39818SAndrew Lewycky * running so the lookup function returns a locked process. 564f3a39818SAndrew Lewycky */ 565f3a39818SAndrew Lewycky struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 566f3a39818SAndrew Lewycky 567f3a39818SAndrew Lewycky if (!p) 568f3a39818SAndrew Lewycky return; /* Presumably process exited. */ 569f3a39818SAndrew Lewycky 570f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 571f3a39818SAndrew Lewycky 572f3a39818SAndrew Lewycky if (valid_id_bits >= INTERRUPT_DATA_BITS) { 573f3a39818SAndrew Lewycky /* Partial ID is a full ID. */ 574f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, partial_id); 575f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 576f3a39818SAndrew Lewycky } else { 577f3a39818SAndrew Lewycky /* 578f3a39818SAndrew Lewycky * Partial ID is in fact partial. For now we completely 579f3a39818SAndrew Lewycky * ignore it, but we could use any bits we did receive to 580f3a39818SAndrew Lewycky * search faster. 581f3a39818SAndrew Lewycky */ 582f3a39818SAndrew Lewycky struct signal_page *page; 583f3a39818SAndrew Lewycky unsigned i; 584f3a39818SAndrew Lewycky 585f3a39818SAndrew Lewycky list_for_each_entry(page, &p->signal_event_pages, event_pages) 586f3a39818SAndrew Lewycky for (i = 0; i < SLOTS_PER_PAGE; i++) 587f3a39818SAndrew Lewycky if (is_slot_signaled(page, i)) { 588f3a39818SAndrew Lewycky ev = lookup_event_by_page_slot(p, 589f3a39818SAndrew Lewycky page, i); 590f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 591f3a39818SAndrew Lewycky } 592f3a39818SAndrew Lewycky } 593f3a39818SAndrew Lewycky 594f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 595f3a39818SAndrew Lewycky mutex_unlock(&p->mutex); 596f3a39818SAndrew Lewycky } 597f3a39818SAndrew Lewycky 598f3a39818SAndrew Lewycky static struct kfd_event_waiter *alloc_event_waiters(uint32_t num_events) 599f3a39818SAndrew Lewycky { 600f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters; 601f3a39818SAndrew Lewycky uint32_t i; 602f3a39818SAndrew Lewycky 603f3a39818SAndrew Lewycky event_waiters = kmalloc_array(num_events, 604f3a39818SAndrew Lewycky sizeof(struct kfd_event_waiter), 605f3a39818SAndrew Lewycky GFP_KERNEL); 606f3a39818SAndrew Lewycky 607f3a39818SAndrew Lewycky for (i = 0; (event_waiters) && (i < num_events) ; i++) { 608f3a39818SAndrew Lewycky INIT_LIST_HEAD(&event_waiters[i].waiters); 609f3a39818SAndrew Lewycky event_waiters[i].sleeping_task = current; 610f3a39818SAndrew Lewycky event_waiters[i].activated = false; 611f3a39818SAndrew Lewycky } 612f3a39818SAndrew Lewycky 613f3a39818SAndrew Lewycky return event_waiters; 614f3a39818SAndrew Lewycky } 615f3a39818SAndrew Lewycky 616f3a39818SAndrew Lewycky static int init_event_waiter(struct kfd_process *p, 617f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter, 61859d3e8beSAlexey Skidanov uint32_t event_id, 61959d3e8beSAlexey Skidanov uint32_t input_index) 620f3a39818SAndrew Lewycky { 621f3a39818SAndrew Lewycky struct kfd_event *ev = lookup_event_by_id(p, event_id); 622f3a39818SAndrew Lewycky 623f3a39818SAndrew Lewycky if (!ev) 624f3a39818SAndrew Lewycky return -EINVAL; 625f3a39818SAndrew Lewycky 62659d3e8beSAlexey Skidanov waiter->event = ev; 62759d3e8beSAlexey Skidanov waiter->input_index = input_index; 628f3a39818SAndrew Lewycky waiter->activated = ev->signaled; 629f3a39818SAndrew Lewycky ev->signaled = ev->signaled && !ev->auto_reset; 630f3a39818SAndrew Lewycky 631f3a39818SAndrew Lewycky list_add(&waiter->waiters, &ev->waiters); 632f3a39818SAndrew Lewycky 633f3a39818SAndrew Lewycky return 0; 634f3a39818SAndrew Lewycky } 635f3a39818SAndrew Lewycky 636f3a39818SAndrew Lewycky static bool test_event_condition(bool all, uint32_t num_events, 637f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters) 638f3a39818SAndrew Lewycky { 639f3a39818SAndrew Lewycky uint32_t i; 640f3a39818SAndrew Lewycky uint32_t activated_count = 0; 641f3a39818SAndrew Lewycky 642f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 643f3a39818SAndrew Lewycky if (event_waiters[i].activated) { 644f3a39818SAndrew Lewycky if (!all) 645f3a39818SAndrew Lewycky return true; 646f3a39818SAndrew Lewycky 647f3a39818SAndrew Lewycky activated_count++; 648f3a39818SAndrew Lewycky } 649f3a39818SAndrew Lewycky } 650f3a39818SAndrew Lewycky 651f3a39818SAndrew Lewycky return activated_count == num_events; 652f3a39818SAndrew Lewycky } 653f3a39818SAndrew Lewycky 65459d3e8beSAlexey Skidanov /* 65559d3e8beSAlexey Skidanov * Copy event specific data, if defined. 65659d3e8beSAlexey Skidanov * Currently only memory exception events have additional data to copy to user 65759d3e8beSAlexey Skidanov */ 65859d3e8beSAlexey Skidanov static bool copy_signaled_event_data(uint32_t num_events, 65959d3e8beSAlexey Skidanov struct kfd_event_waiter *event_waiters, 66059d3e8beSAlexey Skidanov struct kfd_event_data __user *data) 66159d3e8beSAlexey Skidanov { 66259d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *src; 66359d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data __user *dst; 66459d3e8beSAlexey Skidanov struct kfd_event_waiter *waiter; 66559d3e8beSAlexey Skidanov struct kfd_event *event; 66659d3e8beSAlexey Skidanov uint32_t i; 66759d3e8beSAlexey Skidanov 66859d3e8beSAlexey Skidanov for (i = 0; i < num_events; i++) { 66959d3e8beSAlexey Skidanov waiter = &event_waiters[i]; 67059d3e8beSAlexey Skidanov event = waiter->event; 67159d3e8beSAlexey Skidanov if (waiter->activated && event->type == KFD_EVENT_TYPE_MEMORY) { 67259d3e8beSAlexey Skidanov dst = &data[waiter->input_index].memory_exception_data; 67359d3e8beSAlexey Skidanov src = &event->memory_exception_data; 67459d3e8beSAlexey Skidanov if (copy_to_user(dst, src, 67559d3e8beSAlexey Skidanov sizeof(struct kfd_hsa_memory_exception_data))) 67659d3e8beSAlexey Skidanov return false; 67759d3e8beSAlexey Skidanov } 67859d3e8beSAlexey Skidanov } 67959d3e8beSAlexey Skidanov 68059d3e8beSAlexey Skidanov return true; 68159d3e8beSAlexey Skidanov 68259d3e8beSAlexey Skidanov } 68359d3e8beSAlexey Skidanov 68459d3e8beSAlexey Skidanov 68559d3e8beSAlexey Skidanov 686f3a39818SAndrew Lewycky static long user_timeout_to_jiffies(uint32_t user_timeout_ms) 687f3a39818SAndrew Lewycky { 688f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE) 689f3a39818SAndrew Lewycky return 0; 690f3a39818SAndrew Lewycky 691f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_INFINITE) 692f3a39818SAndrew Lewycky return MAX_SCHEDULE_TIMEOUT; 693f3a39818SAndrew Lewycky 694f3a39818SAndrew Lewycky /* 695f3a39818SAndrew Lewycky * msecs_to_jiffies interprets all values above 2^31-1 as infinite, 696f3a39818SAndrew Lewycky * but we consider them finite. 697f3a39818SAndrew Lewycky * This hack is wrong, but nobody is likely to notice. 698f3a39818SAndrew Lewycky */ 699f3a39818SAndrew Lewycky user_timeout_ms = min_t(uint32_t, user_timeout_ms, 0x7FFFFFFF); 700f3a39818SAndrew Lewycky 701f3a39818SAndrew Lewycky return msecs_to_jiffies(user_timeout_ms) + 1; 702f3a39818SAndrew Lewycky } 703f3a39818SAndrew Lewycky 704f3a39818SAndrew Lewycky static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters) 705f3a39818SAndrew Lewycky { 706f3a39818SAndrew Lewycky uint32_t i; 707f3a39818SAndrew Lewycky 708f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) 709f3a39818SAndrew Lewycky list_del(&waiters[i].waiters); 710f3a39818SAndrew Lewycky 711f3a39818SAndrew Lewycky kfree(waiters); 712f3a39818SAndrew Lewycky } 713f3a39818SAndrew Lewycky 714f3a39818SAndrew Lewycky int kfd_wait_on_events(struct kfd_process *p, 71559d3e8beSAlexey Skidanov uint32_t num_events, void __user *data, 716f3a39818SAndrew Lewycky bool all, uint32_t user_timeout_ms, 717f3a39818SAndrew Lewycky enum kfd_event_wait_result *wait_result) 718f3a39818SAndrew Lewycky { 71959d3e8beSAlexey Skidanov struct kfd_event_data __user *events = 72059d3e8beSAlexey Skidanov (struct kfd_event_data __user *) data; 721f3a39818SAndrew Lewycky uint32_t i; 722f3a39818SAndrew Lewycky int ret = 0; 723f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters = NULL; 724f3a39818SAndrew Lewycky long timeout = user_timeout_to_jiffies(user_timeout_ms); 725f3a39818SAndrew Lewycky 726f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 727f3a39818SAndrew Lewycky 728f3a39818SAndrew Lewycky event_waiters = alloc_event_waiters(num_events); 729f3a39818SAndrew Lewycky if (!event_waiters) { 730f3a39818SAndrew Lewycky ret = -ENOMEM; 731f3a39818SAndrew Lewycky goto fail; 732f3a39818SAndrew Lewycky } 733f3a39818SAndrew Lewycky 734f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 73559d3e8beSAlexey Skidanov struct kfd_event_data event_data; 736f3a39818SAndrew Lewycky 73759d3e8beSAlexey Skidanov if (copy_from_user(&event_data, &events[i], 73859d3e8beSAlexey Skidanov sizeof(struct kfd_event_data))) 739f3a39818SAndrew Lewycky goto fail; 740f3a39818SAndrew Lewycky 74159d3e8beSAlexey Skidanov ret = init_event_waiter(p, &event_waiters[i], 74259d3e8beSAlexey Skidanov event_data.event_id, i); 743f3a39818SAndrew Lewycky if (ret) 744f3a39818SAndrew Lewycky goto fail; 745f3a39818SAndrew Lewycky } 746f3a39818SAndrew Lewycky 747f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 748f3a39818SAndrew Lewycky 749f3a39818SAndrew Lewycky while (true) { 750f3a39818SAndrew Lewycky if (fatal_signal_pending(current)) { 751f3a39818SAndrew Lewycky ret = -EINTR; 752f3a39818SAndrew Lewycky break; 753f3a39818SAndrew Lewycky } 754f3a39818SAndrew Lewycky 755f3a39818SAndrew Lewycky if (signal_pending(current)) { 756f3a39818SAndrew Lewycky /* 757f3a39818SAndrew Lewycky * This is wrong when a nonzero, non-infinite timeout 758f3a39818SAndrew Lewycky * is specified. We need to use 759f3a39818SAndrew Lewycky * ERESTARTSYS_RESTARTBLOCK, but struct restart_block 760f3a39818SAndrew Lewycky * contains a union with data for each user and it's 761f3a39818SAndrew Lewycky * in generic kernel code that I don't want to 762f3a39818SAndrew Lewycky * touch yet. 763f3a39818SAndrew Lewycky */ 764f3a39818SAndrew Lewycky ret = -ERESTARTSYS; 765f3a39818SAndrew Lewycky break; 766f3a39818SAndrew Lewycky } 767f3a39818SAndrew Lewycky 768f3a39818SAndrew Lewycky if (test_event_condition(all, num_events, event_waiters)) { 76959d3e8beSAlexey Skidanov if (copy_signaled_event_data(num_events, 77059d3e8beSAlexey Skidanov event_waiters, events)) 771f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_COMPLETE; 77259d3e8beSAlexey Skidanov else 77359d3e8beSAlexey Skidanov *wait_result = KFD_WAIT_ERROR; 774f3a39818SAndrew Lewycky break; 775f3a39818SAndrew Lewycky } 776f3a39818SAndrew Lewycky 777f3a39818SAndrew Lewycky if (timeout <= 0) { 778f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_TIMEOUT; 779f3a39818SAndrew Lewycky break; 780f3a39818SAndrew Lewycky } 781f3a39818SAndrew Lewycky 782f3a39818SAndrew Lewycky timeout = schedule_timeout_interruptible(timeout); 783f3a39818SAndrew Lewycky } 784f3a39818SAndrew Lewycky __set_current_state(TASK_RUNNING); 785f3a39818SAndrew Lewycky 786f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 787f3a39818SAndrew Lewycky free_waiters(num_events, event_waiters); 788f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 789f3a39818SAndrew Lewycky 790f3a39818SAndrew Lewycky return ret; 791f3a39818SAndrew Lewycky 792f3a39818SAndrew Lewycky fail: 793f3a39818SAndrew Lewycky if (event_waiters) 794f3a39818SAndrew Lewycky free_waiters(num_events, event_waiters); 795f3a39818SAndrew Lewycky 796f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 797f3a39818SAndrew Lewycky 798f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_ERROR; 799f3a39818SAndrew Lewycky 800f3a39818SAndrew Lewycky return ret; 801f3a39818SAndrew Lewycky } 802f3a39818SAndrew Lewycky 803f3a39818SAndrew Lewycky int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma) 804f3a39818SAndrew Lewycky { 805f3a39818SAndrew Lewycky 806f3a39818SAndrew Lewycky unsigned int page_index; 807f3a39818SAndrew Lewycky unsigned long pfn; 808f3a39818SAndrew Lewycky struct signal_page *page; 809f3a39818SAndrew Lewycky 810f3a39818SAndrew Lewycky /* check required size is logical */ 811f3a39818SAndrew Lewycky if (get_order(KFD_SIGNAL_EVENT_LIMIT * 8) != 812f3a39818SAndrew Lewycky get_order(vma->vm_end - vma->vm_start)) { 813f3a39818SAndrew Lewycky pr_err("amdkfd: event page mmap requested illegal size\n"); 814f3a39818SAndrew Lewycky return -EINVAL; 815f3a39818SAndrew Lewycky } 816f3a39818SAndrew Lewycky 817f3a39818SAndrew Lewycky page_index = vma->vm_pgoff; 818f3a39818SAndrew Lewycky 819f3a39818SAndrew Lewycky page = lookup_signal_page_by_index(p, page_index); 820f3a39818SAndrew Lewycky if (!page) { 821f3a39818SAndrew Lewycky /* Probably KFD bug, but mmap is user-accessible. */ 822f3a39818SAndrew Lewycky pr_debug("signal page could not be found for page_index %u\n", 823f3a39818SAndrew Lewycky page_index); 824f3a39818SAndrew Lewycky return -EINVAL; 825f3a39818SAndrew Lewycky } 826f3a39818SAndrew Lewycky 827f3a39818SAndrew Lewycky pfn = __pa(page->kernel_address); 828f3a39818SAndrew Lewycky pfn >>= PAGE_SHIFT; 829f3a39818SAndrew Lewycky 830f3a39818SAndrew Lewycky vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE 831f3a39818SAndrew Lewycky | VM_DONTDUMP | VM_PFNMAP; 832f3a39818SAndrew Lewycky 833f3a39818SAndrew Lewycky pr_debug("mapping signal page\n"); 834f3a39818SAndrew Lewycky pr_debug(" start user address == 0x%08lx\n", vma->vm_start); 835f3a39818SAndrew Lewycky pr_debug(" end user address == 0x%08lx\n", vma->vm_end); 836f3a39818SAndrew Lewycky pr_debug(" pfn == 0x%016lX\n", pfn); 837f3a39818SAndrew Lewycky pr_debug(" vm_flags == 0x%08lX\n", vma->vm_flags); 838f3a39818SAndrew Lewycky pr_debug(" size == 0x%08lX\n", 839f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start); 840f3a39818SAndrew Lewycky 841f3a39818SAndrew Lewycky page->user_address = (uint64_t __user *)vma->vm_start; 842f3a39818SAndrew Lewycky 843f3a39818SAndrew Lewycky /* mapping the page to user process */ 844f3a39818SAndrew Lewycky return remap_pfn_range(vma, vma->vm_start, pfn, 845f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start, vma->vm_page_prot); 846f3a39818SAndrew Lewycky } 84759d3e8beSAlexey Skidanov 84859d3e8beSAlexey Skidanov /* 84959d3e8beSAlexey Skidanov * Assumes that p->event_mutex is held and of course 85059d3e8beSAlexey Skidanov * that p is not going away (current or locked). 85159d3e8beSAlexey Skidanov */ 85259d3e8beSAlexey Skidanov static void lookup_events_by_type_and_signal(struct kfd_process *p, 85359d3e8beSAlexey Skidanov int type, void *event_data) 85459d3e8beSAlexey Skidanov { 85559d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *ev_data; 85659d3e8beSAlexey Skidanov struct kfd_event *ev; 85759d3e8beSAlexey Skidanov int bkt; 85859d3e8beSAlexey Skidanov bool send_signal = true; 85959d3e8beSAlexey Skidanov 86059d3e8beSAlexey Skidanov ev_data = (struct kfd_hsa_memory_exception_data *) event_data; 86159d3e8beSAlexey Skidanov 86259d3e8beSAlexey Skidanov hash_for_each(p->events, bkt, ev, events) 86359d3e8beSAlexey Skidanov if (ev->type == type) { 86459d3e8beSAlexey Skidanov send_signal = false; 86559d3e8beSAlexey Skidanov dev_dbg(kfd_device, 86659d3e8beSAlexey Skidanov "Event found: id %X type %d", 86759d3e8beSAlexey Skidanov ev->event_id, ev->type); 86859d3e8beSAlexey Skidanov set_event(ev); 86959d3e8beSAlexey Skidanov if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data) 87059d3e8beSAlexey Skidanov ev->memory_exception_data = *ev_data; 87159d3e8beSAlexey Skidanov } 87259d3e8beSAlexey Skidanov 87359d3e8beSAlexey Skidanov /* Send SIGTERM no event of type "type" has been found*/ 87459d3e8beSAlexey Skidanov if (send_signal) { 875*81663016SOded Gabbay if (send_sigterm) { 87659d3e8beSAlexey Skidanov dev_warn(kfd_device, 87759d3e8beSAlexey Skidanov "Sending SIGTERM to HSA Process with PID %d ", 87859d3e8beSAlexey Skidanov p->lead_thread->pid); 87959d3e8beSAlexey Skidanov send_sig(SIGTERM, p->lead_thread, 0); 880*81663016SOded Gabbay } else { 881*81663016SOded Gabbay dev_err(kfd_device, 882*81663016SOded Gabbay "HSA Process (PID %d) got unhandled exception", 883*81663016SOded Gabbay p->lead_thread->pid); 884*81663016SOded Gabbay } 88559d3e8beSAlexey Skidanov } 88659d3e8beSAlexey Skidanov } 88759d3e8beSAlexey Skidanov 88859d3e8beSAlexey Skidanov void kfd_signal_iommu_event(struct kfd_dev *dev, unsigned int pasid, 88959d3e8beSAlexey Skidanov unsigned long address, bool is_write_requested, 89059d3e8beSAlexey Skidanov bool is_execute_requested) 89159d3e8beSAlexey Skidanov { 89259d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data memory_exception_data; 89359d3e8beSAlexey Skidanov struct vm_area_struct *vma; 89459d3e8beSAlexey Skidanov 89559d3e8beSAlexey Skidanov /* 89659d3e8beSAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 89759d3e8beSAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 89859d3e8beSAlexey Skidanov * running so the lookup function returns a locked process. 89959d3e8beSAlexey Skidanov */ 90059d3e8beSAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 90159d3e8beSAlexey Skidanov 90259d3e8beSAlexey Skidanov if (!p) 90359d3e8beSAlexey Skidanov return; /* Presumably process exited. */ 90459d3e8beSAlexey Skidanov 90559d3e8beSAlexey Skidanov memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 90659d3e8beSAlexey Skidanov 90759d3e8beSAlexey Skidanov down_read(&p->mm->mmap_sem); 90859d3e8beSAlexey Skidanov vma = find_vma(p->mm, address); 90959d3e8beSAlexey Skidanov 91059d3e8beSAlexey Skidanov memory_exception_data.gpu_id = dev->id; 91159d3e8beSAlexey Skidanov memory_exception_data.va = address; 91259d3e8beSAlexey Skidanov /* Set failure reason */ 91359d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 91459d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 91559d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 91659d3e8beSAlexey Skidanov if (vma) { 91759d3e8beSAlexey Skidanov if (vma->vm_start > address) { 91859d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 91959d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 92059d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 92159d3e8beSAlexey Skidanov } else { 92259d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 0; 92359d3e8beSAlexey Skidanov if (is_write_requested && !(vma->vm_flags & VM_WRITE)) 92459d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 1; 92559d3e8beSAlexey Skidanov else 92659d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 92759d3e8beSAlexey Skidanov if (is_execute_requested && !(vma->vm_flags & VM_EXEC)) 92859d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 1; 92959d3e8beSAlexey Skidanov else 93059d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 93159d3e8beSAlexey Skidanov } 93259d3e8beSAlexey Skidanov } 93359d3e8beSAlexey Skidanov 93459d3e8beSAlexey Skidanov up_read(&p->mm->mmap_sem); 93559d3e8beSAlexey Skidanov 93659d3e8beSAlexey Skidanov mutex_lock(&p->event_mutex); 93759d3e8beSAlexey Skidanov 93859d3e8beSAlexey Skidanov /* Lookup events by type and signal them */ 93959d3e8beSAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY, 94059d3e8beSAlexey Skidanov &memory_exception_data); 94159d3e8beSAlexey Skidanov 94259d3e8beSAlexey Skidanov mutex_unlock(&p->event_mutex); 94359d3e8beSAlexey Skidanov mutex_unlock(&p->mutex); 94459d3e8beSAlexey Skidanov } 945930c5ff4SAlexey Skidanov 946930c5ff4SAlexey Skidanov void kfd_signal_hw_exception_event(unsigned int pasid) 947930c5ff4SAlexey Skidanov { 948930c5ff4SAlexey Skidanov /* 949930c5ff4SAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 950930c5ff4SAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 951930c5ff4SAlexey Skidanov * running so the lookup function returns a locked process. 952930c5ff4SAlexey Skidanov */ 953930c5ff4SAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 954930c5ff4SAlexey Skidanov 955930c5ff4SAlexey Skidanov if (!p) 956930c5ff4SAlexey Skidanov return; /* Presumably process exited. */ 957930c5ff4SAlexey Skidanov 958930c5ff4SAlexey Skidanov mutex_lock(&p->event_mutex); 959930c5ff4SAlexey Skidanov 960930c5ff4SAlexey Skidanov /* Lookup events by type and signal them */ 961930c5ff4SAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_HW_EXCEPTION, NULL); 962930c5ff4SAlexey Skidanov 963930c5ff4SAlexey Skidanov mutex_unlock(&p->event_mutex); 964930c5ff4SAlexey Skidanov mutex_unlock(&p->mutex); 965930c5ff4SAlexey Skidanov } 966