1f3a39818SAndrew Lewycky /* 2f3a39818SAndrew Lewycky * Copyright 2014 Advanced Micro Devices, Inc. 3f3a39818SAndrew Lewycky * 4f3a39818SAndrew Lewycky * Permission is hereby granted, free of charge, to any person obtaining a 5f3a39818SAndrew Lewycky * copy of this software and associated documentation files (the "Software"), 6f3a39818SAndrew Lewycky * to deal in the Software without restriction, including without limitation 7f3a39818SAndrew Lewycky * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8f3a39818SAndrew Lewycky * and/or sell copies of the Software, and to permit persons to whom the 9f3a39818SAndrew Lewycky * Software is furnished to do so, subject to the following conditions: 10f3a39818SAndrew Lewycky * 11f3a39818SAndrew Lewycky * The above copyright notice and this permission notice shall be included in 12f3a39818SAndrew Lewycky * all copies or substantial portions of the Software. 13f3a39818SAndrew Lewycky * 14f3a39818SAndrew Lewycky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15f3a39818SAndrew Lewycky * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16f3a39818SAndrew Lewycky * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17f3a39818SAndrew Lewycky * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18f3a39818SAndrew Lewycky * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19f3a39818SAndrew Lewycky * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20f3a39818SAndrew Lewycky * OTHER DEALINGS IN THE SOFTWARE. 21f3a39818SAndrew Lewycky */ 22f3a39818SAndrew Lewycky 23f3a39818SAndrew Lewycky #include <linux/mm_types.h> 24f3a39818SAndrew Lewycky #include <linux/slab.h> 25f3a39818SAndrew Lewycky #include <linux/types.h> 26f3a39818SAndrew Lewycky #include <linux/sched.h> 27f3a39818SAndrew Lewycky #include <linux/uaccess.h> 28f3a39818SAndrew Lewycky #include <linux/mm.h> 29f3a39818SAndrew Lewycky #include <linux/mman.h> 30f3a39818SAndrew Lewycky #include <linux/memory.h> 31f3a39818SAndrew Lewycky #include "kfd_priv.h" 32f3a39818SAndrew Lewycky #include "kfd_events.h" 3359d3e8beSAlexey Skidanov #include <linux/device.h> 34f3a39818SAndrew Lewycky 35f3a39818SAndrew Lewycky /* 36f3a39818SAndrew Lewycky * A task can only be on a single wait_queue at a time, but we need to support 37f3a39818SAndrew Lewycky * waiting on multiple events (any/all). 38f3a39818SAndrew Lewycky * Instead of each event simply having a wait_queue with sleeping tasks, it 39f3a39818SAndrew Lewycky * has a singly-linked list of tasks. 40f3a39818SAndrew Lewycky * A thread that wants to sleep creates an array of these, one for each event 41f3a39818SAndrew Lewycky * and adds one to each event's waiter chain. 42f3a39818SAndrew Lewycky */ 43f3a39818SAndrew Lewycky struct kfd_event_waiter { 44f3a39818SAndrew Lewycky struct list_head waiters; 45f3a39818SAndrew Lewycky struct task_struct *sleeping_task; 46f3a39818SAndrew Lewycky 47f3a39818SAndrew Lewycky /* Transitions to true when the event this belongs to is signaled. */ 48f3a39818SAndrew Lewycky bool activated; 4959d3e8beSAlexey Skidanov 5059d3e8beSAlexey Skidanov /* Event */ 5159d3e8beSAlexey Skidanov struct kfd_event *event; 5259d3e8beSAlexey Skidanov uint32_t input_index; 53f3a39818SAndrew Lewycky }; 54f3a39818SAndrew Lewycky 55f3a39818SAndrew Lewycky /* 56f3a39818SAndrew Lewycky * Over-complicated pooled allocator for event notification slots. 57f3a39818SAndrew Lewycky * 58f3a39818SAndrew Lewycky * Each signal event needs a 64-bit signal slot where the signaler will write 59f3a39818SAndrew Lewycky * a 1 before sending an interrupt.l (This is needed because some interrupts 60f3a39818SAndrew Lewycky * do not contain enough spare data bits to identify an event.) 61f3a39818SAndrew Lewycky * We get whole pages from vmalloc and map them to the process VA. 62f3a39818SAndrew Lewycky * Individual signal events are then allocated a slot in a page. 63f3a39818SAndrew Lewycky */ 64f3a39818SAndrew Lewycky 65f3a39818SAndrew Lewycky struct signal_page { 66f3a39818SAndrew Lewycky struct list_head event_pages; /* kfd_process.signal_event_pages */ 67f3a39818SAndrew Lewycky uint64_t *kernel_address; 68f3a39818SAndrew Lewycky uint64_t __user *user_address; 69f3a39818SAndrew Lewycky uint32_t page_index; /* Index into the mmap aperture. */ 70f3a39818SAndrew Lewycky unsigned int free_slots; 71f3a39818SAndrew Lewycky unsigned long used_slot_bitmap[0]; 72f3a39818SAndrew Lewycky }; 73f3a39818SAndrew Lewycky 74f3a39818SAndrew Lewycky #define SLOTS_PER_PAGE KFD_SIGNAL_EVENT_LIMIT 75f3a39818SAndrew Lewycky #define SLOT_BITMAP_SIZE BITS_TO_LONGS(SLOTS_PER_PAGE) 76f3a39818SAndrew Lewycky #define BITS_PER_PAGE (ilog2(SLOTS_PER_PAGE)+1) 77f3a39818SAndrew Lewycky #define SIGNAL_PAGE_SIZE (sizeof(struct signal_page) + \ 78f3a39818SAndrew Lewycky SLOT_BITMAP_SIZE * sizeof(long)) 79f3a39818SAndrew Lewycky 80f3a39818SAndrew Lewycky /* 81f3a39818SAndrew Lewycky * For signal events, the event ID is used as the interrupt user data. 82f3a39818SAndrew Lewycky * For SQ s_sendmsg interrupts, this is limited to 8 bits. 83f3a39818SAndrew Lewycky */ 84f3a39818SAndrew Lewycky 85f3a39818SAndrew Lewycky #define INTERRUPT_DATA_BITS 8 86f3a39818SAndrew Lewycky #define SIGNAL_EVENT_ID_SLOT_SHIFT 0 87f3a39818SAndrew Lewycky 88f3a39818SAndrew Lewycky static uint64_t *page_slots(struct signal_page *page) 89f3a39818SAndrew Lewycky { 90f3a39818SAndrew Lewycky return page->kernel_address; 91f3a39818SAndrew Lewycky } 92f3a39818SAndrew Lewycky 93f3a39818SAndrew Lewycky static bool allocate_free_slot(struct kfd_process *process, 94f3a39818SAndrew Lewycky struct signal_page **out_page, 95f3a39818SAndrew Lewycky unsigned int *out_slot_index) 96f3a39818SAndrew Lewycky { 97f3a39818SAndrew Lewycky struct signal_page *page; 98f3a39818SAndrew Lewycky 99f3a39818SAndrew Lewycky list_for_each_entry(page, &process->signal_event_pages, event_pages) { 100f3a39818SAndrew Lewycky if (page->free_slots > 0) { 101f3a39818SAndrew Lewycky unsigned int slot = 102f3a39818SAndrew Lewycky find_first_zero_bit(page->used_slot_bitmap, 103f3a39818SAndrew Lewycky SLOTS_PER_PAGE); 104f3a39818SAndrew Lewycky 105f3a39818SAndrew Lewycky __set_bit(slot, page->used_slot_bitmap); 106f3a39818SAndrew Lewycky page->free_slots--; 107f3a39818SAndrew Lewycky 108f3a39818SAndrew Lewycky page_slots(page)[slot] = UNSIGNALED_EVENT_SLOT; 109f3a39818SAndrew Lewycky 110f3a39818SAndrew Lewycky *out_page = page; 111f3a39818SAndrew Lewycky *out_slot_index = slot; 112f3a39818SAndrew Lewycky 113f3a39818SAndrew Lewycky pr_debug("allocated event signal slot in page %p, slot %d\n", 114f3a39818SAndrew Lewycky page, slot); 115f3a39818SAndrew Lewycky 116f3a39818SAndrew Lewycky return true; 117f3a39818SAndrew Lewycky } 118f3a39818SAndrew Lewycky } 119f3a39818SAndrew Lewycky 120f3a39818SAndrew Lewycky pr_debug("No free event signal slots were found for process %p\n", 121f3a39818SAndrew Lewycky process); 122f3a39818SAndrew Lewycky 123f3a39818SAndrew Lewycky return false; 124f3a39818SAndrew Lewycky } 125f3a39818SAndrew Lewycky 126f3a39818SAndrew Lewycky #define list_tail_entry(head, type, member) \ 127f3a39818SAndrew Lewycky list_entry((head)->prev, type, member) 128f3a39818SAndrew Lewycky 129f3a39818SAndrew Lewycky static bool allocate_signal_page(struct file *devkfd, struct kfd_process *p) 130f3a39818SAndrew Lewycky { 131f3a39818SAndrew Lewycky void *backing_store; 132f3a39818SAndrew Lewycky struct signal_page *page; 133f3a39818SAndrew Lewycky 134f3a39818SAndrew Lewycky page = kzalloc(SIGNAL_PAGE_SIZE, GFP_KERNEL); 135f3a39818SAndrew Lewycky if (!page) 136f3a39818SAndrew Lewycky goto fail_alloc_signal_page; 137f3a39818SAndrew Lewycky 138f3a39818SAndrew Lewycky page->free_slots = SLOTS_PER_PAGE; 139f3a39818SAndrew Lewycky 140f3a39818SAndrew Lewycky backing_store = (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO, 141f3a39818SAndrew Lewycky get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); 142f3a39818SAndrew Lewycky if (!backing_store) 143f3a39818SAndrew Lewycky goto fail_alloc_signal_store; 144f3a39818SAndrew Lewycky 145f3a39818SAndrew Lewycky /* prevent user-mode info leaks */ 146f3a39818SAndrew Lewycky memset(backing_store, (uint8_t) UNSIGNALED_EVENT_SLOT, 147f3a39818SAndrew Lewycky KFD_SIGNAL_EVENT_LIMIT * 8); 148f3a39818SAndrew Lewycky 149f3a39818SAndrew Lewycky page->kernel_address = backing_store; 150f3a39818SAndrew Lewycky 151f3a39818SAndrew Lewycky if (list_empty(&p->signal_event_pages)) 152f3a39818SAndrew Lewycky page->page_index = 0; 153f3a39818SAndrew Lewycky else 154f3a39818SAndrew Lewycky page->page_index = list_tail_entry(&p->signal_event_pages, 155f3a39818SAndrew Lewycky struct signal_page, 156f3a39818SAndrew Lewycky event_pages)->page_index + 1; 157f3a39818SAndrew Lewycky 158f3a39818SAndrew Lewycky pr_debug("allocated new event signal page at %p, for process %p\n", 159f3a39818SAndrew Lewycky page, p); 160f3a39818SAndrew Lewycky pr_debug("page index is %d\n", page->page_index); 161f3a39818SAndrew Lewycky 162f3a39818SAndrew Lewycky list_add(&page->event_pages, &p->signal_event_pages); 163f3a39818SAndrew Lewycky 164f3a39818SAndrew Lewycky return true; 165f3a39818SAndrew Lewycky 166f3a39818SAndrew Lewycky fail_alloc_signal_store: 167f3a39818SAndrew Lewycky kfree(page); 168f3a39818SAndrew Lewycky fail_alloc_signal_page: 169f3a39818SAndrew Lewycky return false; 170f3a39818SAndrew Lewycky } 171f3a39818SAndrew Lewycky 172f3a39818SAndrew Lewycky static bool allocate_event_notification_slot(struct file *devkfd, 173f3a39818SAndrew Lewycky struct kfd_process *p, 174f3a39818SAndrew Lewycky struct signal_page **page, 175f3a39818SAndrew Lewycky unsigned int *signal_slot_index) 176f3a39818SAndrew Lewycky { 177f3a39818SAndrew Lewycky bool ret; 178f3a39818SAndrew Lewycky 179f3a39818SAndrew Lewycky ret = allocate_free_slot(p, page, signal_slot_index); 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 197f3a39818SAndrew Lewycky * and reused until it exits. */ 198f3a39818SAndrew Lewycky } 199f3a39818SAndrew Lewycky 200f3a39818SAndrew Lewycky static struct signal_page *lookup_signal_page_by_index(struct kfd_process *p, 201f3a39818SAndrew Lewycky unsigned int page_index) 202f3a39818SAndrew Lewycky { 203f3a39818SAndrew Lewycky struct signal_page *page; 204f3a39818SAndrew Lewycky 205f3a39818SAndrew Lewycky /* 206f3a39818SAndrew Lewycky * This is safe because we don't delete signal pages until the 207f3a39818SAndrew Lewycky * process exits. 208f3a39818SAndrew Lewycky */ 209f3a39818SAndrew Lewycky list_for_each_entry(page, &p->signal_event_pages, event_pages) 210f3a39818SAndrew Lewycky if (page->page_index == page_index) 211f3a39818SAndrew Lewycky return page; 212f3a39818SAndrew Lewycky 213f3a39818SAndrew Lewycky return NULL; 214f3a39818SAndrew Lewycky } 215f3a39818SAndrew Lewycky 216f3a39818SAndrew Lewycky /* 217f3a39818SAndrew Lewycky * Assumes that p->event_mutex is held and of course that p is not going 218f3a39818SAndrew Lewycky * away (current or locked). 219f3a39818SAndrew Lewycky */ 220f3a39818SAndrew Lewycky static struct kfd_event *lookup_event_by_id(struct kfd_process *p, uint32_t id) 221f3a39818SAndrew Lewycky { 222f3a39818SAndrew Lewycky struct kfd_event *ev; 223f3a39818SAndrew Lewycky 224f3a39818SAndrew Lewycky hash_for_each_possible(p->events, ev, events, id) 225f3a39818SAndrew Lewycky if (ev->event_id == id) 226f3a39818SAndrew Lewycky return ev; 227f3a39818SAndrew Lewycky 228f3a39818SAndrew Lewycky return NULL; 229f3a39818SAndrew Lewycky } 230f3a39818SAndrew Lewycky 231f3a39818SAndrew Lewycky static u32 make_signal_event_id(struct signal_page *page, 232f3a39818SAndrew Lewycky unsigned int signal_slot_index) 233f3a39818SAndrew Lewycky { 234f3a39818SAndrew Lewycky return page->page_index | 235f3a39818SAndrew Lewycky (signal_slot_index << SIGNAL_EVENT_ID_SLOT_SHIFT); 236f3a39818SAndrew Lewycky } 237f3a39818SAndrew Lewycky 238f3a39818SAndrew Lewycky /* 239f3a39818SAndrew Lewycky * Produce a kfd event id for a nonsignal event. 240f3a39818SAndrew Lewycky * These are arbitrary numbers, so we do a sequential search through 241f3a39818SAndrew Lewycky * the hash table for an unused number. 242f3a39818SAndrew Lewycky */ 243f3a39818SAndrew Lewycky static u32 make_nonsignal_event_id(struct kfd_process *p) 244f3a39818SAndrew Lewycky { 245f3a39818SAndrew Lewycky u32 id; 246f3a39818SAndrew Lewycky 247f3a39818SAndrew Lewycky for (id = p->next_nonsignal_event_id; 248f3a39818SAndrew Lewycky id < KFD_LAST_NONSIGNAL_EVENT_ID && 249f3a39818SAndrew Lewycky lookup_event_by_id(p, id) != NULL; 250f3a39818SAndrew Lewycky id++) 251f3a39818SAndrew Lewycky ; 252f3a39818SAndrew Lewycky 253f3a39818SAndrew Lewycky if (id < KFD_LAST_NONSIGNAL_EVENT_ID) { 254f3a39818SAndrew Lewycky 255f3a39818SAndrew Lewycky /* 256f3a39818SAndrew Lewycky * What if id == LAST_NONSIGNAL_EVENT_ID - 1? 257f3a39818SAndrew Lewycky * Then next_nonsignal_event_id = LAST_NONSIGNAL_EVENT_ID so 258f3a39818SAndrew Lewycky * the first loop fails immediately and we proceed with the 259f3a39818SAndrew Lewycky * wraparound loop below. 260f3a39818SAndrew Lewycky */ 261f3a39818SAndrew Lewycky p->next_nonsignal_event_id = id + 1; 262f3a39818SAndrew Lewycky 263f3a39818SAndrew Lewycky return id; 264f3a39818SAndrew Lewycky } 265f3a39818SAndrew Lewycky 266f3a39818SAndrew Lewycky for (id = KFD_FIRST_NONSIGNAL_EVENT_ID; 267f3a39818SAndrew Lewycky id < KFD_LAST_NONSIGNAL_EVENT_ID && 268f3a39818SAndrew Lewycky lookup_event_by_id(p, id) != NULL; 269f3a39818SAndrew Lewycky id++) 270f3a39818SAndrew Lewycky ; 271f3a39818SAndrew Lewycky 272f3a39818SAndrew Lewycky 273f3a39818SAndrew Lewycky if (id < KFD_LAST_NONSIGNAL_EVENT_ID) { 274f3a39818SAndrew Lewycky p->next_nonsignal_event_id = id + 1; 275f3a39818SAndrew Lewycky return id; 276f3a39818SAndrew Lewycky } 277f3a39818SAndrew Lewycky 278f3a39818SAndrew Lewycky p->next_nonsignal_event_id = KFD_FIRST_NONSIGNAL_EVENT_ID; 279f3a39818SAndrew Lewycky return 0; 280f3a39818SAndrew Lewycky } 281f3a39818SAndrew Lewycky 282f3a39818SAndrew Lewycky static struct kfd_event *lookup_event_by_page_slot(struct kfd_process *p, 283f3a39818SAndrew Lewycky struct signal_page *page, 284f3a39818SAndrew Lewycky unsigned int signal_slot) 285f3a39818SAndrew Lewycky { 286f3a39818SAndrew Lewycky return lookup_event_by_id(p, make_signal_event_id(page, signal_slot)); 287f3a39818SAndrew Lewycky } 288f3a39818SAndrew Lewycky 289f3a39818SAndrew Lewycky static int create_signal_event(struct file *devkfd, 290f3a39818SAndrew Lewycky struct kfd_process *p, 291f3a39818SAndrew Lewycky struct kfd_event *ev) 292f3a39818SAndrew Lewycky { 293f3a39818SAndrew Lewycky if (p->signal_event_count == KFD_SIGNAL_EVENT_LIMIT) { 294f3a39818SAndrew Lewycky pr_warn("amdkfd: Signal event wasn't created because limit was reached\n"); 295f3a39818SAndrew Lewycky return -ENOMEM; 296f3a39818SAndrew Lewycky } 297f3a39818SAndrew Lewycky 298f3a39818SAndrew Lewycky if (!allocate_event_notification_slot(devkfd, p, &ev->signal_page, 299f3a39818SAndrew Lewycky &ev->signal_slot_index)) { 300f3a39818SAndrew Lewycky pr_warn("amdkfd: Signal event wasn't created because out of kernel memory\n"); 301f3a39818SAndrew Lewycky return -ENOMEM; 302f3a39818SAndrew Lewycky } 303f3a39818SAndrew Lewycky 304f3a39818SAndrew Lewycky p->signal_event_count++; 305f3a39818SAndrew Lewycky 306f3a39818SAndrew Lewycky ev->user_signal_address = 307f3a39818SAndrew Lewycky &ev->signal_page->user_address[ev->signal_slot_index]; 308f3a39818SAndrew Lewycky 309f3a39818SAndrew Lewycky ev->event_id = make_signal_event_id(ev->signal_page, 310f3a39818SAndrew Lewycky ev->signal_slot_index); 311f3a39818SAndrew Lewycky 312f3a39818SAndrew Lewycky pr_debug("signal event number %zu created with id %d, address %p\n", 313f3a39818SAndrew Lewycky p->signal_event_count, ev->event_id, 314f3a39818SAndrew Lewycky ev->user_signal_address); 315f3a39818SAndrew Lewycky 3166235e15eSOded Gabbay 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 { 348f3a39818SAndrew Lewycky if (ev->signal_page != NULL) { 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; 587f3a39818SAndrew Lewycky unsigned 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 620f3a39818SAndrew Lewycky static int init_event_waiter(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 list_add(&waiter->waiters, &ev->waiters); 636f3a39818SAndrew Lewycky 637f3a39818SAndrew Lewycky return 0; 638f3a39818SAndrew Lewycky } 639f3a39818SAndrew Lewycky 640f3a39818SAndrew Lewycky static bool test_event_condition(bool all, uint32_t num_events, 641f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters) 642f3a39818SAndrew Lewycky { 643f3a39818SAndrew Lewycky uint32_t i; 644f3a39818SAndrew Lewycky uint32_t activated_count = 0; 645f3a39818SAndrew Lewycky 646f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 647f3a39818SAndrew Lewycky if (event_waiters[i].activated) { 648f3a39818SAndrew Lewycky if (!all) 649f3a39818SAndrew Lewycky return true; 650f3a39818SAndrew Lewycky 651f3a39818SAndrew Lewycky activated_count++; 652f3a39818SAndrew Lewycky } 653f3a39818SAndrew Lewycky } 654f3a39818SAndrew Lewycky 655f3a39818SAndrew Lewycky return activated_count == num_events; 656f3a39818SAndrew Lewycky } 657f3a39818SAndrew Lewycky 65859d3e8beSAlexey Skidanov /* 65959d3e8beSAlexey Skidanov * Copy event specific data, if defined. 66059d3e8beSAlexey Skidanov * Currently only memory exception events have additional data to copy to user 66159d3e8beSAlexey Skidanov */ 66259d3e8beSAlexey Skidanov static bool copy_signaled_event_data(uint32_t num_events, 66359d3e8beSAlexey Skidanov struct kfd_event_waiter *event_waiters, 66459d3e8beSAlexey Skidanov struct kfd_event_data __user *data) 66559d3e8beSAlexey Skidanov { 66659d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *src; 66759d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data __user *dst; 66859d3e8beSAlexey Skidanov struct kfd_event_waiter *waiter; 66959d3e8beSAlexey Skidanov struct kfd_event *event; 67059d3e8beSAlexey Skidanov uint32_t i; 67159d3e8beSAlexey Skidanov 67259d3e8beSAlexey Skidanov for (i = 0; i < num_events; i++) { 67359d3e8beSAlexey Skidanov waiter = &event_waiters[i]; 67459d3e8beSAlexey Skidanov event = waiter->event; 67559d3e8beSAlexey Skidanov if (waiter->activated && event->type == KFD_EVENT_TYPE_MEMORY) { 67659d3e8beSAlexey Skidanov dst = &data[waiter->input_index].memory_exception_data; 67759d3e8beSAlexey Skidanov src = &event->memory_exception_data; 67859d3e8beSAlexey Skidanov if (copy_to_user(dst, src, 67959d3e8beSAlexey Skidanov sizeof(struct kfd_hsa_memory_exception_data))) 68059d3e8beSAlexey Skidanov return false; 68159d3e8beSAlexey Skidanov } 68259d3e8beSAlexey Skidanov } 68359d3e8beSAlexey Skidanov 68459d3e8beSAlexey Skidanov return true; 68559d3e8beSAlexey Skidanov 68659d3e8beSAlexey Skidanov } 68759d3e8beSAlexey Skidanov 68859d3e8beSAlexey Skidanov 68959d3e8beSAlexey Skidanov 690f3a39818SAndrew Lewycky static long user_timeout_to_jiffies(uint32_t user_timeout_ms) 691f3a39818SAndrew Lewycky { 692f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE) 693f3a39818SAndrew Lewycky return 0; 694f3a39818SAndrew Lewycky 695f3a39818SAndrew Lewycky if (user_timeout_ms == KFD_EVENT_TIMEOUT_INFINITE) 696f3a39818SAndrew Lewycky return MAX_SCHEDULE_TIMEOUT; 697f3a39818SAndrew Lewycky 698f3a39818SAndrew Lewycky /* 699f3a39818SAndrew Lewycky * msecs_to_jiffies interprets all values above 2^31-1 as infinite, 700f3a39818SAndrew Lewycky * but we consider them finite. 701f3a39818SAndrew Lewycky * This hack is wrong, but nobody is likely to notice. 702f3a39818SAndrew Lewycky */ 703f3a39818SAndrew Lewycky user_timeout_ms = min_t(uint32_t, user_timeout_ms, 0x7FFFFFFF); 704f3a39818SAndrew Lewycky 705f3a39818SAndrew Lewycky return msecs_to_jiffies(user_timeout_ms) + 1; 706f3a39818SAndrew Lewycky } 707f3a39818SAndrew Lewycky 708f3a39818SAndrew Lewycky static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters) 709f3a39818SAndrew Lewycky { 710f3a39818SAndrew Lewycky uint32_t i; 711f3a39818SAndrew Lewycky 712f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) 713f3a39818SAndrew Lewycky list_del(&waiters[i].waiters); 714f3a39818SAndrew Lewycky 715f3a39818SAndrew Lewycky kfree(waiters); 716f3a39818SAndrew Lewycky } 717f3a39818SAndrew Lewycky 718f3a39818SAndrew Lewycky int kfd_wait_on_events(struct kfd_process *p, 71959d3e8beSAlexey Skidanov uint32_t num_events, void __user *data, 720f3a39818SAndrew Lewycky bool all, uint32_t user_timeout_ms, 721f3a39818SAndrew Lewycky enum kfd_event_wait_result *wait_result) 722f3a39818SAndrew Lewycky { 72359d3e8beSAlexey Skidanov struct kfd_event_data __user *events = 72459d3e8beSAlexey Skidanov (struct kfd_event_data __user *) data; 725f3a39818SAndrew Lewycky uint32_t i; 726f3a39818SAndrew Lewycky int ret = 0; 727f3a39818SAndrew Lewycky struct kfd_event_waiter *event_waiters = NULL; 728f3a39818SAndrew Lewycky long timeout = user_timeout_to_jiffies(user_timeout_ms); 729f3a39818SAndrew Lewycky 730f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 731f3a39818SAndrew Lewycky 732f3a39818SAndrew Lewycky event_waiters = alloc_event_waiters(num_events); 733f3a39818SAndrew Lewycky if (!event_waiters) { 734f3a39818SAndrew Lewycky ret = -ENOMEM; 735f3a39818SAndrew Lewycky goto fail; 736f3a39818SAndrew Lewycky } 737f3a39818SAndrew Lewycky 738f3a39818SAndrew Lewycky for (i = 0; i < num_events; i++) { 73959d3e8beSAlexey Skidanov struct kfd_event_data event_data; 740f3a39818SAndrew Lewycky 74159d3e8beSAlexey Skidanov if (copy_from_user(&event_data, &events[i], 742*8bf79388SPan Bian sizeof(struct kfd_event_data))) { 743*8bf79388SPan Bian ret = -EFAULT; 744f3a39818SAndrew Lewycky goto fail; 745*8bf79388SPan Bian } 746f3a39818SAndrew Lewycky 74759d3e8beSAlexey Skidanov ret = init_event_waiter(p, &event_waiters[i], 74859d3e8beSAlexey Skidanov event_data.event_id, i); 749f3a39818SAndrew Lewycky if (ret) 750f3a39818SAndrew Lewycky goto fail; 751f3a39818SAndrew Lewycky } 752f3a39818SAndrew Lewycky 753f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 754f3a39818SAndrew Lewycky 755f3a39818SAndrew Lewycky while (true) { 756f3a39818SAndrew Lewycky if (fatal_signal_pending(current)) { 757f3a39818SAndrew Lewycky ret = -EINTR; 758f3a39818SAndrew Lewycky break; 759f3a39818SAndrew Lewycky } 760f3a39818SAndrew Lewycky 761f3a39818SAndrew Lewycky if (signal_pending(current)) { 762f3a39818SAndrew Lewycky /* 763f3a39818SAndrew Lewycky * This is wrong when a nonzero, non-infinite timeout 764f3a39818SAndrew Lewycky * is specified. We need to use 765f3a39818SAndrew Lewycky * ERESTARTSYS_RESTARTBLOCK, but struct restart_block 766f3a39818SAndrew Lewycky * contains a union with data for each user and it's 767f3a39818SAndrew Lewycky * in generic kernel code that I don't want to 768f3a39818SAndrew Lewycky * touch yet. 769f3a39818SAndrew Lewycky */ 770f3a39818SAndrew Lewycky ret = -ERESTARTSYS; 771f3a39818SAndrew Lewycky break; 772f3a39818SAndrew Lewycky } 773f3a39818SAndrew Lewycky 774f3a39818SAndrew Lewycky if (test_event_condition(all, num_events, event_waiters)) { 77559d3e8beSAlexey Skidanov if (copy_signaled_event_data(num_events, 77659d3e8beSAlexey Skidanov event_waiters, events)) 777f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_COMPLETE; 77859d3e8beSAlexey Skidanov else 77959d3e8beSAlexey Skidanov *wait_result = KFD_WAIT_ERROR; 780f3a39818SAndrew Lewycky break; 781f3a39818SAndrew Lewycky } 782f3a39818SAndrew Lewycky 783f3a39818SAndrew Lewycky if (timeout <= 0) { 784f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_TIMEOUT; 785f3a39818SAndrew Lewycky break; 786f3a39818SAndrew Lewycky } 787f3a39818SAndrew Lewycky 788f3a39818SAndrew Lewycky timeout = schedule_timeout_interruptible(timeout); 789f3a39818SAndrew Lewycky } 790f3a39818SAndrew Lewycky __set_current_state(TASK_RUNNING); 791f3a39818SAndrew Lewycky 792f3a39818SAndrew Lewycky mutex_lock(&p->event_mutex); 793f3a39818SAndrew Lewycky free_waiters(num_events, event_waiters); 794f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 795f3a39818SAndrew Lewycky 796f3a39818SAndrew Lewycky return ret; 797f3a39818SAndrew Lewycky 798f3a39818SAndrew Lewycky fail: 799f3a39818SAndrew Lewycky if (event_waiters) 800f3a39818SAndrew Lewycky free_waiters(num_events, event_waiters); 801f3a39818SAndrew Lewycky 802f3a39818SAndrew Lewycky mutex_unlock(&p->event_mutex); 803f3a39818SAndrew Lewycky 804f3a39818SAndrew Lewycky *wait_result = KFD_WAIT_ERROR; 805f3a39818SAndrew Lewycky 806f3a39818SAndrew Lewycky return ret; 807f3a39818SAndrew Lewycky } 808f3a39818SAndrew Lewycky 809f3a39818SAndrew Lewycky int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma) 810f3a39818SAndrew Lewycky { 811f3a39818SAndrew Lewycky 812f3a39818SAndrew Lewycky unsigned int page_index; 813f3a39818SAndrew Lewycky unsigned long pfn; 814f3a39818SAndrew Lewycky struct signal_page *page; 815f3a39818SAndrew Lewycky 816f3a39818SAndrew Lewycky /* check required size is logical */ 817f3a39818SAndrew Lewycky if (get_order(KFD_SIGNAL_EVENT_LIMIT * 8) != 818f3a39818SAndrew Lewycky get_order(vma->vm_end - vma->vm_start)) { 819f3a39818SAndrew Lewycky pr_err("amdkfd: event page mmap requested illegal size\n"); 820f3a39818SAndrew Lewycky return -EINVAL; 821f3a39818SAndrew Lewycky } 822f3a39818SAndrew Lewycky 823f3a39818SAndrew Lewycky page_index = vma->vm_pgoff; 824f3a39818SAndrew Lewycky 825f3a39818SAndrew Lewycky page = lookup_signal_page_by_index(p, page_index); 826f3a39818SAndrew Lewycky if (!page) { 827f3a39818SAndrew Lewycky /* Probably KFD bug, but mmap is user-accessible. */ 828f3a39818SAndrew Lewycky pr_debug("signal page could not be found for page_index %u\n", 829f3a39818SAndrew Lewycky page_index); 830f3a39818SAndrew Lewycky return -EINVAL; 831f3a39818SAndrew Lewycky } 832f3a39818SAndrew Lewycky 833f3a39818SAndrew Lewycky pfn = __pa(page->kernel_address); 834f3a39818SAndrew Lewycky pfn >>= PAGE_SHIFT; 835f3a39818SAndrew Lewycky 836f3a39818SAndrew Lewycky vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE 837f3a39818SAndrew Lewycky | VM_DONTDUMP | VM_PFNMAP; 838f3a39818SAndrew Lewycky 839f3a39818SAndrew Lewycky pr_debug("mapping signal page\n"); 840f3a39818SAndrew Lewycky pr_debug(" start user address == 0x%08lx\n", vma->vm_start); 841f3a39818SAndrew Lewycky pr_debug(" end user address == 0x%08lx\n", vma->vm_end); 842f3a39818SAndrew Lewycky pr_debug(" pfn == 0x%016lX\n", pfn); 843f3a39818SAndrew Lewycky pr_debug(" vm_flags == 0x%08lX\n", vma->vm_flags); 844f3a39818SAndrew Lewycky pr_debug(" size == 0x%08lX\n", 845f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start); 846f3a39818SAndrew Lewycky 847f3a39818SAndrew Lewycky page->user_address = (uint64_t __user *)vma->vm_start; 848f3a39818SAndrew Lewycky 849f3a39818SAndrew Lewycky /* mapping the page to user process */ 850f3a39818SAndrew Lewycky return remap_pfn_range(vma, vma->vm_start, pfn, 851f3a39818SAndrew Lewycky vma->vm_end - vma->vm_start, vma->vm_page_prot); 852f3a39818SAndrew Lewycky } 85359d3e8beSAlexey Skidanov 85459d3e8beSAlexey Skidanov /* 85559d3e8beSAlexey Skidanov * Assumes that p->event_mutex is held and of course 85659d3e8beSAlexey Skidanov * that p is not going away (current or locked). 85759d3e8beSAlexey Skidanov */ 85859d3e8beSAlexey Skidanov static void lookup_events_by_type_and_signal(struct kfd_process *p, 85959d3e8beSAlexey Skidanov int type, void *event_data) 86059d3e8beSAlexey Skidanov { 86159d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data *ev_data; 86259d3e8beSAlexey Skidanov struct kfd_event *ev; 86359d3e8beSAlexey Skidanov int bkt; 86459d3e8beSAlexey Skidanov bool send_signal = true; 86559d3e8beSAlexey Skidanov 86659d3e8beSAlexey Skidanov ev_data = (struct kfd_hsa_memory_exception_data *) event_data; 86759d3e8beSAlexey Skidanov 86859d3e8beSAlexey Skidanov hash_for_each(p->events, bkt, ev, events) 86959d3e8beSAlexey Skidanov if (ev->type == type) { 87059d3e8beSAlexey Skidanov send_signal = false; 87159d3e8beSAlexey Skidanov dev_dbg(kfd_device, 87259d3e8beSAlexey Skidanov "Event found: id %X type %d", 87359d3e8beSAlexey Skidanov ev->event_id, ev->type); 87459d3e8beSAlexey Skidanov set_event(ev); 87559d3e8beSAlexey Skidanov if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data) 87659d3e8beSAlexey Skidanov ev->memory_exception_data = *ev_data; 87759d3e8beSAlexey Skidanov } 87859d3e8beSAlexey Skidanov 87959d3e8beSAlexey Skidanov /* Send SIGTERM no event of type "type" has been found*/ 88059d3e8beSAlexey Skidanov if (send_signal) { 88181663016SOded Gabbay if (send_sigterm) { 88259d3e8beSAlexey Skidanov dev_warn(kfd_device, 88359d3e8beSAlexey Skidanov "Sending SIGTERM to HSA Process with PID %d ", 88459d3e8beSAlexey Skidanov p->lead_thread->pid); 88559d3e8beSAlexey Skidanov send_sig(SIGTERM, p->lead_thread, 0); 88681663016SOded Gabbay } else { 88781663016SOded Gabbay dev_err(kfd_device, 88881663016SOded Gabbay "HSA Process (PID %d) got unhandled exception", 88981663016SOded Gabbay p->lead_thread->pid); 89081663016SOded Gabbay } 89159d3e8beSAlexey Skidanov } 89259d3e8beSAlexey Skidanov } 89359d3e8beSAlexey Skidanov 89459d3e8beSAlexey Skidanov void kfd_signal_iommu_event(struct kfd_dev *dev, unsigned int pasid, 89559d3e8beSAlexey Skidanov unsigned long address, bool is_write_requested, 89659d3e8beSAlexey Skidanov bool is_execute_requested) 89759d3e8beSAlexey Skidanov { 89859d3e8beSAlexey Skidanov struct kfd_hsa_memory_exception_data memory_exception_data; 89959d3e8beSAlexey Skidanov struct vm_area_struct *vma; 90059d3e8beSAlexey Skidanov 90159d3e8beSAlexey Skidanov /* 90259d3e8beSAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 90359d3e8beSAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 90459d3e8beSAlexey Skidanov * running so the lookup function returns a locked process. 90559d3e8beSAlexey Skidanov */ 90659d3e8beSAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 90759d3e8beSAlexey Skidanov 90859d3e8beSAlexey Skidanov if (!p) 90959d3e8beSAlexey Skidanov return; /* Presumably process exited. */ 91059d3e8beSAlexey Skidanov 91159d3e8beSAlexey Skidanov memset(&memory_exception_data, 0, sizeof(memory_exception_data)); 91259d3e8beSAlexey Skidanov 91359d3e8beSAlexey Skidanov down_read(&p->mm->mmap_sem); 91459d3e8beSAlexey Skidanov vma = find_vma(p->mm, address); 91559d3e8beSAlexey Skidanov 91659d3e8beSAlexey Skidanov memory_exception_data.gpu_id = dev->id; 91759d3e8beSAlexey Skidanov memory_exception_data.va = address; 91859d3e8beSAlexey Skidanov /* Set failure reason */ 91959d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 92059d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 92159d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 92259d3e8beSAlexey Skidanov if (vma) { 92359d3e8beSAlexey Skidanov if (vma->vm_start > address) { 92459d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 1; 92559d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 92659d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 92759d3e8beSAlexey Skidanov } else { 92859d3e8beSAlexey Skidanov memory_exception_data.failure.NotPresent = 0; 92959d3e8beSAlexey Skidanov if (is_write_requested && !(vma->vm_flags & VM_WRITE)) 93059d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 1; 93159d3e8beSAlexey Skidanov else 93259d3e8beSAlexey Skidanov memory_exception_data.failure.ReadOnly = 0; 93359d3e8beSAlexey Skidanov if (is_execute_requested && !(vma->vm_flags & VM_EXEC)) 93459d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 1; 93559d3e8beSAlexey Skidanov else 93659d3e8beSAlexey Skidanov memory_exception_data.failure.NoExecute = 0; 93759d3e8beSAlexey Skidanov } 93859d3e8beSAlexey Skidanov } 93959d3e8beSAlexey Skidanov 94059d3e8beSAlexey Skidanov up_read(&p->mm->mmap_sem); 94159d3e8beSAlexey Skidanov 94259d3e8beSAlexey Skidanov mutex_lock(&p->event_mutex); 94359d3e8beSAlexey Skidanov 94459d3e8beSAlexey Skidanov /* Lookup events by type and signal them */ 94559d3e8beSAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY, 94659d3e8beSAlexey Skidanov &memory_exception_data); 94759d3e8beSAlexey Skidanov 94859d3e8beSAlexey Skidanov mutex_unlock(&p->event_mutex); 94959d3e8beSAlexey Skidanov mutex_unlock(&p->mutex); 95059d3e8beSAlexey Skidanov } 951930c5ff4SAlexey Skidanov 952930c5ff4SAlexey Skidanov void kfd_signal_hw_exception_event(unsigned int pasid) 953930c5ff4SAlexey Skidanov { 954930c5ff4SAlexey Skidanov /* 955930c5ff4SAlexey Skidanov * Because we are called from arbitrary context (workqueue) as opposed 956930c5ff4SAlexey Skidanov * to process context, kfd_process could attempt to exit while we are 957930c5ff4SAlexey Skidanov * running so the lookup function returns a locked process. 958930c5ff4SAlexey Skidanov */ 959930c5ff4SAlexey Skidanov struct kfd_process *p = kfd_lookup_process_by_pasid(pasid); 960930c5ff4SAlexey Skidanov 961930c5ff4SAlexey Skidanov if (!p) 962930c5ff4SAlexey Skidanov return; /* Presumably process exited. */ 963930c5ff4SAlexey Skidanov 964930c5ff4SAlexey Skidanov mutex_lock(&p->event_mutex); 965930c5ff4SAlexey Skidanov 966930c5ff4SAlexey Skidanov /* Lookup events by type and signal them */ 967930c5ff4SAlexey Skidanov lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_HW_EXCEPTION, NULL); 968930c5ff4SAlexey Skidanov 969930c5ff4SAlexey Skidanov mutex_unlock(&p->event_mutex); 970930c5ff4SAlexey Skidanov mutex_unlock(&p->mutex); 971930c5ff4SAlexey Skidanov } 972