1f48ad614SDennis Dalessandro /* 2*3d2a9d64SDennis Dalessandro * Copyright(c) 2020 Cornelis Networks, Inc. 334ab4de7SMichael J. Ruhl * Copyright(c) 2016 - 2017 Intel Corporation. 4f48ad614SDennis Dalessandro * 5f48ad614SDennis Dalessandro * This file is provided under a dual BSD/GPLv2 license. When using or 6f48ad614SDennis Dalessandro * redistributing this file, you may do so under either license. 7f48ad614SDennis Dalessandro * 8f48ad614SDennis Dalessandro * GPL LICENSE SUMMARY 9f48ad614SDennis Dalessandro * 10f48ad614SDennis Dalessandro * This program is free software; you can redistribute it and/or modify 11f48ad614SDennis Dalessandro * it under the terms of version 2 of the GNU General Public License as 12f48ad614SDennis Dalessandro * published by the Free Software Foundation. 13f48ad614SDennis Dalessandro * 14f48ad614SDennis Dalessandro * This program is distributed in the hope that it will be useful, but 15f48ad614SDennis Dalessandro * WITHOUT ANY WARRANTY; without even the implied warranty of 16f48ad614SDennis Dalessandro * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17f48ad614SDennis Dalessandro * General Public License for more details. 18f48ad614SDennis Dalessandro * 19f48ad614SDennis Dalessandro * BSD LICENSE 20f48ad614SDennis Dalessandro * 21f48ad614SDennis Dalessandro * Redistribution and use in source and binary forms, with or without 22f48ad614SDennis Dalessandro * modification, are permitted provided that the following conditions 23f48ad614SDennis Dalessandro * are met: 24f48ad614SDennis Dalessandro * 25f48ad614SDennis Dalessandro * - Redistributions of source code must retain the above copyright 26f48ad614SDennis Dalessandro * notice, this list of conditions and the following disclaimer. 27f48ad614SDennis Dalessandro * - Redistributions in binary form must reproduce the above copyright 28f48ad614SDennis Dalessandro * notice, this list of conditions and the following disclaimer in 29f48ad614SDennis Dalessandro * the documentation and/or other materials provided with the 30f48ad614SDennis Dalessandro * distribution. 31f48ad614SDennis Dalessandro * - Neither the name of Intel Corporation nor the names of its 32f48ad614SDennis Dalessandro * contributors may be used to endorse or promote products derived 33f48ad614SDennis Dalessandro * from this software without specific prior written permission. 34f48ad614SDennis Dalessandro * 35f48ad614SDennis Dalessandro * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36f48ad614SDennis Dalessandro * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37f48ad614SDennis Dalessandro * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38f48ad614SDennis Dalessandro * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39f48ad614SDennis Dalessandro * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40f48ad614SDennis Dalessandro * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41f48ad614SDennis Dalessandro * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42f48ad614SDennis Dalessandro * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43f48ad614SDennis Dalessandro * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44f48ad614SDennis Dalessandro * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45f48ad614SDennis Dalessandro * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 46f48ad614SDennis Dalessandro * 47f48ad614SDennis Dalessandro */ 48f48ad614SDennis Dalessandro #include <linux/list.h> 49f48ad614SDennis Dalessandro #include <linux/rculist.h> 50f48ad614SDennis Dalessandro #include <linux/mmu_notifier.h> 51f48ad614SDennis Dalessandro #include <linux/interval_tree_generic.h> 52*3d2a9d64SDennis Dalessandro #include <linux/sched/mm.h> 53f48ad614SDennis Dalessandro 54f48ad614SDennis Dalessandro #include "mmu_rb.h" 55f48ad614SDennis Dalessandro #include "trace.h" 56f48ad614SDennis Dalessandro 57f48ad614SDennis Dalessandro static unsigned long mmu_node_start(struct mmu_rb_node *); 58f48ad614SDennis Dalessandro static unsigned long mmu_node_last(struct mmu_rb_node *); 5993065ac7SMichal Hocko static int mmu_notifier_range_start(struct mmu_notifier *, 605d6527a7SJérôme Glisse const struct mmu_notifier_range *); 61f48ad614SDennis Dalessandro static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *, 62f48ad614SDennis Dalessandro unsigned long, unsigned long); 63b85ced91SDean Luick static void do_remove(struct mmu_rb_handler *handler, 64b85ced91SDean Luick struct list_head *del_list); 65b85ced91SDean Luick static void handle_remove(struct work_struct *work); 66f48ad614SDennis Dalessandro 670fc859a6SBhumika Goyal static const struct mmu_notifier_ops mn_opts = { 68f48ad614SDennis Dalessandro .invalidate_range_start = mmu_notifier_range_start, 69f48ad614SDennis Dalessandro }; 70f48ad614SDennis Dalessandro 71f48ad614SDennis Dalessandro INTERVAL_TREE_DEFINE(struct mmu_rb_node, node, unsigned long, __last, 72f48ad614SDennis Dalessandro mmu_node_start, mmu_node_last, static, __mmu_int_rb); 73f48ad614SDennis Dalessandro 74f48ad614SDennis Dalessandro static unsigned long mmu_node_start(struct mmu_rb_node *node) 75f48ad614SDennis Dalessandro { 76f48ad614SDennis Dalessandro return node->addr & PAGE_MASK; 77f48ad614SDennis Dalessandro } 78f48ad614SDennis Dalessandro 79f48ad614SDennis Dalessandro static unsigned long mmu_node_last(struct mmu_rb_node *node) 80f48ad614SDennis Dalessandro { 81f48ad614SDennis Dalessandro return PAGE_ALIGN(node->addr + node->len) - 1; 82f48ad614SDennis Dalessandro } 83f48ad614SDennis Dalessandro 84*3d2a9d64SDennis Dalessandro int hfi1_mmu_rb_register(void *ops_arg, 85e0b09ac5SDean Luick struct mmu_rb_ops *ops, 86b85ced91SDean Luick struct workqueue_struct *wq, 87e0b09ac5SDean Luick struct mmu_rb_handler **handler) 88f48ad614SDennis Dalessandro { 89*3d2a9d64SDennis Dalessandro struct mmu_rb_handler *h; 903faa3d9aSIra Weiny int ret; 91f48ad614SDennis Dalessandro 92*3d2a9d64SDennis Dalessandro h = kmalloc(sizeof(*h), GFP_KERNEL); 93*3d2a9d64SDennis Dalessandro if (!h) 94f48ad614SDennis Dalessandro return -ENOMEM; 95f48ad614SDennis Dalessandro 96*3d2a9d64SDennis Dalessandro h->root = RB_ROOT_CACHED; 97*3d2a9d64SDennis Dalessandro h->ops = ops; 98*3d2a9d64SDennis Dalessandro h->ops_arg = ops_arg; 99*3d2a9d64SDennis Dalessandro INIT_HLIST_NODE(&h->mn.hlist); 100*3d2a9d64SDennis Dalessandro spin_lock_init(&h->lock); 101*3d2a9d64SDennis Dalessandro h->mn.ops = &mn_opts; 102*3d2a9d64SDennis Dalessandro INIT_WORK(&h->del_work, handle_remove); 103*3d2a9d64SDennis Dalessandro INIT_LIST_HEAD(&h->del_list); 104*3d2a9d64SDennis Dalessandro INIT_LIST_HEAD(&h->lru_list); 105*3d2a9d64SDennis Dalessandro h->wq = wq; 1063faa3d9aSIra Weiny 107*3d2a9d64SDennis Dalessandro ret = mmu_notifier_register(&h->mn, current->mm); 1083faa3d9aSIra Weiny if (ret) { 109*3d2a9d64SDennis Dalessandro kfree(h); 1103faa3d9aSIra Weiny return ret; 1113faa3d9aSIra Weiny } 1123faa3d9aSIra Weiny 113*3d2a9d64SDennis Dalessandro *handler = h; 114e0b09ac5SDean Luick return 0; 115f48ad614SDennis Dalessandro } 116f48ad614SDennis Dalessandro 117e0b09ac5SDean Luick void hfi1_mmu_rb_unregister(struct mmu_rb_handler *handler) 118f48ad614SDennis Dalessandro { 11920a42d08SDean Luick struct mmu_rb_node *rbnode; 12020a42d08SDean Luick struct rb_node *node; 121f48ad614SDennis Dalessandro unsigned long flags; 122b85ced91SDean Luick struct list_head del_list; 123f48ad614SDennis Dalessandro 124f48ad614SDennis Dalessandro /* Unregister first so we don't get any more notifications. */ 125*3d2a9d64SDennis Dalessandro mmu_notifier_unregister(&handler->mn, handler->mn.mm); 126f48ad614SDennis Dalessandro 127b85ced91SDean Luick /* 128b85ced91SDean Luick * Make sure the wq delete handler is finished running. It will not 129b85ced91SDean Luick * be triggered once the mmu notifiers are unregistered above. 130b85ced91SDean Luick */ 131b85ced91SDean Luick flush_work(&handler->del_work); 132b85ced91SDean Luick 133b85ced91SDean Luick INIT_LIST_HEAD(&del_list); 134b85ced91SDean Luick 135f48ad614SDennis Dalessandro spin_lock_irqsave(&handler->lock, flags); 136f808c13fSDavidlohr Bueso while ((node = rb_first_cached(&handler->root))) { 137f48ad614SDennis Dalessandro rbnode = rb_entry(node, struct mmu_rb_node, node); 138f808c13fSDavidlohr Bueso rb_erase_cached(node, &handler->root); 1390636e9abSDean Luick /* move from LRU list to delete list */ 1400636e9abSDean Luick list_move(&rbnode->list, &del_list); 141f48ad614SDennis Dalessandro } 142f48ad614SDennis Dalessandro spin_unlock_irqrestore(&handler->lock, flags); 143f48ad614SDennis Dalessandro 144b85ced91SDean Luick do_remove(handler, &del_list); 145b85ced91SDean Luick 146f48ad614SDennis Dalessandro kfree(handler); 147f48ad614SDennis Dalessandro } 148f48ad614SDennis Dalessandro 149e0b09ac5SDean Luick int hfi1_mmu_rb_insert(struct mmu_rb_handler *handler, 150e0b09ac5SDean Luick struct mmu_rb_node *mnode) 151f48ad614SDennis Dalessandro { 152f48ad614SDennis Dalessandro struct mmu_rb_node *node; 153f48ad614SDennis Dalessandro unsigned long flags; 154f48ad614SDennis Dalessandro int ret = 0; 155f48ad614SDennis Dalessandro 15634ab4de7SMichael J. Ruhl trace_hfi1_mmu_rb_insert(mnode->addr, mnode->len); 157*3d2a9d64SDennis Dalessandro 158*3d2a9d64SDennis Dalessandro if (current->mm != handler->mn.mm) 159*3d2a9d64SDennis Dalessandro return -EPERM; 160*3d2a9d64SDennis Dalessandro 161f48ad614SDennis Dalessandro spin_lock_irqsave(&handler->lock, flags); 162f48ad614SDennis Dalessandro node = __mmu_rb_search(handler, mnode->addr, mnode->len); 163f48ad614SDennis Dalessandro if (node) { 164f48ad614SDennis Dalessandro ret = -EINVAL; 165f48ad614SDennis Dalessandro goto unlock; 166f48ad614SDennis Dalessandro } 167e0b09ac5SDean Luick __mmu_int_rb_insert(mnode, &handler->root); 1680636e9abSDean Luick list_add(&mnode->list, &handler->lru_list); 169f48ad614SDennis Dalessandro 170e0b09ac5SDean Luick ret = handler->ops->insert(handler->ops_arg, mnode); 1710636e9abSDean Luick if (ret) { 172e0b09ac5SDean Luick __mmu_int_rb_remove(mnode, &handler->root); 1730636e9abSDean Luick list_del(&mnode->list); /* remove from LRU list */ 1740636e9abSDean Luick } 175*3d2a9d64SDennis Dalessandro mnode->handler = handler; 176f48ad614SDennis Dalessandro unlock: 177f48ad614SDennis Dalessandro spin_unlock_irqrestore(&handler->lock, flags); 178f48ad614SDennis Dalessandro return ret; 179f48ad614SDennis Dalessandro } 180f48ad614SDennis Dalessandro 181f48ad614SDennis Dalessandro /* Caller must hold handler lock */ 182f48ad614SDennis Dalessandro static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *handler, 183f48ad614SDennis Dalessandro unsigned long addr, 184f48ad614SDennis Dalessandro unsigned long len) 185f48ad614SDennis Dalessandro { 186f48ad614SDennis Dalessandro struct mmu_rb_node *node = NULL; 187f48ad614SDennis Dalessandro 18834ab4de7SMichael J. Ruhl trace_hfi1_mmu_rb_search(addr, len); 189f48ad614SDennis Dalessandro if (!handler->ops->filter) { 190e0b09ac5SDean Luick node = __mmu_int_rb_iter_first(&handler->root, addr, 191f48ad614SDennis Dalessandro (addr + len) - 1); 192f48ad614SDennis Dalessandro } else { 193e0b09ac5SDean Luick for (node = __mmu_int_rb_iter_first(&handler->root, addr, 194f48ad614SDennis Dalessandro (addr + len) - 1); 195f48ad614SDennis Dalessandro node; 196f48ad614SDennis Dalessandro node = __mmu_int_rb_iter_next(node, addr, 197f48ad614SDennis Dalessandro (addr + len) - 1)) { 198f48ad614SDennis Dalessandro if (handler->ops->filter(node, addr, len)) 199f48ad614SDennis Dalessandro return node; 200f48ad614SDennis Dalessandro } 201f48ad614SDennis Dalessandro } 202f48ad614SDennis Dalessandro return node; 203f48ad614SDennis Dalessandro } 204f48ad614SDennis Dalessandro 2057be85676SSebastian Sanchez bool hfi1_mmu_rb_remove_unless_exact(struct mmu_rb_handler *handler, 2067be85676SSebastian Sanchez unsigned long addr, unsigned long len, 2077be85676SSebastian Sanchez struct mmu_rb_node **rb_node) 208f48ad614SDennis Dalessandro { 209f48ad614SDennis Dalessandro struct mmu_rb_node *node; 210f48ad614SDennis Dalessandro unsigned long flags; 2117be85676SSebastian Sanchez bool ret = false; 212f48ad614SDennis Dalessandro 213*3d2a9d64SDennis Dalessandro if (current->mm != handler->mn.mm) 214*3d2a9d64SDennis Dalessandro return ret; 215*3d2a9d64SDennis Dalessandro 216f48ad614SDennis Dalessandro spin_lock_irqsave(&handler->lock, flags); 217f48ad614SDennis Dalessandro node = __mmu_rb_search(handler, addr, len); 2180636e9abSDean Luick if (node) { 2197be85676SSebastian Sanchez if (node->addr == addr && node->len == len) 2207be85676SSebastian Sanchez goto unlock; 221e0b09ac5SDean Luick __mmu_int_rb_remove(node, &handler->root); 2220636e9abSDean Luick list_del(&node->list); /* remove from LRU list */ 2237be85676SSebastian Sanchez ret = true; 2240636e9abSDean Luick } 2257be85676SSebastian Sanchez unlock: 226f48ad614SDennis Dalessandro spin_unlock_irqrestore(&handler->lock, flags); 2277be85676SSebastian Sanchez *rb_node = node; 2287be85676SSebastian Sanchez return ret; 229f48ad614SDennis Dalessandro } 230f48ad614SDennis Dalessandro 23110345998SDean Luick void hfi1_mmu_rb_evict(struct mmu_rb_handler *handler, void *evict_arg) 23210345998SDean Luick { 2330636e9abSDean Luick struct mmu_rb_node *rbnode, *ptr; 23410345998SDean Luick struct list_head del_list; 23510345998SDean Luick unsigned long flags; 23610345998SDean Luick bool stop = false; 23710345998SDean Luick 238*3d2a9d64SDennis Dalessandro if (current->mm != handler->mn.mm) 239*3d2a9d64SDennis Dalessandro return; 240*3d2a9d64SDennis Dalessandro 24110345998SDean Luick INIT_LIST_HEAD(&del_list); 24210345998SDean Luick 24310345998SDean Luick spin_lock_irqsave(&handler->lock, flags); 2440636e9abSDean Luick list_for_each_entry_safe_reverse(rbnode, ptr, &handler->lru_list, 2450636e9abSDean Luick list) { 24610345998SDean Luick if (handler->ops->evict(handler->ops_arg, rbnode, evict_arg, 24710345998SDean Luick &stop)) { 24810345998SDean Luick __mmu_int_rb_remove(rbnode, &handler->root); 2490636e9abSDean Luick /* move from LRU list to delete list */ 2500636e9abSDean Luick list_move(&rbnode->list, &del_list); 25110345998SDean Luick } 25210345998SDean Luick if (stop) 25310345998SDean Luick break; 25410345998SDean Luick } 25510345998SDean Luick spin_unlock_irqrestore(&handler->lock, flags); 25610345998SDean Luick 25710345998SDean Luick while (!list_empty(&del_list)) { 25810345998SDean Luick rbnode = list_first_entry(&del_list, struct mmu_rb_node, list); 25910345998SDean Luick list_del(&rbnode->list); 260082b3532SDean Luick handler->ops->remove(handler->ops_arg, rbnode); 26110345998SDean Luick } 26210345998SDean Luick } 26310345998SDean Luick 264b85ced91SDean Luick /* 265b85ced91SDean Luick * It is up to the caller to ensure that this function does not race with the 266b85ced91SDean Luick * mmu invalidate notifier which may be calling the users remove callback on 267b85ced91SDean Luick * 'node'. 268b85ced91SDean Luick */ 269e0b09ac5SDean Luick void hfi1_mmu_rb_remove(struct mmu_rb_handler *handler, 270e0b09ac5SDean Luick struct mmu_rb_node *node) 271f48ad614SDennis Dalessandro { 2723c1091aaSIra Weiny unsigned long flags; 273f48ad614SDennis Dalessandro 274*3d2a9d64SDennis Dalessandro if (current->mm != handler->mn.mm) 275*3d2a9d64SDennis Dalessandro return; 276*3d2a9d64SDennis Dalessandro 2773c1091aaSIra Weiny /* Validity of handler and node pointers has been checked by caller. */ 27834ab4de7SMichael J. Ruhl trace_hfi1_mmu_rb_remove(node->addr, node->len); 2793c1091aaSIra Weiny spin_lock_irqsave(&handler->lock, flags); 280e0b09ac5SDean Luick __mmu_int_rb_remove(node, &handler->root); 2810636e9abSDean Luick list_del(&node->list); /* remove from LRU list */ 2823c1091aaSIra Weiny spin_unlock_irqrestore(&handler->lock, flags); 2833c1091aaSIra Weiny 284082b3532SDean Luick handler->ops->remove(handler->ops_arg, node); 285f48ad614SDennis Dalessandro } 286f48ad614SDennis Dalessandro 28793065ac7SMichal Hocko static int mmu_notifier_range_start(struct mmu_notifier *mn, 2885d6527a7SJérôme Glisse const struct mmu_notifier_range *range) 289f48ad614SDennis Dalessandro { 290f48ad614SDennis Dalessandro struct mmu_rb_handler *handler = 291f48ad614SDennis Dalessandro container_of(mn, struct mmu_rb_handler, mn); 292f808c13fSDavidlohr Bueso struct rb_root_cached *root = &handler->root; 293f48ad614SDennis Dalessandro struct mmu_rb_node *node, *ptr = NULL; 294f48ad614SDennis Dalessandro unsigned long flags; 295b85ced91SDean Luick bool added = false; 296f48ad614SDennis Dalessandro 297f48ad614SDennis Dalessandro spin_lock_irqsave(&handler->lock, flags); 2985d6527a7SJérôme Glisse for (node = __mmu_int_rb_iter_first(root, range->start, range->end-1); 299f48ad614SDennis Dalessandro node; node = ptr) { 300f48ad614SDennis Dalessandro /* Guard against node removal. */ 3015d6527a7SJérôme Glisse ptr = __mmu_int_rb_iter_next(node, range->start, 3025d6527a7SJérôme Glisse range->end - 1); 30334ab4de7SMichael J. Ruhl trace_hfi1_mmu_mem_invalidate(node->addr, node->len); 304e0b09ac5SDean Luick if (handler->ops->invalidate(handler->ops_arg, node)) { 305f48ad614SDennis Dalessandro __mmu_int_rb_remove(node, root); 3060636e9abSDean Luick /* move from LRU list to delete list */ 3070636e9abSDean Luick list_move(&node->list, &handler->del_list); 308b85ced91SDean Luick added = true; 309f48ad614SDennis Dalessandro } 310f48ad614SDennis Dalessandro } 311f48ad614SDennis Dalessandro spin_unlock_irqrestore(&handler->lock, flags); 312b85ced91SDean Luick 313b85ced91SDean Luick if (added) 314b85ced91SDean Luick queue_work(handler->wq, &handler->del_work); 31593065ac7SMichal Hocko 31693065ac7SMichal Hocko return 0; 317b85ced91SDean Luick } 318b85ced91SDean Luick 319b85ced91SDean Luick /* 320b85ced91SDean Luick * Call the remove function for the given handler and the list. This 321b85ced91SDean Luick * is expected to be called with a delete list extracted from handler. 322b85ced91SDean Luick * The caller should not be holding the handler lock. 323b85ced91SDean Luick */ 324b85ced91SDean Luick static void do_remove(struct mmu_rb_handler *handler, 325b85ced91SDean Luick struct list_head *del_list) 326b85ced91SDean Luick { 327b85ced91SDean Luick struct mmu_rb_node *node; 328b85ced91SDean Luick 329b85ced91SDean Luick while (!list_empty(del_list)) { 330b85ced91SDean Luick node = list_first_entry(del_list, struct mmu_rb_node, list); 331b85ced91SDean Luick list_del(&node->list); 332082b3532SDean Luick handler->ops->remove(handler->ops_arg, node); 333b85ced91SDean Luick } 334b85ced91SDean Luick } 335b85ced91SDean Luick 336b85ced91SDean Luick /* 337b85ced91SDean Luick * Work queue function to remove all nodes that have been queued up to 338c1e8d7c6SMichel Lespinasse * be removed. The key feature is that mm->mmap_lock is not being held 339b85ced91SDean Luick * and the remove callback can sleep while taking it, if needed. 340b85ced91SDean Luick */ 341b85ced91SDean Luick static void handle_remove(struct work_struct *work) 342b85ced91SDean Luick { 343b85ced91SDean Luick struct mmu_rb_handler *handler = container_of(work, 344b85ced91SDean Luick struct mmu_rb_handler, 345b85ced91SDean Luick del_work); 346b85ced91SDean Luick struct list_head del_list; 347b85ced91SDean Luick unsigned long flags; 348b85ced91SDean Luick 349b85ced91SDean Luick /* remove anything that is queued to get removed */ 350b85ced91SDean Luick spin_lock_irqsave(&handler->lock, flags); 351b85ced91SDean Luick list_replace_init(&handler->del_list, &del_list); 352b85ced91SDean Luick spin_unlock_irqrestore(&handler->lock, flags); 353b85ced91SDean Luick 354b85ced91SDean Luick do_remove(handler, &del_list); 355f48ad614SDennis Dalessandro } 356