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> 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 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 && 250*4eacc26bSKent 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 && 269*4eacc26bSKent 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) { 29579775b62SKent Russell pr_warn("Signal event wasn't created because limit was reached\n"); 296f3a39818SAndrew Lewycky return -ENOMEM; 297f3a39818SAndrew Lewycky } 298f3a39818SAndrew Lewycky 299f3a39818SAndrew Lewycky if (!allocate_event_notification_slot(devkfd, p, &ev->signal_page, 300f3a39818SAndrew Lewycky &ev->signal_slot_index)) { 30179775b62SKent Russell pr_warn("Signal event wasn't created because out of kernel memory\n"); 302f3a39818SAndrew Lewycky return -ENOMEM; 303f3a39818SAndrew Lewycky } 304f3a39818SAndrew Lewycky 305f3a39818SAndrew Lewycky p->signal_event_count++; 306f3a39818SAndrew Lewycky 307f3a39818SAndrew Lewycky ev->user_signal_address = 308f3a39818SAndrew Lewycky &ev->signal_page->user_address[ev->signal_slot_index]; 309f3a39818SAndrew Lewycky 310f3a39818SAndrew Lewycky ev->event_id = make_signal_event_id(ev->signal_page, 311f3a39818SAndrew Lewycky ev->signal_slot_index); 312f3a39818SAndrew Lewycky 31379775b62SKent Russell pr_debug("Signal event number %zu created with id %d, address %p\n", 3146235e15eSOded Gabbay p->signal_event_count, ev->event_id, 3156235e15eSOded Gabbay ev->user_signal_address); 3166235e15eSOded Gabbay 317f3a39818SAndrew Lewycky return 0; 318f3a39818SAndrew Lewycky } 319f3a39818SAndrew Lewycky 320f3a39818SAndrew Lewycky /* 321f3a39818SAndrew Lewycky * No non-signal events are supported yet. 322f3a39818SAndrew Lewycky * We create them as events that never signal. 323f3a39818SAndrew Lewycky * Set event calls from user-mode are failed. 324f3a39818SAndrew Lewycky */ 325f3a39818SAndrew Lewycky static int create_other_event(struct kfd_process *p, struct kfd_event *ev) 326f3a39818SAndrew Lewycky { 327f3a39818SAndrew Lewycky ev->event_id = make_nonsignal_event_id(p); 328f3a39818SAndrew Lewycky if (ev->event_id == 0) 329f3a39818SAndrew Lewycky return -ENOMEM; 330f3a39818SAndrew Lewycky 331f3a39818SAndrew Lewycky return 0; 332f3a39818SAndrew Lewycky } 333f3a39818SAndrew Lewycky 334f3a39818SAndrew Lewycky void kfd_event_init_process(struct kfd_process *p) 335f3a39818SAndrew Lewycky { 336f3a39818SAndrew Lewycky mutex_init(&p->event_mutex); 337f3a39818SAndrew Lewycky hash_init(p->events); 338f3a39818SAndrew Lewycky INIT_LIST_HEAD(&p->signal_event_pages); 339f3a39818SAndrew Lewycky p->next_nonsignal_event_id = KFD_FIRST_NONSIGNAL_EVENT_ID; 340f3a39818SAndrew Lewycky p->signal_event_count = 0; 341f3a39818SAndrew Lewycky } 342f3a39818SAndrew Lewycky 343f3a39818SAndrew Lewycky static void destroy_event(struct kfd_process *p, struct kfd_event *ev) 344f3a39818SAndrew Lewycky { 345*4eacc26bSKent Russell if (ev->signal_page) { 346f3a39818SAndrew Lewycky release_event_notification_slot(ev->signal_page, 347f3a39818SAndrew Lewycky ev->signal_slot_index); 348f3a39818SAndrew Lewycky p->signal_event_count--; 349f3a39818SAndrew Lewycky } 350f3a39818SAndrew Lewycky 351f3a39818SAndrew Lewycky /* 352f3a39818SAndrew Lewycky * Abandon the list of waiters. Individual waiting threads will 353f3a39818SAndrew Lewycky * clean up their own data. 354f3a39818SAndrew Lewycky */ 355f3a39818SAndrew Lewycky list_del(&ev->waiters); 356f3a39818SAndrew Lewycky 357f3a39818SAndrew Lewycky hash_del(&ev->events); 358f3a39818SAndrew Lewycky kfree(ev); 359f3a39818SAndrew Lewycky } 360f3a39818SAndrew Lewycky 361f3a39818SAndrew Lewycky static void destroy_events(struct kfd_process *p) 362f3a39818SAndrew Lewycky { 363f3a39818SAndrew Lewycky struct kfd_event *ev; 364f3a39818SAndrew Lewycky struct hlist_node *tmp; 365f3a39818SAndrew Lewycky unsigned int hash_bkt; 366f3a39818SAndrew Lewycky 367f3a39818SAndrew Lewycky hash_for_each_safe(p->events, hash_bkt, tmp, ev, events) 368f3a39818SAndrew Lewycky destroy_event(p, ev); 369f3a39818SAndrew Lewycky } 370f3a39818SAndrew Lewycky 371f3a39818SAndrew Lewycky /* 372f3a39818SAndrew Lewycky * We assume that the process is being destroyed and there is no need to 373f3a39818SAndrew Lewycky * unmap the pages or keep bookkeeping data in order. 374f3a39818SAndrew Lewycky */ 375f3a39818SAndrew Lewycky static void shutdown_signal_pages(struct kfd_process *p) 376f3a39818SAndrew Lewycky { 377f3a39818SAndrew Lewycky struct signal_page *page, *tmp; 378f3a39818SAndrew Lewycky 379f3a39818SAndrew Lewycky list_for_each_entry_safe(page, tmp, &p->signal_event_pages, 380f3a39818SAndrew Lewycky event_pages) { 381f3a39818SAndrew Lewycky free_pages((unsigned long)page->kernel_address, 382f3a39818SAndrew Lewycky get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); 383f3a39818SAndrew Lewycky kfree(page); 384f3a39818SAndrew Lewycky } 385f3a39818SAndrew Lewycky } 386f3a39818SAndrew Lewycky 387f3a39818SAndrew Lewycky void kfd_event_free_process(struct kfd_process *p) 388f3a39818SAndrew Lewycky { 389f3a39818SAndrew Lewycky destroy_events(p); 390f3a39818SAndrew Lewycky shutdown_signal_pages(p); 391f3a39818SAndrew Lewycky } 392f3a39818SAndrew Lewycky 393f3a39818SAndrew Lewycky static bool event_can_be_gpu_signaled(const struct kfd_event *ev) 394f3a39818SAndrew Lewycky { 395f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL || 396f3a39818SAndrew Lewycky ev->type == KFD_EVENT_TYPE_DEBUG; 397f3a39818SAndrew Lewycky } 398f3a39818SAndrew Lewycky 399f3a39818SAndrew Lewycky static bool event_can_be_cpu_signaled(const struct kfd_event *ev) 400f3a39818SAndrew Lewycky { 401f3a39818SAndrew Lewycky return ev->type == KFD_EVENT_TYPE_SIGNAL; 402f3a39818SAndrew Lewycky } 403f3a39818SAndrew Lewycky 404f3a39818SAndrew Lewycky int kfd_event_create(struct file *devkfd, struct kfd_process *p, 405f3a39818SAndrew Lewycky uint32_t event_type, bool auto_reset, uint32_t node_id, 406f3a39818SAndrew Lewycky uint32_t *event_id, uint32_t *event_trigger_data, 407f3a39818SAndrew Lewycky uint64_t *event_page_offset, uint32_t *event_slot_index) 408f3a39818SAndrew Lewycky { 409f3a39818SAndrew Lewycky int ret = 0; 410f3a39818SAndrew Lewycky struct kfd_event *ev = kzalloc(sizeof(*ev), GFP_KERNEL); 411f3a39818SAndrew Lewycky 412f3a39818SAndrew Lewycky if (!ev) 413f3a39818SAndrew Lewycky return -ENOMEM; 414f3a39818SAndrew Lewycky 415f3a39818SAndrew Lewycky ev->type = event_type; 416f3a39818SAndrew Lewycky ev->auto_reset = auto_reset; 417f3a39818SAndrew Lewycky ev->signaled = false; 418f3a39818SAndrew Lewycky 419f3a39818SAndrew Lewycky INIT_LIST_HEAD(&ev->waiters); 420f3a39818SAndrew Lewycky 421f3a39818SAndrew Lewycky *event_page_offset = 0; 422f3a39818SAndrew Lewycky 423f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 424f3a39818SAndrew Lewycky 425f3a39818SAndrew Lewycky switch (event_type) { 426f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_SIGNAL: 427f3a39818SAndrew Lewycky case KFD_EVENT_TYPE_DEBUG: 428f3a39818SAndrew Lewycky ret = create_signal_event(devkfd, p, ev); 429f3a39818SAndrew Lewycky if (!ret) { 430f3a39818SAndrew Lewycky *event_page_offset = (ev->signal_page->page_index | 431f3a39818SAndrew Lewycky KFD_MMAP_EVENTS_MASK); 432f3a39818SAndrew Lewycky *event_page_offset <<= PAGE_SHIFT; 433f3a39818SAndrew Lewycky *event_slot_index = ev->signal_slot_index; 434f3a39818SAndrew Lewycky } 435f3a39818SAndrew Lewycky break; 436f3a39818SAndrew Lewycky default: 437f3a39818SAndrew Lewycky ret = create_other_event(p, ev); 438f3a39818SAndrew Lewycky break; 439f3a39818SAndrew Lewycky } 440f3a39818SAndrew Lewycky 441f3a39818SAndrew Lewycky if (!ret) { 442f3a39818SAndrew Lewycky hash_add(p->events, &ev->events, ev->event_id); 443f3a39818SAndrew Lewycky 444f3a39818SAndrew Lewycky *event_id = ev->event_id; 445f3a39818SAndrew Lewycky *event_trigger_data = ev->event_id; 446f3a39818SAndrew Lewycky } else { 447f3a39818SAndrew Lewycky kfree(ev); 448f3a39818SAndrew Lewycky } 449f3a39818SAndrew Lewycky 450f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 451f3a39818SAndrew Lewycky 452f3a39818SAndrew Lewycky return ret; 453f3a39818SAndrew Lewycky } 454f3a39818SAndrew Lewycky 455f3a39818SAndrew Lewycky /* Assumes that p is current. */ 456f3a39818SAndrew Lewycky int kfd_event_destroy(struct kfd_process *p, uint32_t event_id) 457f3a39818SAndrew Lewycky { 458f3a39818SAndrew Lewycky struct kfd_event *ev; 459f3a39818SAndrew Lewycky int ret = 0; 460f3a39818SAndrew Lewycky 461f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 462f3a39818SAndrew Lewycky 463f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 464f3a39818SAndrew Lewycky 465f3a39818SAndrew Lewycky if (ev) 466f3a39818SAndrew Lewycky destroy_event(p, ev); 467f3a39818SAndrew Lewycky else 468f3a39818SAndrew Lewycky ret = -EINVAL; 469f3a39818SAndrew Lewycky 470f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 471f3a39818SAndrew Lewycky return ret; 472f3a39818SAndrew Lewycky } 473f3a39818SAndrew Lewycky 474f3a39818SAndrew Lewycky static void set_event(struct kfd_event *ev) 475f3a39818SAndrew Lewycky { 476f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter; 477f3a39818SAndrew Lewycky struct kfd_event_waiter *next; 478f3a39818SAndrew Lewycky 479f3a39818SAndrew Lewycky /* Auto reset if the list is non-empty and we're waking someone. */ 480f3a39818SAndrew Lewycky ev->signaled = !ev->auto_reset || list_empty(&ev->waiters); 481f3a39818SAndrew Lewycky 482f3a39818SAndrew Lewycky list_for_each_entry_safe(waiter, next, &ev->waiters, waiters) { 483f3a39818SAndrew Lewycky waiter->activated = true; 484f3a39818SAndrew Lewycky 485f3a39818SAndrew Lewycky /* _init because free_waiters will call list_del */ 486f3a39818SAndrew Lewycky list_del_init(&waiter->waiters); 487f3a39818SAndrew Lewycky 488f3a39818SAndrew Lewycky wake_up_process(waiter->sleeping_task); 489f3a39818SAndrew Lewycky } 490f3a39818SAndrew Lewycky } 491f3a39818SAndrew Lewycky 492f3a39818SAndrew Lewycky /* Assumes that p is current. */ 493f3a39818SAndrew Lewycky int kfd_set_event(struct kfd_process *p, uint32_t event_id) 494f3a39818SAndrew Lewycky { 495f3a39818SAndrew Lewycky int ret = 0; 496f3a39818SAndrew Lewycky struct kfd_event *ev; 497f3a39818SAndrew Lewycky 498f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 499f3a39818SAndrew Lewycky 500f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 501f3a39818SAndrew Lewycky 502f3a39818SAndrew Lewycky if (ev && event_can_be_cpu_signaled(ev)) 503f3a39818SAndrew Lewycky set_event(ev); 504f3a39818SAndrew Lewycky else 505f3a39818SAndrew Lewycky ret = -EINVAL; 506f3a39818SAndrew Lewycky 507f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 508f3a39818SAndrew Lewycky return ret; 509f3a39818SAndrew Lewycky } 510f3a39818SAndrew Lewycky 511f3a39818SAndrew Lewycky static void reset_event(struct kfd_event *ev) 512f3a39818SAndrew Lewycky { 513f3a39818SAndrew Lewycky ev->signaled = false; 514f3a39818SAndrew Lewycky } 515f3a39818SAndrew Lewycky 516f3a39818SAndrew Lewycky /* Assumes that p is current. */ 517f3a39818SAndrew Lewycky int kfd_reset_event(struct kfd_process *p, uint32_t event_id) 518f3a39818SAndrew Lewycky { 519f3a39818SAndrew Lewycky int ret = 0; 520f3a39818SAndrew Lewycky struct kfd_event *ev; 521f3a39818SAndrew Lewycky 522f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 523f3a39818SAndrew Lewycky 524f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, event_id); 525f3a39818SAndrew Lewycky 526f3a39818SAndrew Lewycky if (ev && event_can_be_cpu_signaled(ev)) 527f3a39818SAndrew Lewycky reset_event(ev); 528f3a39818SAndrew Lewycky else 529f3a39818SAndrew Lewycky ret = -EINVAL; 530f3a39818SAndrew Lewycky 531f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 532f3a39818SAndrew Lewycky return ret; 533f3a39818SAndrew Lewycky 534f3a39818SAndrew Lewycky } 535f3a39818SAndrew Lewycky 536f3a39818SAndrew Lewycky static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev) 537f3a39818SAndrew Lewycky { 538f3a39818SAndrew Lewycky page_slots(ev->signal_page)[ev->signal_slot_index] = 539f3a39818SAndrew Lewycky UNSIGNALED_EVENT_SLOT; 540f3a39818SAndrew Lewycky } 541f3a39818SAndrew Lewycky 542f3a39818SAndrew Lewycky static bool is_slot_signaled(struct signal_page *page, unsigned int index) 543f3a39818SAndrew Lewycky { 544f3a39818SAndrew Lewycky return page_slots(page)[index] != UNSIGNALED_EVENT_SLOT; 545f3a39818SAndrew Lewycky } 546f3a39818SAndrew Lewycky 547f3a39818SAndrew Lewycky static void set_event_from_interrupt(struct kfd_process *p, 548f3a39818SAndrew Lewycky struct kfd_event *ev) 549f3a39818SAndrew Lewycky { 550f3a39818SAndrew Lewycky if (ev && event_can_be_gpu_signaled(ev)) { 551f3a39818SAndrew Lewycky acknowledge_signal(p, ev); 552f3a39818SAndrew Lewycky set_event(ev); 553f3a39818SAndrew Lewycky } 554f3a39818SAndrew Lewycky } 555f3a39818SAndrew Lewycky 556f3a39818SAndrew Lewycky void kfd_signal_event_interrupt(unsigned int pasid, uint32_t partial_id, 557f3a39818SAndrew Lewycky uint32_t valid_id_bits) 558f3a39818SAndrew Lewycky { 559f3a39818SAndrew Lewycky struct kfd_event *ev; 560f3a39818SAndrew Lewycky 561f3a39818SAndrew Lewycky /* 562f3a39818SAndrew Lewycky * Because we are called from arbitrary context (workqueue) as opposed 563f3a39818SAndrew Lewycky * to process context, kfd_process could attempt to exit while we are 564f3a39818SAndrew Lewycky * running so the lookup function returns a locked process. 565f3a39818SAndrew Lewycky */ 566f3a39818SAndrew Lewycky struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 567f3a39818SAndrew Lewycky 568f3a39818SAndrew Lewycky if (!p) 569f3a39818SAndrew Lewycky return; /* Presumably process exited. */ 570f3a39818SAndrew Lewycky 571f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 572f3a39818SAndrew Lewycky 573f3a39818SAndrew Lewycky if (valid_id_bits >= INTERRUPT_DATA_BITS) { 574f3a39818SAndrew Lewycky /* Partial ID is a full ID. */ 575f3a39818SAndrew Lewycky ev = lookup_event_by_id(p, partial_id); 576f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 577f3a39818SAndrew Lewycky } else { 578f3a39818SAndrew Lewycky /* 579f3a39818SAndrew Lewycky * Partial ID is in fact partial. For now we completely 580f3a39818SAndrew Lewycky * ignore it, but we could use any bits we did receive to 581f3a39818SAndrew Lewycky * search faster. 582f3a39818SAndrew Lewycky */ 583f3a39818SAndrew Lewycky struct signal_page *page; 5848eabaf54SKent Russell unsigned int i; 585f3a39818SAndrew Lewycky 586f3a39818SAndrew Lewycky list_for_each_entry(page, &p->signal_event_pages, event_pages) 587f3a39818SAndrew Lewycky for (i = 0; i < SLOTS_PER_PAGE; i++) 588f3a39818SAndrew Lewycky if (is_slot_signaled(page, i)) { 589f3a39818SAndrew Lewycky ev = lookup_event_by_page_slot(p, 590f3a39818SAndrew Lewycky page, i); 591f3a39818SAndrew Lewycky set_event_from_interrupt(p, ev); 592f3a39818SAndrew Lewycky } 593f3a39818SAndrew Lewycky } 594f3a39818SAndrew Lewycky 595f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 596f3a39818SAndrew Lewycky mutex_unlock(&p->mutex); 597f3a39818SAndrew Lewycky } 598f3a39818SAndrew Lewycky 599f3a39818SAndrew Lewycky static struct kfd_event_waiter *alloc_event_waiters(uint32_t num_events) 600f3a39818SAndrew Lewycky { 601f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters; 602f3a39818SAndrew Lewycky uint32_t i; 603f3a39818SAndrew Lewycky 604f3a39818SAndrew Lewycky event_waiters = kmalloc_array(num_events, 605f3a39818SAndrew Lewycky sizeof(struct kfd_event_waiter), 606f3a39818SAndrew Lewycky GFP_KERNEL); 607f3a39818SAndrew Lewycky 608f3a39818SAndrew Lewycky for (i = 0; (event_waiters) && (i < num_events) ; i++) { 609f3a39818SAndrew Lewycky INIT_LIST_HEAD(&event_waiters[i].waiters); 610f3a39818SAndrew Lewycky event_waiters[i].sleeping_task = current; 611f3a39818SAndrew Lewycky event_waiters[i].activated = false; 612f3a39818SAndrew Lewycky } 613f3a39818SAndrew Lewycky 614f3a39818SAndrew Lewycky return event_waiters; 615f3a39818SAndrew Lewycky } 616f3a39818SAndrew Lewycky 617f3a39818SAndrew Lewycky static int init_event_waiter(struct kfd_process *p, 618f3a39818SAndrew Lewycky struct kfd_event_waiter *waiter, 61959d3e8beSAlexey Skidanov uint32_t event_id, 62059d3e8beSAlexey Skidanov uint32_t input_index) 621f3a39818SAndrew Lewycky { 622f3a39818SAndrew Lewycky struct kfd_event *ev = lookup_event_by_id(p, event_id); 623f3a39818SAndrew Lewycky 624f3a39818SAndrew Lewycky if (!ev) 625f3a39818SAndrew Lewycky return -EINVAL; 626f3a39818SAndrew Lewycky 62759d3e8beSAlexey Skidanov waiter->event = ev; 62859d3e8beSAlexey Skidanov waiter->input_index = input_index; 629f3a39818SAndrew Lewycky waiter->activated = ev->signaled; 630f3a39818SAndrew Lewycky ev->signaled = ev->signaled && !ev->auto_reset; 631f3a39818SAndrew Lewycky 632f3a39818SAndrew Lewycky list_add(&waiter->waiters, &ev->waiters); 633f3a39818SAndrew Lewycky 634f3a39818SAndrew Lewycky return 0; 635f3a39818SAndrew Lewycky } 636f3a39818SAndrew Lewycky 637f3a39818SAndrew Lewycky static bool test_event_condition(bool all, uint32_t num_events, 638f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters) 639f3a39818SAndrew Lewycky { 640f3a39818SAndrew Lewycky uint32_t i; 641f3a39818SAndrew Lewycky uint32_t activated_count = 0; 642f3a39818SAndrew Lewycky 643f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 644f3a39818SAndrew Lewycky if (event_waiters[i].activated) { 645f3a39818SAndrew Lewycky if (!all) 646f3a39818SAndrew Lewycky return true; 647f3a39818SAndrew Lewycky 648f3a39818SAndrew Lewycky activated_count++; 649f3a39818SAndrew Lewycky } 650f3a39818SAndrew Lewycky } 651f3a39818SAndrew Lewycky 652f3a39818SAndrew Lewycky return activated_count == num_events; 653f3a39818SAndrew Lewycky } 654f3a39818SAndrew Lewycky 65559d3e8beSAlexey Skidanov /* 65659d3e8beSAlexey Skidanov * Copy event specific data, if defined. 65759d3e8beSAlexey Skidanov * Currently only memory exception events have additional data to copy to user 65859d3e8beSAlexey Skidanov */ 65959d3e8beSAlexey Skidanov static bool copy_signaled_event_data(uint32_t num_events, 66059d3e8beSAlexey Skidanov struct kfd_event_waiter *event_waiters, 66159d3e8beSAlexey Skidanov struct kfd_event_data __user *data) 66259d3e8beSAlexey Skidanov { 66359d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *src; 66459d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data __user *dst; 66559d3e8beSAlexey Skidanov struct kfd_event_waiter *waiter; 66659d3e8beSAlexey Skidanov struct kfd_event *event; 66759d3e8beSAlexey Skidanov uint32_t i; 66859d3e8beSAlexey Skidanov 66959d3e8beSAlexey Skidanov for (i = 0; i < num_events; i++) { 67059d3e8beSAlexey Skidanov waiter = &event_waiters[i]; 67159d3e8beSAlexey Skidanov event = waiter->event; 67259d3e8beSAlexey Skidanov if (waiter->activated && event->type == KFD_EVENT_TYPE_MEMORY) { 67359d3e8beSAlexey Skidanov dst = &data[waiter->input_index].memory_exception_data; 67459d3e8beSAlexey Skidanov src = &event->memory_exception_data; 67559d3e8beSAlexey Skidanov if (copy_to_user(dst, src, 67659d3e8beSAlexey Skidanov sizeof(struct kfd_hsa_memory_exception_data))) 67759d3e8beSAlexey Skidanov return false; 67859d3e8beSAlexey Skidanov } 67959d3e8beSAlexey Skidanov } 68059d3e8beSAlexey Skidanov 68159d3e8beSAlexey Skidanov return true; 68259d3e8beSAlexey Skidanov 68359d3e8beSAlexey Skidanov } 68459d3e8beSAlexey Skidanov 68559d3e8beSAlexey Skidanov 68659d3e8beSAlexey Skidanov 687f3a39818SAndrew Lewycky static long user_timeout_to_jiffies(uint32_t user_timeout_ms) 688f3a39818SAndrew Lewycky { 689f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE) 690f3a39818SAndrew Lewycky return 0; 691f3a39818SAndrew Lewycky 692f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_INFINITE) 693f3a39818SAndrew Lewycky return MAX_SCHEDULE_TIMEOUT; 694f3a39818SAndrew Lewycky 695f3a39818SAndrew Lewycky /* 696f3a39818SAndrew Lewycky * msecs_to_jiffies interprets all values above 2^31-1 as infinite, 697f3a39818SAndrew Lewycky * but we consider them finite. 698f3a39818SAndrew Lewycky * This hack is wrong, but nobody is likely to notice. 699f3a39818SAndrew Lewycky */ 700f3a39818SAndrew Lewycky user_timeout_ms = min_t(uint32_t, user_timeout_ms, 0x7FFFFFFF); 701f3a39818SAndrew Lewycky 702f3a39818SAndrew Lewycky return msecs_to_jiffies(user_timeout_ms) + 1; 703f3a39818SAndrew Lewycky } 704f3a39818SAndrew Lewycky 705f3a39818SAndrew Lewycky static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters) 706f3a39818SAndrew Lewycky { 707f3a39818SAndrew Lewycky uint32_t i; 708f3a39818SAndrew Lewycky 709f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) 710f3a39818SAndrew Lewycky list_del(&waiters[i].waiters); 711f3a39818SAndrew Lewycky 712f3a39818SAndrew Lewycky kfree(waiters); 713f3a39818SAndrew Lewycky } 714f3a39818SAndrew Lewycky 715f3a39818SAndrew Lewycky int kfd_wait_on_events(struct kfd_process *p, 71659d3e8beSAlexey Skidanov uint32_t num_events, void __user *data, 717f3a39818SAndrew Lewycky bool all, uint32_t user_timeout_ms, 718f3a39818SAndrew Lewycky enum kfd_event_wait_result *wait_result) 719f3a39818SAndrew Lewycky { 72059d3e8beSAlexey Skidanov struct kfd_event_data __user *events = 72159d3e8beSAlexey Skidanov (struct kfd_event_data __user *) data; 722f3a39818SAndrew Lewycky uint32_t i; 723f3a39818SAndrew Lewycky int ret = 0; 724f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters = NULL; 725f3a39818SAndrew Lewycky long timeout = user_timeout_to_jiffies(user_timeout_ms); 726f3a39818SAndrew Lewycky 727f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 728f3a39818SAndrew Lewycky 729f3a39818SAndrew Lewycky event_waiters = alloc_event_waiters(num_events); 730f3a39818SAndrew Lewycky if (!event_waiters) { 731f3a39818SAndrew Lewycky ret = -ENOMEM; 732f3a39818SAndrew Lewycky goto fail; 733f3a39818SAndrew Lewycky } 734f3a39818SAndrew Lewycky 735f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 73659d3e8beSAlexey Skidanov struct kfd_event_data event_data; 737f3a39818SAndrew Lewycky 73859d3e8beSAlexey Skidanov if (copy_from_user(&event_data, &events[i], 7398bf79388SPan Bian sizeof(struct kfd_event_data))) { 7408bf79388SPan Bian ret = -EFAULT; 741f3a39818SAndrew Lewycky goto fail; 7428bf79388SPan Bian } 743f3a39818SAndrew Lewycky 74459d3e8beSAlexey Skidanov ret = init_event_waiter(p, &event_waiters[i], 74559d3e8beSAlexey Skidanov event_data.event_id, i); 746f3a39818SAndrew Lewycky if (ret) 747f3a39818SAndrew Lewycky goto fail; 748f3a39818SAndrew Lewycky } 749f3a39818SAndrew Lewycky 750f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 751f3a39818SAndrew Lewycky 752f3a39818SAndrew Lewycky while (true) { 753f3a39818SAndrew Lewycky if (fatal_signal_pending(current)) { 754f3a39818SAndrew Lewycky ret = -EINTR; 755f3a39818SAndrew Lewycky break; 756f3a39818SAndrew Lewycky } 757f3a39818SAndrew Lewycky 758f3a39818SAndrew Lewycky if (signal_pending(current)) { 759f3a39818SAndrew Lewycky /* 760f3a39818SAndrew Lewycky * This is wrong when a nonzero, non-infinite timeout 761f3a39818SAndrew Lewycky * is specified. We need to use 762f3a39818SAndrew Lewycky * ERESTARTSYS_RESTARTBLOCK, but struct restart_block 763f3a39818SAndrew Lewycky * contains a union with data for each user and it's 764f3a39818SAndrew Lewycky * in generic kernel code that I don't want to 765f3a39818SAndrew Lewycky * touch yet. 766f3a39818SAndrew Lewycky */ 767f3a39818SAndrew Lewycky ret = -ERESTARTSYS; 768f3a39818SAndrew Lewycky break; 769f3a39818SAndrew Lewycky } 770f3a39818SAndrew Lewycky 771f3a39818SAndrew Lewycky if (test_event_condition(all, num_events, event_waiters)) { 77259d3e8beSAlexey Skidanov if (copy_signaled_event_data(num_events, 77359d3e8beSAlexey Skidanov event_waiters, events)) 774f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_COMPLETE; 77559d3e8beSAlexey Skidanov else 77659d3e8beSAlexey Skidanov *wait_result = KFD_WAIT_ERROR; 777f3a39818SAndrew Lewycky break; 778f3a39818SAndrew Lewycky } 779f3a39818SAndrew Lewycky 780f3a39818SAndrew Lewycky if (timeout <= 0) { 781f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_TIMEOUT; 782f3a39818SAndrew Lewycky break; 783f3a39818SAndrew Lewycky } 784f3a39818SAndrew Lewycky 785f3a39818SAndrew Lewycky timeout = schedule_timeout_interruptible(timeout); 786f3a39818SAndrew Lewycky } 787f3a39818SAndrew Lewycky __set_current_state(TASK_RUNNING); 788f3a39818SAndrew Lewycky 789f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 790f3a39818SAndrew Lewycky free_waiters(num_events, event_waiters); 791f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 792f3a39818SAndrew Lewycky 793f3a39818SAndrew Lewycky return ret; 794f3a39818SAndrew Lewycky 795f3a39818SAndrew Lewycky fail: 796f3a39818SAndrew Lewycky if (event_waiters) 797f3a39818SAndrew Lewycky free_waiters(num_events, event_waiters); 798f3a39818SAndrew Lewycky 799f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 800f3a39818SAndrew Lewycky 801f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_ERROR; 802f3a39818SAndrew Lewycky 803f3a39818SAndrew Lewycky return ret; 804f3a39818SAndrew Lewycky } 805f3a39818SAndrew Lewycky 806f3a39818SAndrew Lewycky int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma) 807f3a39818SAndrew Lewycky { 808f3a39818SAndrew Lewycky 809f3a39818SAndrew Lewycky unsigned int page_index; 810f3a39818SAndrew Lewycky unsigned long pfn; 811f3a39818SAndrew Lewycky struct signal_page *page; 812f3a39818SAndrew Lewycky 813f3a39818SAndrew Lewycky /* check required size is logical */ 814f3a39818SAndrew Lewycky if (get_order(KFD_SIGNAL_EVENT_LIMIT * 8) != 815f3a39818SAndrew Lewycky get_order(vma->vm_end - vma->vm_start)) { 81679775b62SKent Russell pr_err("Event page mmap requested illegal size\n"); 817f3a39818SAndrew Lewycky return -EINVAL; 818f3a39818SAndrew Lewycky } 819f3a39818SAndrew Lewycky 820f3a39818SAndrew Lewycky page_index = vma->vm_pgoff; 821f3a39818SAndrew Lewycky 822f3a39818SAndrew Lewycky page = lookup_signal_page_by_index(p, page_index); 823f3a39818SAndrew Lewycky if (!page) { 824f3a39818SAndrew Lewycky /* Probably KFD bug, but mmap is user-accessible. */ 82579775b62SKent Russell pr_debug("Signal page could not be found for page_index %u\n", 826f3a39818SAndrew Lewycky page_index); 827f3a39818SAndrew Lewycky return -EINVAL; 828f3a39818SAndrew Lewycky } 829f3a39818SAndrew Lewycky 830f3a39818SAndrew Lewycky pfn = __pa(page->kernel_address); 831f3a39818SAndrew Lewycky pfn >>= PAGE_SHIFT; 832f3a39818SAndrew Lewycky 833f3a39818SAndrew Lewycky vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE 834f3a39818SAndrew Lewycky | VM_DONTDUMP | VM_PFNMAP; 835f3a39818SAndrew Lewycky 83679775b62SKent Russell pr_debug("Mapping signal page\n"); 837f3a39818SAndrew Lewycky pr_debug(" start user address == 0x%08lx\n", vma->vm_start); 838f3a39818SAndrew Lewycky pr_debug(" end user address == 0x%08lx\n", vma->vm_end); 839f3a39818SAndrew Lewycky pr_debug(" pfn == 0x%016lX\n", pfn); 840f3a39818SAndrew Lewycky pr_debug(" vm_flags == 0x%08lX\n", vma->vm_flags); 841f3a39818SAndrew Lewycky pr_debug(" size == 0x%08lX\n", 842f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start); 843f3a39818SAndrew Lewycky 844f3a39818SAndrew Lewycky page->user_address = (uint64_t __user *)vma->vm_start; 845f3a39818SAndrew Lewycky 846f3a39818SAndrew Lewycky /* mapping the page to user process */ 847f3a39818SAndrew Lewycky return remap_pfn_range(vma, vma->vm_start, pfn, 848f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start, vma->vm_page_prot); 849f3a39818SAndrew Lewycky } 85059d3e8beSAlexey Skidanov 85159d3e8beSAlexey Skidanov /* 85259d3e8beSAlexey Skidanov * Assumes that p->event_mutex is held and of course 85359d3e8beSAlexey Skidanov * that p is not going away (current or locked). 85459d3e8beSAlexey Skidanov */ 85559d3e8beSAlexey Skidanov static void lookup_events_by_type_and_signal(struct kfd_process *p, 85659d3e8beSAlexey Skidanov int type, void *event_data) 85759d3e8beSAlexey Skidanov { 85859d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *ev_data; 85959d3e8beSAlexey Skidanov struct kfd_event *ev; 86059d3e8beSAlexey Skidanov int bkt; 86159d3e8beSAlexey Skidanov bool send_signal = true; 86259d3e8beSAlexey Skidanov 86359d3e8beSAlexey Skidanov ev_data = (struct kfd_hsa_memory_exception_data *) event_data; 86459d3e8beSAlexey Skidanov 86559d3e8beSAlexey Skidanov hash_for_each(p->events, bkt, ev, events) 86659d3e8beSAlexey Skidanov if (ev->type == type) { 86759d3e8beSAlexey Skidanov send_signal = false; 86859d3e8beSAlexey Skidanov dev_dbg(kfd_device, 86959d3e8beSAlexey Skidanov "Event found: id %X type %d", 87059d3e8beSAlexey Skidanov ev->event_id, ev->type); 87159d3e8beSAlexey Skidanov set_event(ev); 87259d3e8beSAlexey Skidanov if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data) 87359d3e8beSAlexey Skidanov ev->memory_exception_data = *ev_data; 87459d3e8beSAlexey Skidanov } 87559d3e8beSAlexey Skidanov 87659d3e8beSAlexey Skidanov /* Send SIGTERM no event of type "type" has been found*/ 87759d3e8beSAlexey Skidanov if (send_signal) { 87881663016SOded Gabbay if (send_sigterm) { 87959d3e8beSAlexey Skidanov dev_warn(kfd_device, 88059d3e8beSAlexey Skidanov "Sending SIGTERM to HSA Process with PID %d ", 88159d3e8beSAlexey Skidanov p->lead_thread->pid); 88259d3e8beSAlexey Skidanov send_sig(SIGTERM, p->lead_thread, 0); 88381663016SOded Gabbay } else { 88481663016SOded Gabbay dev_err(kfd_device, 88581663016SOded Gabbay "HSA Process (PID %d) got unhandled exception", 88681663016SOded Gabbay p->lead_thread->pid); 88781663016SOded Gabbay } 88859d3e8beSAlexey Skidanov } 88959d3e8beSAlexey Skidanov } 89059d3e8beSAlexey Skidanov 89159d3e8beSAlexey Skidanov void kfd_signal_iommu_event(struct kfd_dev *dev, unsigned int pasid, 89259d3e8beSAlexey Skidanov unsigned long address, bool is_write_requested, 89359d3e8beSAlexey Skidanov bool is_execute_requested) 89459d3e8beSAlexey Skidanov { 89559d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data memory_exception_data; 89659d3e8beSAlexey Skidanov struct vm_area_struct *vma; 89759d3e8beSAlexey Skidanov 89859d3e8beSAlexey Skidanov /* 89959d3e8beSAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 90059d3e8beSAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 90159d3e8beSAlexey Skidanov * running so the lookup function returns a locked process. 90259d3e8beSAlexey Skidanov */ 90359d3e8beSAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 90459d3e8beSAlexey Skidanov 90559d3e8beSAlexey Skidanov if (!p) 90659d3e8beSAlexey Skidanov return; /* Presumably process exited. */ 90759d3e8beSAlexey Skidanov 90859d3e8beSAlexey Skidanov memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 90959d3e8beSAlexey Skidanov 91059d3e8beSAlexey Skidanov down_read(&p->mm->mmap_sem); 91159d3e8beSAlexey Skidanov vma = find_vma(p->mm, address); 91259d3e8beSAlexey Skidanov 91359d3e8beSAlexey Skidanov memory_exception_data.gpu_id = dev->id; 91459d3e8beSAlexey Skidanov memory_exception_data.va = address; 91559d3e8beSAlexey Skidanov /* Set failure reason */ 91659d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 91759d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 91859d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 91959d3e8beSAlexey Skidanov if (vma) { 92059d3e8beSAlexey Skidanov if (vma->vm_start > address) { 92159d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 92259d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 92359d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 92459d3e8beSAlexey Skidanov } else { 92559d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 0; 92659d3e8beSAlexey Skidanov if (is_write_requested && !(vma->vm_flags & VM_WRITE)) 92759d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 1; 92859d3e8beSAlexey Skidanov else 92959d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 93059d3e8beSAlexey Skidanov if (is_execute_requested && !(vma->vm_flags & VM_EXEC)) 93159d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 1; 93259d3e8beSAlexey Skidanov else 93359d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 93459d3e8beSAlexey Skidanov } 93559d3e8beSAlexey Skidanov } 93659d3e8beSAlexey Skidanov 93759d3e8beSAlexey Skidanov up_read(&p->mm->mmap_sem); 93859d3e8beSAlexey Skidanov 93959d3e8beSAlexey Skidanov mutex_lock(&p->event_mutex); 94059d3e8beSAlexey Skidanov 94159d3e8beSAlexey Skidanov /* Lookup events by type and signal them */ 94259d3e8beSAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY, 94359d3e8beSAlexey Skidanov &memory_exception_data); 94459d3e8beSAlexey Skidanov 94559d3e8beSAlexey Skidanov mutex_unlock(&p->event_mutex); 94659d3e8beSAlexey Skidanov mutex_unlock(&p->mutex); 94759d3e8beSAlexey Skidanov } 948930c5ff4SAlexey Skidanov 949930c5ff4SAlexey Skidanov void kfd_signal_hw_exception_event(unsigned int pasid) 950930c5ff4SAlexey Skidanov { 951930c5ff4SAlexey Skidanov /* 952930c5ff4SAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 953930c5ff4SAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 954930c5ff4SAlexey Skidanov * running so the lookup function returns a locked process. 955930c5ff4SAlexey Skidanov */ 956930c5ff4SAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 957930c5ff4SAlexey Skidanov 958930c5ff4SAlexey Skidanov if (!p) 959930c5ff4SAlexey Skidanov return; /* Presumably process exited. */ 960930c5ff4SAlexey Skidanov 961930c5ff4SAlexey Skidanov mutex_lock(&p->event_mutex); 962930c5ff4SAlexey Skidanov 963930c5ff4SAlexey Skidanov /* Lookup events by type and signal them */ 964930c5ff4SAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_HW_EXCEPTION, NULL); 965930c5ff4SAlexey Skidanov 966930c5ff4SAlexey Skidanov mutex_unlock(&p->event_mutex); 967930c5ff4SAlexey Skidanov mutex_unlock(&p->mutex); 968930c5ff4SAlexey Skidanov } 969