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> 263f07c014SIngo Molnar #include <linux/sched/signal.h> 279b56bb11SFelix Kuehling #include <linux/sched/mm.h> 28f3a39818SAndrew Lewycky #include <linux/uaccess.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 11379775b62SKent Russell 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 15879775b62SKent Russell pr_debug("Allocated new event signal page at %p, for process %p\n", 159f3a39818SAndrew Lewycky page, p); 16079775b62SKent Russell 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); 180991ca8eeSEdward O'Callaghan if (!ret) { 181f3a39818SAndrew Lewycky ret = allocate_signal_page(devkfd, p); 182991ca8eeSEdward O'Callaghan if (ret) 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 1978eabaf54SKent Russell * and reused until it exits. 1988eabaf54SKent Russell */ 199f3a39818SAndrew Lewycky } 200f3a39818SAndrew Lewycky 201f3a39818SAndrew Lewycky static struct signal_page *lookup_signal_page_by_index(struct kfd_process *p, 202f3a39818SAndrew Lewycky unsigned int page_index) 203f3a39818SAndrew Lewycky { 204f3a39818SAndrew Lewycky struct signal_page *page; 205f3a39818SAndrew Lewycky 206f3a39818SAndrew Lewycky /* 207f3a39818SAndrew Lewycky * This is safe because we don't delete signal pages until the 208f3a39818SAndrew Lewycky * process exits. 209f3a39818SAndrew Lewycky */ 210f3a39818SAndrew Lewycky list_for_each_entry(page, &p->signal_event_pages, event_pages) 211f3a39818SAndrew Lewycky if (page->page_index == page_index) 212f3a39818SAndrew Lewycky return page; 213f3a39818SAndrew Lewycky 214f3a39818SAndrew Lewycky return NULL; 215f3a39818SAndrew Lewycky } 216f3a39818SAndrew Lewycky 217f3a39818SAndrew Lewycky /* 218f3a39818SAndrew Lewycky * Assumes that p->event_mutex is held and of course that p is not going 219f3a39818SAndrew Lewycky * away (current or locked). 220f3a39818SAndrew Lewycky */ 221f3a39818SAndrew Lewycky static struct kfd_event *lookup_event_by_id(struct kfd_process *p, uint32_t id) 222f3a39818SAndrew Lewycky { 223f3a39818SAndrew Lewycky struct kfd_event *ev; 224f3a39818SAndrew Lewycky 225f3a39818SAndrew Lewycky hash_for_each_possible(p->events, ev, events, id) 226f3a39818SAndrew Lewycky if (ev->event_id == id) 227f3a39818SAndrew Lewycky return ev; 228f3a39818SAndrew Lewycky 229f3a39818SAndrew Lewycky return NULL; 230f3a39818SAndrew Lewycky } 231f3a39818SAndrew Lewycky 232f3a39818SAndrew Lewycky static u32 make_signal_event_id(struct signal_page *page, 233f3a39818SAndrew Lewycky unsigned int signal_slot_index) 234f3a39818SAndrew Lewycky { 235f3a39818SAndrew Lewycky return page->page_index | 236f3a39818SAndrew Lewycky (signal_slot_index << SIGNAL_EVENT_ID_SLOT_SHIFT); 237f3a39818SAndrew Lewycky } 238f3a39818SAndrew Lewycky 239f3a39818SAndrew Lewycky /* 240f3a39818SAndrew Lewycky * Produce a kfd event id for a nonsignal event. 241f3a39818SAndrew Lewycky * These are arbitrary numbers, so we do a sequential search through 242f3a39818SAndrew Lewycky * the hash table for an unused number. 243f3a39818SAndrew Lewycky */ 244f3a39818SAndrew Lewycky static u32 make_nonsignal_event_id(struct kfd_process *p) 245f3a39818SAndrew Lewycky { 246f3a39818SAndrew Lewycky u32 id; 247f3a39818SAndrew Lewycky 248f3a39818SAndrew Lewycky for (id = p->next_nonsignal_event_id; 249f3a39818SAndrew Lewycky id < KFD_LAST_NONSIGNAL_EVENT_ID && 2504eacc26bSKent Russell lookup_event_by_id(p, id); 251f3a39818SAndrew Lewycky id++) 252f3a39818SAndrew Lewycky ; 253f3a39818SAndrew Lewycky 254f3a39818SAndrew Lewycky if (id < KFD_LAST_NONSIGNAL_EVENT_ID) { 255f3a39818SAndrew Lewycky 256f3a39818SAndrew Lewycky /* 257f3a39818SAndrew Lewycky * What if id == LAST_NONSIGNAL_EVENT_ID - 1? 258f3a39818SAndrew Lewycky * Then next_nonsignal_event_id = LAST_NONSIGNAL_EVENT_ID so 259f3a39818SAndrew Lewycky * the first loop fails immediately and we proceed with the 260f3a39818SAndrew Lewycky * wraparound loop below. 261f3a39818SAndrew Lewycky */ 262f3a39818SAndrew Lewycky p->next_nonsignal_event_id = id + 1; 263f3a39818SAndrew Lewycky 264f3a39818SAndrew Lewycky return id; 265f3a39818SAndrew Lewycky } 266f3a39818SAndrew Lewycky 267f3a39818SAndrew Lewycky for (id = KFD_FIRST_NONSIGNAL_EVENT_ID; 268f3a39818SAndrew Lewycky id < KFD_LAST_NONSIGNAL_EVENT_ID && 2694eacc26bSKent Russell lookup_event_by_id(p, id); 270f3a39818SAndrew Lewycky id++) 271f3a39818SAndrew Lewycky ; 272f3a39818SAndrew Lewycky 273f3a39818SAndrew Lewycky 274f3a39818SAndrew Lewycky if (id < KFD_LAST_NONSIGNAL_EVENT_ID) { 275f3a39818SAndrew Lewycky p->next_nonsignal_event_id = id + 1; 276f3a39818SAndrew Lewycky return id; 277f3a39818SAndrew Lewycky } 278f3a39818SAndrew Lewycky 279f3a39818SAndrew Lewycky p->next_nonsignal_event_id = KFD_FIRST_NONSIGNAL_EVENT_ID; 280f3a39818SAndrew Lewycky return 0; 281f3a39818SAndrew Lewycky } 282f3a39818SAndrew Lewycky 283f3a39818SAndrew Lewycky static struct kfd_event *lookup_event_by_page_slot(struct kfd_process *p, 284f3a39818SAndrew Lewycky struct signal_page *page, 285f3a39818SAndrew Lewycky unsigned int signal_slot) 286f3a39818SAndrew Lewycky { 287f3a39818SAndrew Lewycky return lookup_event_by_id(p, make_signal_event_id(page, signal_slot)); 288f3a39818SAndrew Lewycky } 289f3a39818SAndrew Lewycky 290f3a39818SAndrew Lewycky static int create_signal_event(struct file *devkfd, 291f3a39818SAndrew Lewycky struct kfd_process *p, 292f3a39818SAndrew Lewycky struct kfd_event *ev) 293f3a39818SAndrew Lewycky { 294f3a39818SAndrew Lewycky if (p->signal_event_count == KFD_SIGNAL_EVENT_LIMIT) { 295c986169fSFelix Kuehling if (!p->signal_event_limit_reached) { 29679775b62SKent Russell pr_warn("Signal event wasn't created because limit was reached\n"); 297c986169fSFelix Kuehling p->signal_event_limit_reached = true; 298c986169fSFelix Kuehling } 299f3a39818SAndrew Lewycky return -ENOMEM; 300f3a39818SAndrew Lewycky } 301f3a39818SAndrew Lewycky 302f3a39818SAndrew Lewycky if (!allocate_event_notification_slot(devkfd, p, &ev->signal_page, 303f3a39818SAndrew Lewycky &ev->signal_slot_index)) { 30479775b62SKent Russell pr_warn("Signal event wasn't created because out of kernel memory\n"); 305f3a39818SAndrew Lewycky return -ENOMEM; 306f3a39818SAndrew Lewycky } 307f3a39818SAndrew Lewycky 308f3a39818SAndrew Lewycky p->signal_event_count++; 309f3a39818SAndrew Lewycky 310f3a39818SAndrew Lewycky ev->user_signal_address = 311f3a39818SAndrew Lewycky &ev->signal_page->user_address[ev->signal_slot_index]; 312f3a39818SAndrew Lewycky 313f3a39818SAndrew Lewycky ev->event_id = make_signal_event_id(ev->signal_page, 314f3a39818SAndrew Lewycky ev->signal_slot_index); 315f3a39818SAndrew Lewycky 31679775b62SKent Russell pr_debug("Signal event number %zu created with id %d, address %p\n", 3176235e15eSOded Gabbay p->signal_event_count, ev->event_id, 3186235e15eSOded Gabbay ev->user_signal_address); 3196235e15eSOded Gabbay 320f3a39818SAndrew Lewycky return 0; 321f3a39818SAndrew Lewycky } 322f3a39818SAndrew Lewycky 323f3a39818SAndrew Lewycky /* 324f3a39818SAndrew Lewycky * No non-signal events are supported yet. 325f3a39818SAndrew Lewycky * We create them as events that never signal. 326f3a39818SAndrew Lewycky * Set event calls from user-mode are failed. 327f3a39818SAndrew Lewycky */ 328f3a39818SAndrew Lewycky static int create_other_event(struct kfd_process *p, struct kfd_event *ev) 329f3a39818SAndrew Lewycky { 330f3a39818SAndrew Lewycky ev->event_id = make_nonsignal_event_id(p); 331f3a39818SAndrew Lewycky if (ev->event_id == 0) 332f3a39818SAndrew Lewycky return -ENOMEM; 333f3a39818SAndrew Lewycky 334f3a39818SAndrew Lewycky return 0; 335f3a39818SAndrew Lewycky } 336f3a39818SAndrew Lewycky 337f3a39818SAndrew Lewycky void kfd_event_init_process(struct kfd_process *p) 338f3a39818SAndrew Lewycky { 339f3a39818SAndrew Lewycky mutex_init(&p->event_mutex); 340f3a39818SAndrew Lewycky hash_init(p->events); 341f3a39818SAndrew Lewycky INIT_LIST_HEAD(&p->signal_event_pages); 342f3a39818SAndrew Lewycky p->next_nonsignal_event_id = KFD_FIRST_NONSIGNAL_EVENT_ID; 343f3a39818SAndrew Lewycky p->signal_event_count = 0; 344f3a39818SAndrew Lewycky } 345f3a39818SAndrew Lewycky 346f3a39818SAndrew Lewycky static void destroy_event(struct kfd_process *p, struct kfd_event *ev) 347f3a39818SAndrew Lewycky { 3484eacc26bSKent Russell if (ev->signal_page) { 349f3a39818SAndrew Lewycky release_event_notification_slot(ev->signal_page, 350f3a39818SAndrew Lewycky ev->signal_slot_index); 351f3a39818SAndrew Lewycky p->signal_event_count--; 352f3a39818SAndrew Lewycky } 353f3a39818SAndrew Lewycky 354f3a39818SAndrew Lewycky /* 355f3a39818SAndrew Lewycky * Abandon the list of waiters. Individual waiting threads will 356f3a39818SAndrew Lewycky * clean up their own data. 357f3a39818SAndrew Lewycky */ 358f3a39818SAndrew Lewycky list_del(&ev->waiters); 359f3a39818SAndrew Lewycky 360f3a39818SAndrew Lewycky hash_del(&ev->events); 361f3a39818SAndrew Lewycky kfree(ev); 362f3a39818SAndrew Lewycky } 363f3a39818SAndrew Lewycky 364f3a39818SAndrew Lewycky static void destroy_events(struct kfd_process *p) 365f3a39818SAndrew Lewycky { 366f3a39818SAndrew Lewycky struct kfd_event *ev; 367f3a39818SAndrew Lewycky struct hlist_node *tmp; 368f3a39818SAndrew Lewycky unsigned int hash_bkt; 369f3a39818SAndrew Lewycky 370f3a39818SAndrew Lewycky hash_for_each_safe(p->events, hash_bkt, tmp, ev, events) 371f3a39818SAndrew Lewycky destroy_event(p, ev); 372f3a39818SAndrew Lewycky } 373f3a39818SAndrew Lewycky 374f3a39818SAndrew Lewycky /* 375f3a39818SAndrew Lewycky * We assume that the process is being destroyed and there is no need to 376f3a39818SAndrew Lewycky * unmap the pages or keep bookkeeping data in order. 377f3a39818SAndrew Lewycky */ 378f3a39818SAndrew Lewycky static void shutdown_signal_pages(struct kfd_process *p) 379f3a39818SAndrew Lewycky { 380f3a39818SAndrew Lewycky struct signal_page *page, *tmp; 381f3a39818SAndrew Lewycky 382f3a39818SAndrew Lewycky list_for_each_entry_safe(page, tmp, &p->signal_event_pages, 383f3a39818SAndrew Lewycky event_pages) { 384f3a39818SAndrew Lewycky free_pages((unsigned long)page->kernel_address, 385f3a39818SAndrew Lewycky get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); 386f3a39818SAndrew Lewycky kfree(page); 387f3a39818SAndrew Lewycky } 388f3a39818SAndrew Lewycky } 389f3a39818SAndrew Lewycky 390f3a39818SAndrew Lewycky void kfd_event_free_process(struct kfd_process *p) 391f3a39818SAndrew Lewycky { 392f3a39818SAndrew Lewycky destroy_events(p); 393f3a39818SAndrew Lewycky shutdown_signal_pages(p); 394f3a39818SAndrew Lewycky } 395f3a39818SAndrew Lewycky 396f3a39818SAndrew Lewycky static bool event_can_be_gpu_signaled(const struct kfd_event *ev) 397f3a39818SAndrew Lewycky { 398f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL || 399f3a39818SAndrew Lewycky ev->type == KFD_EVENT_TYPE_DEBUG; 400f3a39818SAndrew Lewycky } 401f3a39818SAndrew Lewycky 402f3a39818SAndrew Lewycky static bool event_can_be_cpu_signaled(const struct kfd_event *ev) 403f3a39818SAndrew Lewycky { 404f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL; 405f3a39818SAndrew Lewycky } 406f3a39818SAndrew Lewycky 407f3a39818SAndrew Lewycky int kfd_event_create(struct file *devkfd, struct kfd_process *p, 408f3a39818SAndrew Lewycky uint32_t event_type, bool auto_reset, uint32_t node_id, 409f3a39818SAndrew Lewycky uint32_t *event_id, uint32_t *event_trigger_data, 410f3a39818SAndrew Lewycky uint64_t *event_page_offset, uint32_t *event_slot_index) 411f3a39818SAndrew Lewycky { 412f3a39818SAndrew Lewycky int ret = 0; 413f3a39818SAndrew Lewycky struct kfd_event *ev = kzalloc(sizeof(*ev), GFP_KERNEL); 414f3a39818SAndrew Lewycky 415f3a39818SAndrew Lewycky if (!ev) 416f3a39818SAndrew Lewycky return -ENOMEM; 417f3a39818SAndrew Lewycky 418f3a39818SAndrew Lewycky ev->type = event_type; 419f3a39818SAndrew Lewycky ev->auto_reset = auto_reset; 420f3a39818SAndrew Lewycky ev->signaled = false; 421f3a39818SAndrew Lewycky 422f3a39818SAndrew Lewycky INIT_LIST_HEAD(&ev->waiters); 423f3a39818SAndrew Lewycky 424f3a39818SAndrew Lewycky *event_page_offset = 0; 425f3a39818SAndrew Lewycky 426f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 427f3a39818SAndrew Lewycky 428f3a39818SAndrew Lewycky switch (event_type) { 429f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_SIGNAL: 430f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_DEBUG: 431f3a39818SAndrew Lewycky ret = create_signal_event(devkfd, p, ev); 432f3a39818SAndrew Lewycky if (!ret) { 433f3a39818SAndrew Lewycky *event_page_offset = (ev->signal_page->page_index | 434f3a39818SAndrew Lewycky KFD_MMAP_EVENTS_MASK); 435f3a39818SAndrew Lewycky *event_page_offset <<= PAGE_SHIFT; 436f3a39818SAndrew Lewycky *event_slot_index = ev->signal_slot_index; 437f3a39818SAndrew Lewycky } 438f3a39818SAndrew Lewycky break; 439f3a39818SAndrew Lewycky default: 440f3a39818SAndrew Lewycky ret = create_other_event(p, ev); 441f3a39818SAndrew Lewycky break; 442f3a39818SAndrew Lewycky } 443f3a39818SAndrew Lewycky 444f3a39818SAndrew Lewycky if (!ret) { 445f3a39818SAndrew Lewycky hash_add(p->events, &ev->events, ev->event_id); 446f3a39818SAndrew Lewycky 447f3a39818SAndrew Lewycky *event_id = ev->event_id; 448f3a39818SAndrew Lewycky *event_trigger_data = ev->event_id; 449f3a39818SAndrew Lewycky } else { 450f3a39818SAndrew Lewycky kfree(ev); 451f3a39818SAndrew Lewycky } 452f3a39818SAndrew Lewycky 453f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 454f3a39818SAndrew Lewycky 455f3a39818SAndrew Lewycky return ret; 456f3a39818SAndrew Lewycky } 457f3a39818SAndrew Lewycky 458f3a39818SAndrew Lewycky /* Assumes that p is current. */ 459f3a39818SAndrew Lewycky int kfd_event_destroy(struct kfd_process *p, uint32_t event_id) 460f3a39818SAndrew Lewycky { 461f3a39818SAndrew Lewycky struct kfd_event *ev; 462f3a39818SAndrew Lewycky int ret = 0; 463f3a39818SAndrew Lewycky 464f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 465f3a39818SAndrew Lewycky 466f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 467f3a39818SAndrew Lewycky 468f3a39818SAndrew Lewycky if (ev) 469f3a39818SAndrew Lewycky destroy_event(p, ev); 470f3a39818SAndrew Lewycky else 471f3a39818SAndrew Lewycky ret = -EINVAL; 472f3a39818SAndrew Lewycky 473f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 474f3a39818SAndrew Lewycky return ret; 475f3a39818SAndrew Lewycky } 476f3a39818SAndrew Lewycky 477f3a39818SAndrew Lewycky static void set_event(struct kfd_event *ev) 478f3a39818SAndrew Lewycky { 479f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter; 480f3a39818SAndrew Lewycky struct kfd_event_waiter *next; 481f3a39818SAndrew Lewycky 482f3a39818SAndrew Lewycky /* Auto reset if the list is non-empty and we're waking someone. */ 483f3a39818SAndrew Lewycky ev->signaled = !ev->auto_reset || list_empty(&ev->waiters); 484f3a39818SAndrew Lewycky 485f3a39818SAndrew Lewycky list_for_each_entry_safe(waiter, next, &ev->waiters, waiters) { 486f3a39818SAndrew Lewycky waiter->activated = true; 487f3a39818SAndrew Lewycky 488f3a39818SAndrew Lewycky /* _init because free_waiters will call list_del */ 489f3a39818SAndrew Lewycky list_del_init(&waiter->waiters); 490f3a39818SAndrew Lewycky 491f3a39818SAndrew Lewycky wake_up_process(waiter->sleeping_task); 492f3a39818SAndrew Lewycky } 493f3a39818SAndrew Lewycky } 494f3a39818SAndrew Lewycky 495f3a39818SAndrew Lewycky /* Assumes that p is current. */ 496f3a39818SAndrew Lewycky int kfd_set_event(struct kfd_process *p, uint32_t event_id) 497f3a39818SAndrew Lewycky { 498f3a39818SAndrew Lewycky int ret = 0; 499f3a39818SAndrew Lewycky struct kfd_event *ev; 500f3a39818SAndrew Lewycky 501f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 502f3a39818SAndrew Lewycky 503f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 504f3a39818SAndrew Lewycky 505f3a39818SAndrew Lewycky if (ev && event_can_be_cpu_signaled(ev)) 506f3a39818SAndrew Lewycky set_event(ev); 507f3a39818SAndrew Lewycky else 508f3a39818SAndrew Lewycky ret = -EINVAL; 509f3a39818SAndrew Lewycky 510f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 511f3a39818SAndrew Lewycky return ret; 512f3a39818SAndrew Lewycky } 513f3a39818SAndrew Lewycky 514f3a39818SAndrew Lewycky static void reset_event(struct kfd_event *ev) 515f3a39818SAndrew Lewycky { 516f3a39818SAndrew Lewycky ev->signaled = false; 517f3a39818SAndrew Lewycky } 518f3a39818SAndrew Lewycky 519f3a39818SAndrew Lewycky /* Assumes that p is current. */ 520f3a39818SAndrew Lewycky int kfd_reset_event(struct kfd_process *p, uint32_t event_id) 521f3a39818SAndrew Lewycky { 522f3a39818SAndrew Lewycky int ret = 0; 523f3a39818SAndrew Lewycky struct kfd_event *ev; 524f3a39818SAndrew Lewycky 525f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 526f3a39818SAndrew Lewycky 527f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 528f3a39818SAndrew Lewycky 529f3a39818SAndrew Lewycky if (ev && event_can_be_cpu_signaled(ev)) 530f3a39818SAndrew Lewycky reset_event(ev); 531f3a39818SAndrew Lewycky else 532f3a39818SAndrew Lewycky ret = -EINVAL; 533f3a39818SAndrew Lewycky 534f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 535f3a39818SAndrew Lewycky return ret; 536f3a39818SAndrew Lewycky 537f3a39818SAndrew Lewycky } 538f3a39818SAndrew Lewycky 539f3a39818SAndrew Lewycky static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev) 540f3a39818SAndrew Lewycky { 541f3a39818SAndrew Lewycky page_slots(ev->signal_page)[ev->signal_slot_index] = 542f3a39818SAndrew Lewycky UNSIGNALED_EVENT_SLOT; 543f3a39818SAndrew Lewycky } 544f3a39818SAndrew Lewycky 545f3a39818SAndrew Lewycky static bool is_slot_signaled(struct signal_page *page, unsigned int index) 546f3a39818SAndrew Lewycky { 547f3a39818SAndrew Lewycky return page_slots(page)[index] != UNSIGNALED_EVENT_SLOT; 548f3a39818SAndrew Lewycky } 549f3a39818SAndrew Lewycky 550f3a39818SAndrew Lewycky static void set_event_from_interrupt(struct kfd_process *p, 551f3a39818SAndrew Lewycky struct kfd_event *ev) 552f3a39818SAndrew Lewycky { 553f3a39818SAndrew Lewycky if (ev && event_can_be_gpu_signaled(ev)) { 554f3a39818SAndrew Lewycky acknowledge_signal(p, ev); 555f3a39818SAndrew Lewycky set_event(ev); 556f3a39818SAndrew Lewycky } 557f3a39818SAndrew Lewycky } 558f3a39818SAndrew Lewycky 559f3a39818SAndrew Lewycky void kfd_signal_event_interrupt(unsigned int pasid, uint32_t partial_id, 560f3a39818SAndrew Lewycky uint32_t valid_id_bits) 561f3a39818SAndrew Lewycky { 562f3a39818SAndrew Lewycky struct kfd_event *ev; 563f3a39818SAndrew Lewycky 564f3a39818SAndrew Lewycky /* 565f3a39818SAndrew Lewycky * Because we are called from arbitrary context (workqueue) as opposed 566f3a39818SAndrew Lewycky * to process context, kfd_process could attempt to exit while we are 567f3a39818SAndrew Lewycky * running so the lookup function returns a locked process. 568f3a39818SAndrew Lewycky */ 569f3a39818SAndrew Lewycky struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 570f3a39818SAndrew Lewycky 571f3a39818SAndrew Lewycky if (!p) 572f3a39818SAndrew Lewycky return; /* Presumably process exited. */ 573f3a39818SAndrew Lewycky 574f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 575f3a39818SAndrew Lewycky 576f3a39818SAndrew Lewycky if (valid_id_bits >= INTERRUPT_DATA_BITS) { 577f3a39818SAndrew Lewycky /* Partial ID is a full ID. */ 578f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, partial_id); 579f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 580f3a39818SAndrew Lewycky } else { 581f3a39818SAndrew Lewycky /* 582f3a39818SAndrew Lewycky * Partial ID is in fact partial. For now we completely 583f3a39818SAndrew Lewycky * ignore it, but we could use any bits we did receive to 584f3a39818SAndrew Lewycky * search faster. 585f3a39818SAndrew Lewycky */ 586f3a39818SAndrew Lewycky struct signal_page *page; 5878eabaf54SKent Russell unsigned int i; 588f3a39818SAndrew Lewycky 589f3a39818SAndrew Lewycky list_for_each_entry(page, &p->signal_event_pages, event_pages) 590f3a39818SAndrew Lewycky for (i = 0; i < SLOTS_PER_PAGE; i++) 591f3a39818SAndrew Lewycky if (is_slot_signaled(page, i)) { 592f3a39818SAndrew Lewycky ev = lookup_event_by_page_slot(p, 593f3a39818SAndrew Lewycky page, i); 594f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 595f3a39818SAndrew Lewycky } 596f3a39818SAndrew Lewycky } 597f3a39818SAndrew Lewycky 598f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 599f3a39818SAndrew Lewycky mutex_unlock(&p->mutex); 600f3a39818SAndrew Lewycky } 601f3a39818SAndrew Lewycky 602f3a39818SAndrew Lewycky static struct kfd_event_waiter *alloc_event_waiters(uint32_t num_events) 603f3a39818SAndrew Lewycky { 604f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters; 605f3a39818SAndrew Lewycky uint32_t i; 606f3a39818SAndrew Lewycky 607f3a39818SAndrew Lewycky event_waiters = kmalloc_array(num_events, 608f3a39818SAndrew Lewycky sizeof(struct kfd_event_waiter), 609f3a39818SAndrew Lewycky GFP_KERNEL); 610f3a39818SAndrew Lewycky 611f3a39818SAndrew Lewycky for (i = 0; (event_waiters) && (i < num_events) ; i++) { 612f3a39818SAndrew Lewycky INIT_LIST_HEAD(&event_waiters[i].waiters); 613f3a39818SAndrew Lewycky event_waiters[i].sleeping_task = current; 614f3a39818SAndrew Lewycky event_waiters[i].activated = false; 615f3a39818SAndrew Lewycky } 616f3a39818SAndrew Lewycky 617f3a39818SAndrew Lewycky return event_waiters; 618f3a39818SAndrew Lewycky } 619f3a39818SAndrew Lewycky 6201f9d09beSSean Keely static int init_event_waiter_get_status(struct kfd_process *p, 621f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter, 62259d3e8beSAlexey Skidanov uint32_t event_id, 62359d3e8beSAlexey Skidanov uint32_t input_index) 624f3a39818SAndrew Lewycky { 625f3a39818SAndrew Lewycky struct kfd_event *ev = lookup_event_by_id(p, event_id); 626f3a39818SAndrew Lewycky 627f3a39818SAndrew Lewycky if (!ev) 628f3a39818SAndrew Lewycky return -EINVAL; 629f3a39818SAndrew Lewycky 63059d3e8beSAlexey Skidanov waiter->event = ev; 63159d3e8beSAlexey Skidanov waiter->input_index = input_index; 632f3a39818SAndrew Lewycky waiter->activated = ev->signaled; 633f3a39818SAndrew Lewycky ev->signaled = ev->signaled && !ev->auto_reset; 634f3a39818SAndrew Lewycky 635f3a39818SAndrew Lewycky return 0; 636f3a39818SAndrew Lewycky } 637f3a39818SAndrew Lewycky 6381f9d09beSSean Keely static void init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter) 6391f9d09beSSean Keely { 6401f9d09beSSean Keely struct kfd_event *ev = waiter->event; 6411f9d09beSSean Keely 6421f9d09beSSean Keely /* Only add to the wait list if we actually need to 6431f9d09beSSean Keely * wait on this event. 6441f9d09beSSean Keely */ 6451f9d09beSSean Keely if (!waiter->activated) 6461f9d09beSSean Keely list_add(&waiter->waiters, &ev->waiters); 6471f9d09beSSean Keely } 6481f9d09beSSean Keely 649f3a39818SAndrew Lewycky static bool test_event_condition(bool all, uint32_t num_events, 650f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters) 651f3a39818SAndrew Lewycky { 652f3a39818SAndrew Lewycky uint32_t i; 653f3a39818SAndrew Lewycky uint32_t activated_count = 0; 654f3a39818SAndrew Lewycky 655f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 656f3a39818SAndrew Lewycky if (event_waiters[i].activated) { 657f3a39818SAndrew Lewycky if (!all) 658f3a39818SAndrew Lewycky return true; 659f3a39818SAndrew Lewycky 660f3a39818SAndrew Lewycky activated_count++; 661f3a39818SAndrew Lewycky } 662f3a39818SAndrew Lewycky } 663f3a39818SAndrew Lewycky 664f3a39818SAndrew Lewycky return activated_count == num_events; 665f3a39818SAndrew Lewycky } 666f3a39818SAndrew Lewycky 66759d3e8beSAlexey Skidanov /* 66859d3e8beSAlexey Skidanov * Copy event specific data, if defined. 66959d3e8beSAlexey Skidanov * Currently only memory exception events have additional data to copy to user 67059d3e8beSAlexey Skidanov */ 67159d3e8beSAlexey Skidanov static bool copy_signaled_event_data(uint32_t num_events, 67259d3e8beSAlexey Skidanov struct kfd_event_waiter *event_waiters, 67359d3e8beSAlexey Skidanov struct kfd_event_data __user *data) 67459d3e8beSAlexey Skidanov { 67559d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *src; 67659d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data __user *dst; 67759d3e8beSAlexey Skidanov struct kfd_event_waiter *waiter; 67859d3e8beSAlexey Skidanov struct kfd_event *event; 67959d3e8beSAlexey Skidanov uint32_t i; 68059d3e8beSAlexey Skidanov 68159d3e8beSAlexey Skidanov for (i = 0; i < num_events; i++) { 68259d3e8beSAlexey Skidanov waiter = &event_waiters[i]; 68359d3e8beSAlexey Skidanov event = waiter->event; 68459d3e8beSAlexey Skidanov if (waiter->activated && event->type == KFD_EVENT_TYPE_MEMORY) { 68559d3e8beSAlexey Skidanov dst = &data[waiter->input_index].memory_exception_data; 68659d3e8beSAlexey Skidanov src = &event->memory_exception_data; 68759d3e8beSAlexey Skidanov if (copy_to_user(dst, src, 68859d3e8beSAlexey Skidanov sizeof(struct kfd_hsa_memory_exception_data))) 68959d3e8beSAlexey Skidanov return false; 69059d3e8beSAlexey Skidanov } 69159d3e8beSAlexey Skidanov } 69259d3e8beSAlexey Skidanov 69359d3e8beSAlexey Skidanov return true; 69459d3e8beSAlexey Skidanov 69559d3e8beSAlexey Skidanov } 69659d3e8beSAlexey Skidanov 69759d3e8beSAlexey Skidanov 69859d3e8beSAlexey Skidanov 699f3a39818SAndrew Lewycky static long user_timeout_to_jiffies(uint32_t user_timeout_ms) 700f3a39818SAndrew Lewycky { 701f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE) 702f3a39818SAndrew Lewycky return 0; 703f3a39818SAndrew Lewycky 704f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_INFINITE) 705f3a39818SAndrew Lewycky return MAX_SCHEDULE_TIMEOUT; 706f3a39818SAndrew Lewycky 707f3a39818SAndrew Lewycky /* 708f3a39818SAndrew Lewycky * msecs_to_jiffies interprets all values above 2^31-1 as infinite, 709f3a39818SAndrew Lewycky * but we consider them finite. 710f3a39818SAndrew Lewycky * This hack is wrong, but nobody is likely to notice. 711f3a39818SAndrew Lewycky */ 712f3a39818SAndrew Lewycky user_timeout_ms = min_t(uint32_t, user_timeout_ms, 0x7FFFFFFF); 713f3a39818SAndrew Lewycky 714f3a39818SAndrew Lewycky return msecs_to_jiffies(user_timeout_ms) + 1; 715f3a39818SAndrew Lewycky } 716f3a39818SAndrew Lewycky 717f3a39818SAndrew Lewycky static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters) 718f3a39818SAndrew Lewycky { 719f3a39818SAndrew Lewycky uint32_t i; 720f3a39818SAndrew Lewycky 721f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) 722f3a39818SAndrew Lewycky list_del(&waiters[i].waiters); 723f3a39818SAndrew Lewycky 724f3a39818SAndrew Lewycky kfree(waiters); 725f3a39818SAndrew Lewycky } 726f3a39818SAndrew Lewycky 727f3a39818SAndrew Lewycky int kfd_wait_on_events(struct kfd_process *p, 72859d3e8beSAlexey Skidanov uint32_t num_events, void __user *data, 729f3a39818SAndrew Lewycky bool all, uint32_t user_timeout_ms, 730f3a39818SAndrew Lewycky enum kfd_event_wait_result *wait_result) 731f3a39818SAndrew Lewycky { 73259d3e8beSAlexey Skidanov struct kfd_event_data __user *events = 73359d3e8beSAlexey Skidanov (struct kfd_event_data __user *) data; 734f3a39818SAndrew Lewycky uint32_t i; 735f3a39818SAndrew Lewycky int ret = 0; 7361f9d09beSSean Keely 737f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters = NULL; 738f3a39818SAndrew Lewycky long timeout = user_timeout_to_jiffies(user_timeout_ms); 739f3a39818SAndrew Lewycky 740f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 741f3a39818SAndrew Lewycky 7421f9d09beSSean Keely /* Set to something unreasonable - this is really 7431f9d09beSSean Keely * just a bool for now. 7441f9d09beSSean Keely */ 7451f9d09beSSean Keely *wait_result = KFD_WAIT_TIMEOUT; 7461f9d09beSSean Keely 747f3a39818SAndrew Lewycky event_waiters = alloc_event_waiters(num_events); 748f3a39818SAndrew Lewycky if (!event_waiters) { 749f3a39818SAndrew Lewycky ret = -ENOMEM; 750f3a39818SAndrew Lewycky goto fail; 751f3a39818SAndrew Lewycky } 752f3a39818SAndrew Lewycky 753f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 75459d3e8beSAlexey Skidanov struct kfd_event_data event_data; 755f3a39818SAndrew Lewycky 75659d3e8beSAlexey Skidanov if (copy_from_user(&event_data, &events[i], 7578bf79388SPan Bian sizeof(struct kfd_event_data))) { 7588bf79388SPan Bian ret = -EFAULT; 759f3a39818SAndrew Lewycky goto fail; 7608bf79388SPan Bian } 761f3a39818SAndrew Lewycky 7621f9d09beSSean Keely ret = init_event_waiter_get_status(p, &event_waiters[i], 76359d3e8beSAlexey Skidanov event_data.event_id, i); 764f3a39818SAndrew Lewycky if (ret) 765f3a39818SAndrew Lewycky goto fail; 766f3a39818SAndrew Lewycky } 767f3a39818SAndrew Lewycky 7681f9d09beSSean Keely /* Check condition once. */ 7691f9d09beSSean Keely if (test_event_condition(all, num_events, event_waiters)) { 7701f9d09beSSean Keely if (copy_signaled_event_data(num_events, 7711f9d09beSSean Keely event_waiters, events)) 7721f9d09beSSean Keely *wait_result = KFD_WAIT_COMPLETE; 7731f9d09beSSean Keely else 7741f9d09beSSean Keely *wait_result = KFD_WAIT_ERROR; 7751f9d09beSSean Keely free_waiters(num_events, event_waiters); 7761f9d09beSSean Keely } else { 7771f9d09beSSean Keely /* Add to wait lists if we need to wait. */ 7781f9d09beSSean Keely for (i = 0; i < num_events; i++) 7791f9d09beSSean Keely init_event_waiter_add_to_waitlist(&event_waiters[i]); 7801f9d09beSSean Keely } 7811f9d09beSSean Keely 782f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 783f3a39818SAndrew Lewycky 7841f9d09beSSean Keely /* Return if all waits were already satisfied. */ 7851f9d09beSSean Keely if (*wait_result != KFD_WAIT_TIMEOUT) { 7861f9d09beSSean Keely __set_current_state(TASK_RUNNING); 7871f9d09beSSean Keely return ret; 7881f9d09beSSean Keely } 7891f9d09beSSean Keely 790f3a39818SAndrew Lewycky while (true) { 791f3a39818SAndrew Lewycky if (fatal_signal_pending(current)) { 792f3a39818SAndrew Lewycky ret = -EINTR; 793f3a39818SAndrew Lewycky break; 794f3a39818SAndrew Lewycky } 795f3a39818SAndrew Lewycky 796f3a39818SAndrew Lewycky if (signal_pending(current)) { 797f3a39818SAndrew Lewycky /* 798f3a39818SAndrew Lewycky * This is wrong when a nonzero, non-infinite timeout 799f3a39818SAndrew Lewycky * is specified. We need to use 800f3a39818SAndrew Lewycky * ERESTARTSYS_RESTARTBLOCK, but struct restart_block 801f3a39818SAndrew Lewycky * contains a union with data for each user and it's 802f3a39818SAndrew Lewycky * in generic kernel code that I don't want to 803f3a39818SAndrew Lewycky * touch yet. 804f3a39818SAndrew Lewycky */ 805f3a39818SAndrew Lewycky ret = -ERESTARTSYS; 806f3a39818SAndrew Lewycky break; 807f3a39818SAndrew Lewycky } 808f3a39818SAndrew Lewycky 809*d9aeec4cSSean Keely /* Set task state to interruptible sleep before 810*d9aeec4cSSean Keely * checking wake-up conditions. A concurrent wake-up 811*d9aeec4cSSean Keely * will put the task back into runnable state. In that 812*d9aeec4cSSean Keely * case schedule_timeout will not put the task to 813*d9aeec4cSSean Keely * sleep and we'll get a chance to re-check the 814*d9aeec4cSSean Keely * updated conditions almost immediately. Otherwise, 815*d9aeec4cSSean Keely * this race condition would lead to a soft hang or a 816*d9aeec4cSSean Keely * very long sleep. 817*d9aeec4cSSean Keely */ 818*d9aeec4cSSean Keely set_current_state(TASK_INTERRUPTIBLE); 819*d9aeec4cSSean Keely 820f3a39818SAndrew Lewycky if (test_event_condition(all, num_events, event_waiters)) { 82159d3e8beSAlexey Skidanov if (copy_signaled_event_data(num_events, 82259d3e8beSAlexey Skidanov event_waiters, events)) 823f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_COMPLETE; 82459d3e8beSAlexey Skidanov else 82559d3e8beSAlexey Skidanov *wait_result = KFD_WAIT_ERROR; 826f3a39818SAndrew Lewycky break; 827f3a39818SAndrew Lewycky } 828f3a39818SAndrew Lewycky 829f3a39818SAndrew Lewycky if (timeout <= 0) { 830f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_TIMEOUT; 831f3a39818SAndrew Lewycky break; 832f3a39818SAndrew Lewycky } 833f3a39818SAndrew Lewycky 834*d9aeec4cSSean Keely timeout = schedule_timeout(timeout); 835f3a39818SAndrew Lewycky } 836f3a39818SAndrew Lewycky __set_current_state(TASK_RUNNING); 837f3a39818SAndrew Lewycky 838f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 839f3a39818SAndrew Lewycky free_waiters(num_events, event_waiters); 840f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 841f3a39818SAndrew Lewycky 842f3a39818SAndrew Lewycky return ret; 843f3a39818SAndrew Lewycky 844f3a39818SAndrew Lewycky fail: 845f3a39818SAndrew Lewycky if (event_waiters) 846f3a39818SAndrew Lewycky free_waiters(num_events, event_waiters); 847f3a39818SAndrew Lewycky 848f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 849f3a39818SAndrew Lewycky 850f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_ERROR; 851f3a39818SAndrew Lewycky 852f3a39818SAndrew Lewycky return ret; 853f3a39818SAndrew Lewycky } 854f3a39818SAndrew Lewycky 855f3a39818SAndrew Lewycky int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma) 856f3a39818SAndrew Lewycky { 857f3a39818SAndrew Lewycky 858f3a39818SAndrew Lewycky unsigned int page_index; 859f3a39818SAndrew Lewycky unsigned long pfn; 860f3a39818SAndrew Lewycky struct signal_page *page; 861f3a39818SAndrew Lewycky 862f3a39818SAndrew Lewycky /* check required size is logical */ 863f3a39818SAndrew Lewycky if (get_order(KFD_SIGNAL_EVENT_LIMIT * 8) != 864f3a39818SAndrew Lewycky get_order(vma->vm_end - vma->vm_start)) { 86579775b62SKent Russell pr_err("Event page mmap requested illegal size\n"); 866f3a39818SAndrew Lewycky return -EINVAL; 867f3a39818SAndrew Lewycky } 868f3a39818SAndrew Lewycky 869f3a39818SAndrew Lewycky page_index = vma->vm_pgoff; 870f3a39818SAndrew Lewycky 871f3a39818SAndrew Lewycky page = lookup_signal_page_by_index(p, page_index); 872f3a39818SAndrew Lewycky if (!page) { 873f3a39818SAndrew Lewycky /* Probably KFD bug, but mmap is user-accessible. */ 87479775b62SKent Russell pr_debug("Signal page could not be found for page_index %u\n", 875f3a39818SAndrew Lewycky page_index); 876f3a39818SAndrew Lewycky return -EINVAL; 877f3a39818SAndrew Lewycky } 878f3a39818SAndrew Lewycky 879f3a39818SAndrew Lewycky pfn = __pa(page->kernel_address); 880f3a39818SAndrew Lewycky pfn >>= PAGE_SHIFT; 881f3a39818SAndrew Lewycky 882f3a39818SAndrew Lewycky vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE 883f3a39818SAndrew Lewycky | VM_DONTDUMP | VM_PFNMAP; 884f3a39818SAndrew Lewycky 88579775b62SKent Russell pr_debug("Mapping signal page\n"); 886f3a39818SAndrew Lewycky pr_debug(" start user address == 0x%08lx\n", vma->vm_start); 887f3a39818SAndrew Lewycky pr_debug(" end user address == 0x%08lx\n", vma->vm_end); 888f3a39818SAndrew Lewycky pr_debug(" pfn == 0x%016lX\n", pfn); 889f3a39818SAndrew Lewycky pr_debug(" vm_flags == 0x%08lX\n", vma->vm_flags); 890f3a39818SAndrew Lewycky pr_debug(" size == 0x%08lX\n", 891f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start); 892f3a39818SAndrew Lewycky 893f3a39818SAndrew Lewycky page->user_address = (uint64_t __user *)vma->vm_start; 894f3a39818SAndrew Lewycky 895f3a39818SAndrew Lewycky /* mapping the page to user process */ 896f3a39818SAndrew Lewycky return remap_pfn_range(vma, vma->vm_start, pfn, 897f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start, vma->vm_page_prot); 898f3a39818SAndrew Lewycky } 89959d3e8beSAlexey Skidanov 90059d3e8beSAlexey Skidanov /* 90159d3e8beSAlexey Skidanov * Assumes that p->event_mutex is held and of course 90259d3e8beSAlexey Skidanov * that p is not going away (current or locked). 90359d3e8beSAlexey Skidanov */ 90459d3e8beSAlexey Skidanov static void lookup_events_by_type_and_signal(struct kfd_process *p, 90559d3e8beSAlexey Skidanov int type, void *event_data) 90659d3e8beSAlexey Skidanov { 90759d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *ev_data; 90859d3e8beSAlexey Skidanov struct kfd_event *ev; 90959d3e8beSAlexey Skidanov int bkt; 91059d3e8beSAlexey Skidanov bool send_signal = true; 91159d3e8beSAlexey Skidanov 91259d3e8beSAlexey Skidanov ev_data = (struct kfd_hsa_memory_exception_data *) event_data; 91359d3e8beSAlexey Skidanov 91459d3e8beSAlexey Skidanov hash_for_each(p->events, bkt, ev, events) 91559d3e8beSAlexey Skidanov if (ev->type == type) { 91659d3e8beSAlexey Skidanov send_signal = false; 91759d3e8beSAlexey Skidanov dev_dbg(kfd_device, 91859d3e8beSAlexey Skidanov "Event found: id %X type %d", 91959d3e8beSAlexey Skidanov ev->event_id, ev->type); 92059d3e8beSAlexey Skidanov set_event(ev); 92159d3e8beSAlexey Skidanov if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data) 92259d3e8beSAlexey Skidanov ev->memory_exception_data = *ev_data; 92359d3e8beSAlexey Skidanov } 92459d3e8beSAlexey Skidanov 92559d3e8beSAlexey Skidanov /* Send SIGTERM no event of type "type" has been found*/ 92659d3e8beSAlexey Skidanov if (send_signal) { 92781663016SOded Gabbay if (send_sigterm) { 92859d3e8beSAlexey Skidanov dev_warn(kfd_device, 92959d3e8beSAlexey Skidanov "Sending SIGTERM to HSA Process with PID %d ", 93059d3e8beSAlexey Skidanov p->lead_thread->pid); 93159d3e8beSAlexey Skidanov send_sig(SIGTERM, p->lead_thread, 0); 93281663016SOded Gabbay } else { 93381663016SOded Gabbay dev_err(kfd_device, 93481663016SOded Gabbay "HSA Process (PID %d) got unhandled exception", 93581663016SOded Gabbay p->lead_thread->pid); 93681663016SOded Gabbay } 93759d3e8beSAlexey Skidanov } 93859d3e8beSAlexey Skidanov } 93959d3e8beSAlexey Skidanov 94059d3e8beSAlexey Skidanov void kfd_signal_iommu_event(struct kfd_dev *dev, unsigned int pasid, 94159d3e8beSAlexey Skidanov unsigned long address, bool is_write_requested, 94259d3e8beSAlexey Skidanov bool is_execute_requested) 94359d3e8beSAlexey Skidanov { 94459d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data memory_exception_data; 94559d3e8beSAlexey Skidanov struct vm_area_struct *vma; 94659d3e8beSAlexey Skidanov 94759d3e8beSAlexey Skidanov /* 94859d3e8beSAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 94959d3e8beSAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 95059d3e8beSAlexey Skidanov * running so the lookup function returns a locked process. 95159d3e8beSAlexey Skidanov */ 95259d3e8beSAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 9539b56bb11SFelix Kuehling struct mm_struct *mm; 95459d3e8beSAlexey Skidanov 95559d3e8beSAlexey Skidanov if (!p) 95659d3e8beSAlexey Skidanov return; /* Presumably process exited. */ 95759d3e8beSAlexey Skidanov 9589b56bb11SFelix Kuehling /* Take a safe reference to the mm_struct, which may otherwise 9599b56bb11SFelix Kuehling * disappear even while the kfd_process is still referenced. 9609b56bb11SFelix Kuehling */ 9619b56bb11SFelix Kuehling mm = get_task_mm(p->lead_thread); 9629b56bb11SFelix Kuehling if (!mm) { 9639b56bb11SFelix Kuehling mutex_unlock(&p->mutex); 9649b56bb11SFelix Kuehling return; /* Process is exiting */ 9659b56bb11SFelix Kuehling } 9669b56bb11SFelix Kuehling 96759d3e8beSAlexey Skidanov memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 96859d3e8beSAlexey Skidanov 9699b56bb11SFelix Kuehling down_read(&mm->mmap_sem); 9709b56bb11SFelix Kuehling vma = find_vma(mm, address); 97159d3e8beSAlexey Skidanov 97259d3e8beSAlexey Skidanov memory_exception_data.gpu_id = dev->id; 97359d3e8beSAlexey Skidanov memory_exception_data.va = address; 97459d3e8beSAlexey Skidanov /* Set failure reason */ 97559d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 97659d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 97759d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 97859d3e8beSAlexey Skidanov if (vma) { 97959d3e8beSAlexey Skidanov if (vma->vm_start > address) { 98059d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 98159d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 98259d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 98359d3e8beSAlexey Skidanov } else { 98459d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 0; 98559d3e8beSAlexey Skidanov if (is_write_requested && !(vma->vm_flags & VM_WRITE)) 98659d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 1; 98759d3e8beSAlexey Skidanov else 98859d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 98959d3e8beSAlexey Skidanov if (is_execute_requested && !(vma->vm_flags & VM_EXEC)) 99059d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 1; 99159d3e8beSAlexey Skidanov else 99259d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 99359d3e8beSAlexey Skidanov } 99459d3e8beSAlexey Skidanov } 99559d3e8beSAlexey Skidanov 9969b56bb11SFelix Kuehling up_read(&mm->mmap_sem); 9979b56bb11SFelix Kuehling mmput(mm); 99859d3e8beSAlexey Skidanov 99959d3e8beSAlexey Skidanov mutex_lock(&p->event_mutex); 100059d3e8beSAlexey Skidanov 100159d3e8beSAlexey Skidanov /* Lookup events by type and signal them */ 100259d3e8beSAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY, 100359d3e8beSAlexey Skidanov &memory_exception_data); 100459d3e8beSAlexey Skidanov 100559d3e8beSAlexey Skidanov mutex_unlock(&p->event_mutex); 100659d3e8beSAlexey Skidanov mutex_unlock(&p->mutex); 100759d3e8beSAlexey Skidanov } 1008930c5ff4SAlexey Skidanov 1009930c5ff4SAlexey Skidanov void kfd_signal_hw_exception_event(unsigned int pasid) 1010930c5ff4SAlexey Skidanov { 1011930c5ff4SAlexey Skidanov /* 1012930c5ff4SAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 1013930c5ff4SAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 1014930c5ff4SAlexey Skidanov * running so the lookup function returns a locked process. 1015930c5ff4SAlexey Skidanov */ 1016930c5ff4SAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 1017930c5ff4SAlexey Skidanov 1018930c5ff4SAlexey Skidanov if (!p) 1019930c5ff4SAlexey Skidanov return; /* Presumably process exited. */ 1020930c5ff4SAlexey Skidanov 1021930c5ff4SAlexey Skidanov mutex_lock(&p->event_mutex); 1022930c5ff4SAlexey Skidanov 1023930c5ff4SAlexey Skidanov /* Lookup events by type and signal them */ 1024930c5ff4SAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_HW_EXCEPTION, NULL); 1025930c5ff4SAlexey Skidanov 1026930c5ff4SAlexey Skidanov mutex_unlock(&p->event_mutex); 1027930c5ff4SAlexey Skidanov mutex_unlock(&p->mutex); 1028930c5ff4SAlexey Skidanov } 1029