1145eba1aSCai Huoqing // SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause 2f48ad614SDennis Dalessandro /* 33d2a9d64SDennis Dalessandro * Copyright(c) 2020 Cornelis Networks, Inc. 434ab4de7SMichael J. Ruhl * Copyright(c) 2016 - 2017 Intel Corporation. 5f48ad614SDennis Dalessandro */ 6145eba1aSCai Huoqing 7f48ad614SDennis Dalessandro #include <linux/list.h> 8f48ad614SDennis Dalessandro #include <linux/rculist.h> 9f48ad614SDennis Dalessandro #include <linux/mmu_notifier.h> 10f48ad614SDennis Dalessandro #include <linux/interval_tree_generic.h> 113d2a9d64SDennis Dalessandro #include <linux/sched/mm.h> 12f48ad614SDennis Dalessandro 13f48ad614SDennis Dalessandro #include "mmu_rb.h" 14f48ad614SDennis Dalessandro #include "trace.h" 15f48ad614SDennis Dalessandro 16f48ad614SDennis Dalessandro static unsigned long mmu_node_start(struct mmu_rb_node *); 17f48ad614SDennis Dalessandro static unsigned long mmu_node_last(struct mmu_rb_node *); 1893065ac7SMichal Hocko static int mmu_notifier_range_start(struct mmu_notifier *, 195d6527a7SJérôme Glisse const struct mmu_notifier_range *); 20f48ad614SDennis Dalessandro static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *, 21f48ad614SDennis Dalessandro unsigned long, unsigned long); 22b85ced91SDean Luick static void do_remove(struct mmu_rb_handler *handler, 23b85ced91SDean Luick struct list_head *del_list); 24b85ced91SDean Luick static void handle_remove(struct work_struct *work); 25f48ad614SDennis Dalessandro 260fc859a6SBhumika Goyal static const struct mmu_notifier_ops mn_opts = { 27f48ad614SDennis Dalessandro .invalidate_range_start = mmu_notifier_range_start, 28f48ad614SDennis Dalessandro }; 29f48ad614SDennis Dalessandro 30f48ad614SDennis Dalessandro INTERVAL_TREE_DEFINE(struct mmu_rb_node, node, unsigned long, __last, 31f48ad614SDennis Dalessandro mmu_node_start, mmu_node_last, static, __mmu_int_rb); 32f48ad614SDennis Dalessandro 33f48ad614SDennis Dalessandro static unsigned long mmu_node_start(struct mmu_rb_node *node) 34f48ad614SDennis Dalessandro { 35f48ad614SDennis Dalessandro return node->addr & PAGE_MASK; 36f48ad614SDennis Dalessandro } 37f48ad614SDennis Dalessandro 38f48ad614SDennis Dalessandro static unsigned long mmu_node_last(struct mmu_rb_node *node) 39f48ad614SDennis Dalessandro { 40f48ad614SDennis Dalessandro return PAGE_ALIGN(node->addr + node->len) - 1; 41f48ad614SDennis Dalessandro } 42f48ad614SDennis Dalessandro 433d2a9d64SDennis Dalessandro int hfi1_mmu_rb_register(void *ops_arg, 44e0b09ac5SDean Luick struct mmu_rb_ops *ops, 45b85ced91SDean Luick struct workqueue_struct *wq, 46e0b09ac5SDean Luick struct mmu_rb_handler **handler) 47f48ad614SDennis Dalessandro { 483d2a9d64SDennis Dalessandro struct mmu_rb_handler *h; 493faa3d9aSIra Weiny int ret; 50f48ad614SDennis Dalessandro 51ca5f7256SMike Marciniszyn h = kzalloc(sizeof(*h), GFP_KERNEL); 523d2a9d64SDennis Dalessandro if (!h) 53f48ad614SDennis Dalessandro return -ENOMEM; 54f48ad614SDennis Dalessandro 553d2a9d64SDennis Dalessandro h->root = RB_ROOT_CACHED; 563d2a9d64SDennis Dalessandro h->ops = ops; 573d2a9d64SDennis Dalessandro h->ops_arg = ops_arg; 583d2a9d64SDennis Dalessandro INIT_HLIST_NODE(&h->mn.hlist); 593d2a9d64SDennis Dalessandro spin_lock_init(&h->lock); 603d2a9d64SDennis Dalessandro h->mn.ops = &mn_opts; 613d2a9d64SDennis Dalessandro INIT_WORK(&h->del_work, handle_remove); 623d2a9d64SDennis Dalessandro INIT_LIST_HEAD(&h->del_list); 633d2a9d64SDennis Dalessandro INIT_LIST_HEAD(&h->lru_list); 643d2a9d64SDennis Dalessandro h->wq = wq; 653faa3d9aSIra Weiny 663d2a9d64SDennis Dalessandro ret = mmu_notifier_register(&h->mn, current->mm); 673faa3d9aSIra Weiny if (ret) { 683d2a9d64SDennis Dalessandro kfree(h); 693faa3d9aSIra Weiny return ret; 703faa3d9aSIra Weiny } 713faa3d9aSIra Weiny 723d2a9d64SDennis Dalessandro *handler = h; 73e0b09ac5SDean Luick return 0; 74f48ad614SDennis Dalessandro } 75f48ad614SDennis Dalessandro 76e0b09ac5SDean Luick void hfi1_mmu_rb_unregister(struct mmu_rb_handler *handler) 77f48ad614SDennis Dalessandro { 7820a42d08SDean Luick struct mmu_rb_node *rbnode; 7920a42d08SDean Luick struct rb_node *node; 80f48ad614SDennis Dalessandro unsigned long flags; 81b85ced91SDean Luick struct list_head del_list; 82f48ad614SDennis Dalessandro 83*2bbac98dSDouglas Miller /* Prevent freeing of mm until we are completely finished. */ 84*2bbac98dSDouglas Miller mmgrab(handler->mn.mm); 85*2bbac98dSDouglas Miller 86f48ad614SDennis Dalessandro /* Unregister first so we don't get any more notifications. */ 873d2a9d64SDennis Dalessandro mmu_notifier_unregister(&handler->mn, handler->mn.mm); 88f48ad614SDennis Dalessandro 89b85ced91SDean Luick /* 90b85ced91SDean Luick * Make sure the wq delete handler is finished running. It will not 91b85ced91SDean Luick * be triggered once the mmu notifiers are unregistered above. 92b85ced91SDean Luick */ 93b85ced91SDean Luick flush_work(&handler->del_work); 94b85ced91SDean Luick 95b85ced91SDean Luick INIT_LIST_HEAD(&del_list); 96b85ced91SDean Luick 97f48ad614SDennis Dalessandro spin_lock_irqsave(&handler->lock, flags); 98f808c13fSDavidlohr Bueso while ((node = rb_first_cached(&handler->root))) { 99f48ad614SDennis Dalessandro rbnode = rb_entry(node, struct mmu_rb_node, node); 100f808c13fSDavidlohr Bueso rb_erase_cached(node, &handler->root); 1010636e9abSDean Luick /* move from LRU list to delete list */ 1020636e9abSDean Luick list_move(&rbnode->list, &del_list); 103f48ad614SDennis Dalessandro } 104f48ad614SDennis Dalessandro spin_unlock_irqrestore(&handler->lock, flags); 105f48ad614SDennis Dalessandro 106b85ced91SDean Luick do_remove(handler, &del_list); 107b85ced91SDean Luick 108*2bbac98dSDouglas Miller /* Now the mm may be freed. */ 109*2bbac98dSDouglas Miller mmdrop(handler->mn.mm); 110*2bbac98dSDouglas Miller 111f48ad614SDennis Dalessandro kfree(handler); 112f48ad614SDennis Dalessandro } 113f48ad614SDennis Dalessandro 114e0b09ac5SDean Luick int hfi1_mmu_rb_insert(struct mmu_rb_handler *handler, 115e0b09ac5SDean Luick struct mmu_rb_node *mnode) 116f48ad614SDennis Dalessandro { 117f48ad614SDennis Dalessandro struct mmu_rb_node *node; 118f48ad614SDennis Dalessandro unsigned long flags; 119f48ad614SDennis Dalessandro int ret = 0; 120f48ad614SDennis Dalessandro 12134ab4de7SMichael J. Ruhl trace_hfi1_mmu_rb_insert(mnode->addr, mnode->len); 1223d2a9d64SDennis Dalessandro 1233d2a9d64SDennis Dalessandro if (current->mm != handler->mn.mm) 1243d2a9d64SDennis Dalessandro return -EPERM; 1253d2a9d64SDennis Dalessandro 126f48ad614SDennis Dalessandro spin_lock_irqsave(&handler->lock, flags); 127f48ad614SDennis Dalessandro node = __mmu_rb_search(handler, mnode->addr, mnode->len); 128f48ad614SDennis Dalessandro if (node) { 129f48ad614SDennis Dalessandro ret = -EINVAL; 130f48ad614SDennis Dalessandro goto unlock; 131f48ad614SDennis Dalessandro } 132e0b09ac5SDean Luick __mmu_int_rb_insert(mnode, &handler->root); 1330636e9abSDean Luick list_add(&mnode->list, &handler->lru_list); 134f48ad614SDennis Dalessandro 135e0b09ac5SDean Luick ret = handler->ops->insert(handler->ops_arg, mnode); 1360636e9abSDean Luick if (ret) { 137e0b09ac5SDean Luick __mmu_int_rb_remove(mnode, &handler->root); 1380636e9abSDean Luick list_del(&mnode->list); /* remove from LRU list */ 1390636e9abSDean Luick } 1403d2a9d64SDennis Dalessandro mnode->handler = handler; 141f48ad614SDennis Dalessandro unlock: 142f48ad614SDennis Dalessandro spin_unlock_irqrestore(&handler->lock, flags); 143f48ad614SDennis Dalessandro return ret; 144f48ad614SDennis Dalessandro } 145f48ad614SDennis Dalessandro 146f48ad614SDennis Dalessandro /* Caller must hold handler lock */ 147f48ad614SDennis Dalessandro static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *handler, 148f48ad614SDennis Dalessandro unsigned long addr, 149f48ad614SDennis Dalessandro unsigned long len) 150f48ad614SDennis Dalessandro { 151f48ad614SDennis Dalessandro struct mmu_rb_node *node = NULL; 152f48ad614SDennis Dalessandro 15334ab4de7SMichael J. Ruhl trace_hfi1_mmu_rb_search(addr, len); 154f48ad614SDennis Dalessandro if (!handler->ops->filter) { 155e0b09ac5SDean Luick node = __mmu_int_rb_iter_first(&handler->root, addr, 156f48ad614SDennis Dalessandro (addr + len) - 1); 157f48ad614SDennis Dalessandro } else { 158e0b09ac5SDean Luick for (node = __mmu_int_rb_iter_first(&handler->root, addr, 159f48ad614SDennis Dalessandro (addr + len) - 1); 160f48ad614SDennis Dalessandro node; 161f48ad614SDennis Dalessandro node = __mmu_int_rb_iter_next(node, addr, 162f48ad614SDennis Dalessandro (addr + len) - 1)) { 163f48ad614SDennis Dalessandro if (handler->ops->filter(node, addr, len)) 164f48ad614SDennis Dalessandro return node; 165f48ad614SDennis Dalessandro } 166f48ad614SDennis Dalessandro } 167f48ad614SDennis Dalessandro return node; 168f48ad614SDennis Dalessandro } 169f48ad614SDennis Dalessandro 1707be85676SSebastian Sanchez bool hfi1_mmu_rb_remove_unless_exact(struct mmu_rb_handler *handler, 1717be85676SSebastian Sanchez unsigned long addr, unsigned long len, 1727be85676SSebastian Sanchez struct mmu_rb_node **rb_node) 173f48ad614SDennis Dalessandro { 174f48ad614SDennis Dalessandro struct mmu_rb_node *node; 175f48ad614SDennis Dalessandro unsigned long flags; 1767be85676SSebastian Sanchez bool ret = false; 177f48ad614SDennis Dalessandro 1783d2a9d64SDennis Dalessandro if (current->mm != handler->mn.mm) 1793d2a9d64SDennis Dalessandro return ret; 1803d2a9d64SDennis Dalessandro 181f48ad614SDennis Dalessandro spin_lock_irqsave(&handler->lock, flags); 182f48ad614SDennis Dalessandro node = __mmu_rb_search(handler, addr, len); 1830636e9abSDean Luick if (node) { 1847be85676SSebastian Sanchez if (node->addr == addr && node->len == len) 1857be85676SSebastian Sanchez goto unlock; 186e0b09ac5SDean Luick __mmu_int_rb_remove(node, &handler->root); 1870636e9abSDean Luick list_del(&node->list); /* remove from LRU list */ 1887be85676SSebastian Sanchez ret = true; 1890636e9abSDean Luick } 1907be85676SSebastian Sanchez unlock: 191f48ad614SDennis Dalessandro spin_unlock_irqrestore(&handler->lock, flags); 1927be85676SSebastian Sanchez *rb_node = node; 1937be85676SSebastian Sanchez return ret; 194f48ad614SDennis Dalessandro } 195f48ad614SDennis Dalessandro 19610345998SDean Luick void hfi1_mmu_rb_evict(struct mmu_rb_handler *handler, void *evict_arg) 19710345998SDean Luick { 1980636e9abSDean Luick struct mmu_rb_node *rbnode, *ptr; 19910345998SDean Luick struct list_head del_list; 20010345998SDean Luick unsigned long flags; 20110345998SDean Luick bool stop = false; 20210345998SDean Luick 2033d2a9d64SDennis Dalessandro if (current->mm != handler->mn.mm) 2043d2a9d64SDennis Dalessandro return; 2053d2a9d64SDennis Dalessandro 20610345998SDean Luick INIT_LIST_HEAD(&del_list); 20710345998SDean Luick 20810345998SDean Luick spin_lock_irqsave(&handler->lock, flags); 2090636e9abSDean Luick list_for_each_entry_safe_reverse(rbnode, ptr, &handler->lru_list, 2100636e9abSDean Luick list) { 21110345998SDean Luick if (handler->ops->evict(handler->ops_arg, rbnode, evict_arg, 21210345998SDean Luick &stop)) { 21310345998SDean Luick __mmu_int_rb_remove(rbnode, &handler->root); 2140636e9abSDean Luick /* move from LRU list to delete list */ 2150636e9abSDean Luick list_move(&rbnode->list, &del_list); 21610345998SDean Luick } 21710345998SDean Luick if (stop) 21810345998SDean Luick break; 21910345998SDean Luick } 22010345998SDean Luick spin_unlock_irqrestore(&handler->lock, flags); 22110345998SDean Luick 22210345998SDean Luick while (!list_empty(&del_list)) { 22310345998SDean Luick rbnode = list_first_entry(&del_list, struct mmu_rb_node, list); 22410345998SDean Luick list_del(&rbnode->list); 225082b3532SDean Luick handler->ops->remove(handler->ops_arg, rbnode); 22610345998SDean Luick } 22710345998SDean Luick } 22810345998SDean Luick 229b85ced91SDean Luick /* 230b85ced91SDean Luick * It is up to the caller to ensure that this function does not race with the 231b85ced91SDean Luick * mmu invalidate notifier which may be calling the users remove callback on 232b85ced91SDean Luick * 'node'. 233b85ced91SDean Luick */ 234e0b09ac5SDean Luick void hfi1_mmu_rb_remove(struct mmu_rb_handler *handler, 235e0b09ac5SDean Luick struct mmu_rb_node *node) 236f48ad614SDennis Dalessandro { 2373c1091aaSIra Weiny unsigned long flags; 238f48ad614SDennis Dalessandro 2393d2a9d64SDennis Dalessandro if (current->mm != handler->mn.mm) 2403d2a9d64SDennis Dalessandro return; 2413d2a9d64SDennis Dalessandro 2423c1091aaSIra Weiny /* Validity of handler and node pointers has been checked by caller. */ 24334ab4de7SMichael J. Ruhl trace_hfi1_mmu_rb_remove(node->addr, node->len); 2443c1091aaSIra Weiny spin_lock_irqsave(&handler->lock, flags); 245e0b09ac5SDean Luick __mmu_int_rb_remove(node, &handler->root); 2460636e9abSDean Luick list_del(&node->list); /* remove from LRU list */ 2473c1091aaSIra Weiny spin_unlock_irqrestore(&handler->lock, flags); 2483c1091aaSIra Weiny 249082b3532SDean Luick handler->ops->remove(handler->ops_arg, node); 250f48ad614SDennis Dalessandro } 251f48ad614SDennis Dalessandro 25293065ac7SMichal Hocko static int mmu_notifier_range_start(struct mmu_notifier *mn, 2535d6527a7SJérôme Glisse const struct mmu_notifier_range *range) 254f48ad614SDennis Dalessandro { 255f48ad614SDennis Dalessandro struct mmu_rb_handler *handler = 256f48ad614SDennis Dalessandro container_of(mn, struct mmu_rb_handler, mn); 257f808c13fSDavidlohr Bueso struct rb_root_cached *root = &handler->root; 258f48ad614SDennis Dalessandro struct mmu_rb_node *node, *ptr = NULL; 259f48ad614SDennis Dalessandro unsigned long flags; 260b85ced91SDean Luick bool added = false; 261f48ad614SDennis Dalessandro 262f48ad614SDennis Dalessandro spin_lock_irqsave(&handler->lock, flags); 2635d6527a7SJérôme Glisse for (node = __mmu_int_rb_iter_first(root, range->start, range->end-1); 264f48ad614SDennis Dalessandro node; node = ptr) { 265f48ad614SDennis Dalessandro /* Guard against node removal. */ 2665d6527a7SJérôme Glisse ptr = __mmu_int_rb_iter_next(node, range->start, 2675d6527a7SJérôme Glisse range->end - 1); 26834ab4de7SMichael J. Ruhl trace_hfi1_mmu_mem_invalidate(node->addr, node->len); 269e0b09ac5SDean Luick if (handler->ops->invalidate(handler->ops_arg, node)) { 270f48ad614SDennis Dalessandro __mmu_int_rb_remove(node, root); 2710636e9abSDean Luick /* move from LRU list to delete list */ 2720636e9abSDean Luick list_move(&node->list, &handler->del_list); 273b85ced91SDean Luick added = true; 274f48ad614SDennis Dalessandro } 275f48ad614SDennis Dalessandro } 276f48ad614SDennis Dalessandro spin_unlock_irqrestore(&handler->lock, flags); 277b85ced91SDean Luick 278b85ced91SDean Luick if (added) 279b85ced91SDean Luick queue_work(handler->wq, &handler->del_work); 28093065ac7SMichal Hocko 28193065ac7SMichal Hocko return 0; 282b85ced91SDean Luick } 283b85ced91SDean Luick 284b85ced91SDean Luick /* 285b85ced91SDean Luick * Call the remove function for the given handler and the list. This 286b85ced91SDean Luick * is expected to be called with a delete list extracted from handler. 287b85ced91SDean Luick * The caller should not be holding the handler lock. 288b85ced91SDean Luick */ 289b85ced91SDean Luick static void do_remove(struct mmu_rb_handler *handler, 290b85ced91SDean Luick struct list_head *del_list) 291b85ced91SDean Luick { 292b85ced91SDean Luick struct mmu_rb_node *node; 293b85ced91SDean Luick 294b85ced91SDean Luick while (!list_empty(del_list)) { 295b85ced91SDean Luick node = list_first_entry(del_list, struct mmu_rb_node, list); 296b85ced91SDean Luick list_del(&node->list); 297082b3532SDean Luick handler->ops->remove(handler->ops_arg, node); 298b85ced91SDean Luick } 299b85ced91SDean Luick } 300b85ced91SDean Luick 301b85ced91SDean Luick /* 302b85ced91SDean Luick * Work queue function to remove all nodes that have been queued up to 303c1e8d7c6SMichel Lespinasse * be removed. The key feature is that mm->mmap_lock is not being held 304b85ced91SDean Luick * and the remove callback can sleep while taking it, if needed. 305b85ced91SDean Luick */ 306b85ced91SDean Luick static void handle_remove(struct work_struct *work) 307b85ced91SDean Luick { 308b85ced91SDean Luick struct mmu_rb_handler *handler = container_of(work, 309b85ced91SDean Luick struct mmu_rb_handler, 310b85ced91SDean Luick del_work); 311b85ced91SDean Luick struct list_head del_list; 312b85ced91SDean Luick unsigned long flags; 313b85ced91SDean Luick 314b85ced91SDean Luick /* remove anything that is queued to get removed */ 315b85ced91SDean Luick spin_lock_irqsave(&handler->lock, flags); 316b85ced91SDean Luick list_replace_init(&handler->del_list, &del_list); 317b85ced91SDean Luick spin_unlock_irqrestore(&handler->lock, flags); 318b85ced91SDean Luick 319b85ced91SDean Luick do_remove(handler, &del_list); 320f48ad614SDennis Dalessandro } 321