18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (C) 2020 Red Hat, Inc. 38c2ecf20Sopenharmony_ci * Author: Jason Wang <jasowang@redhat.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * IOTLB implementation for vhost. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/vhost_iotlb.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define MOD_VERSION "0.1" 128c2ecf20Sopenharmony_ci#define MOD_DESC "VHOST IOTLB" 138c2ecf20Sopenharmony_ci#define MOD_AUTHOR "Jason Wang <jasowang@redhat.com>" 148c2ecf20Sopenharmony_ci#define MOD_LICENSE "GPL v2" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define START(map) ((map)->start) 178c2ecf20Sopenharmony_ci#define LAST(map) ((map)->last) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ciINTERVAL_TREE_DEFINE(struct vhost_iotlb_map, 208c2ecf20Sopenharmony_ci rb, __u64, __subtree_last, 218c2ecf20Sopenharmony_ci START, LAST, static inline, vhost_iotlb_itree); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/** 248c2ecf20Sopenharmony_ci * vhost_iotlb_map_free - remove a map node and free it 258c2ecf20Sopenharmony_ci * @iotlb: the IOTLB 268c2ecf20Sopenharmony_ci * @map: the map that want to be remove and freed 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_civoid vhost_iotlb_map_free(struct vhost_iotlb *iotlb, 298c2ecf20Sopenharmony_ci struct vhost_iotlb_map *map) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci vhost_iotlb_itree_remove(map, &iotlb->root); 328c2ecf20Sopenharmony_ci list_del(&map->link); 338c2ecf20Sopenharmony_ci kfree(map); 348c2ecf20Sopenharmony_ci iotlb->nmaps--; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_map_free); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/** 398c2ecf20Sopenharmony_ci * vhost_iotlb_add_range - add a new range to vhost IOTLB 408c2ecf20Sopenharmony_ci * @iotlb: the IOTLB 418c2ecf20Sopenharmony_ci * @start: start of the IOVA range 428c2ecf20Sopenharmony_ci * @last: last of IOVA range 438c2ecf20Sopenharmony_ci * @addr: the address that is mapped to @start 448c2ecf20Sopenharmony_ci * @perm: access permission of this range 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * Returns an error last is smaller than start or memory allocation 478c2ecf20Sopenharmony_ci * fails 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ciint vhost_iotlb_add_range(struct vhost_iotlb *iotlb, 508c2ecf20Sopenharmony_ci u64 start, u64 last, 518c2ecf20Sopenharmony_ci u64 addr, unsigned int perm) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct vhost_iotlb_map *map; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (last < start) 568c2ecf20Sopenharmony_ci return -EFAULT; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (iotlb->limit && 598c2ecf20Sopenharmony_ci iotlb->nmaps == iotlb->limit && 608c2ecf20Sopenharmony_ci iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) { 618c2ecf20Sopenharmony_ci map = list_first_entry(&iotlb->list, typeof(*map), link); 628c2ecf20Sopenharmony_ci vhost_iotlb_map_free(iotlb, map); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci map = kmalloc(sizeof(*map), GFP_ATOMIC); 668c2ecf20Sopenharmony_ci if (!map) 678c2ecf20Sopenharmony_ci return -ENOMEM; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci map->start = start; 708c2ecf20Sopenharmony_ci map->size = last - start + 1; 718c2ecf20Sopenharmony_ci map->last = last; 728c2ecf20Sopenharmony_ci map->addr = addr; 738c2ecf20Sopenharmony_ci map->perm = perm; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci iotlb->nmaps++; 768c2ecf20Sopenharmony_ci vhost_iotlb_itree_insert(map, &iotlb->root); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&map->link); 798c2ecf20Sopenharmony_ci list_add_tail(&map->link, &iotlb->list); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_add_range); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/** 868c2ecf20Sopenharmony_ci * vring_iotlb_del_range - delete overlapped ranges from vhost IOTLB 878c2ecf20Sopenharmony_ci * @iotlb: the IOTLB 888c2ecf20Sopenharmony_ci * @start: start of the IOVA range 898c2ecf20Sopenharmony_ci * @last: last of IOVA range 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_civoid vhost_iotlb_del_range(struct vhost_iotlb *iotlb, u64 start, u64 last) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct vhost_iotlb_map *map; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci while ((map = vhost_iotlb_itree_iter_first(&iotlb->root, 968c2ecf20Sopenharmony_ci start, last))) 978c2ecf20Sopenharmony_ci vhost_iotlb_map_free(iotlb, map); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_del_range); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/** 1028c2ecf20Sopenharmony_ci * vhost_iotlb_alloc - add a new vhost IOTLB 1038c2ecf20Sopenharmony_ci * @limit: maximum number of IOTLB entries 1048c2ecf20Sopenharmony_ci * @flags: VHOST_IOTLB_FLAG_XXX 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * Returns an error is memory allocation fails 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistruct vhost_iotlb *vhost_iotlb_alloc(unsigned int limit, unsigned int flags) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct vhost_iotlb *iotlb = kzalloc(sizeof(*iotlb), GFP_KERNEL); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!iotlb) 1138c2ecf20Sopenharmony_ci return NULL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci iotlb->root = RB_ROOT_CACHED; 1168c2ecf20Sopenharmony_ci iotlb->limit = limit; 1178c2ecf20Sopenharmony_ci iotlb->nmaps = 0; 1188c2ecf20Sopenharmony_ci iotlb->flags = flags; 1198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&iotlb->list); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return iotlb; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_alloc); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/** 1268c2ecf20Sopenharmony_ci * vhost_iotlb_reset - reset vhost IOTLB (free all IOTLB entries) 1278c2ecf20Sopenharmony_ci * @iotlb: the IOTLB to be reset 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_civoid vhost_iotlb_reset(struct vhost_iotlb *iotlb) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci vhost_iotlb_del_range(iotlb, 0ULL, 0ULL - 1); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_reset); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/** 1368c2ecf20Sopenharmony_ci * vhost_iotlb_free - reset and free vhost IOTLB 1378c2ecf20Sopenharmony_ci * @iotlb: the IOTLB to be freed 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_civoid vhost_iotlb_free(struct vhost_iotlb *iotlb) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci if (iotlb) { 1428c2ecf20Sopenharmony_ci vhost_iotlb_reset(iotlb); 1438c2ecf20Sopenharmony_ci kfree(iotlb); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_free); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/** 1498c2ecf20Sopenharmony_ci * vhost_iotlb_itree_first - return the first overlapped range 1508c2ecf20Sopenharmony_ci * @iotlb: the IOTLB 1518c2ecf20Sopenharmony_ci * @start: start of IOVA range 1528c2ecf20Sopenharmony_ci * @last: last byte in IOVA range 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistruct vhost_iotlb_map * 1558c2ecf20Sopenharmony_civhost_iotlb_itree_first(struct vhost_iotlb *iotlb, u64 start, u64 last) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci return vhost_iotlb_itree_iter_first(&iotlb->root, start, last); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_itree_first); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/** 1628c2ecf20Sopenharmony_ci * vhost_iotlb_itree_next - return the next overlapped range 1638c2ecf20Sopenharmony_ci * @map: the starting map node 1648c2ecf20Sopenharmony_ci * @start: start of IOVA range 1658c2ecf20Sopenharmony_ci * @last: last byte IOVA range 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistruct vhost_iotlb_map * 1688c2ecf20Sopenharmony_civhost_iotlb_itree_next(struct vhost_iotlb_map *map, u64 start, u64 last) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return vhost_iotlb_itree_iter_next(map, start, last); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_itree_next); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ciMODULE_VERSION(MOD_VERSION); 1758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(MOD_DESC); 1768c2ecf20Sopenharmony_ciMODULE_AUTHOR(MOD_AUTHOR); 1778c2ecf20Sopenharmony_ciMODULE_LICENSE(MOD_LICENSE); 178