162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (C) 2020 Red Hat, Inc. 362306a36Sopenharmony_ci * Author: Jason Wang <jasowang@redhat.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * IOTLB implementation for vhost. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/vhost_iotlb.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define MOD_VERSION "0.1" 1262306a36Sopenharmony_ci#define MOD_DESC "VHOST IOTLB" 1362306a36Sopenharmony_ci#define MOD_AUTHOR "Jason Wang <jasowang@redhat.com>" 1462306a36Sopenharmony_ci#define MOD_LICENSE "GPL v2" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define START(map) ((map)->start) 1762306a36Sopenharmony_ci#define LAST(map) ((map)->last) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciINTERVAL_TREE_DEFINE(struct vhost_iotlb_map, 2062306a36Sopenharmony_ci rb, __u64, __subtree_last, 2162306a36Sopenharmony_ci START, LAST, static inline, vhost_iotlb_itree); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/** 2462306a36Sopenharmony_ci * vhost_iotlb_map_free - remove a map node and free it 2562306a36Sopenharmony_ci * @iotlb: the IOTLB 2662306a36Sopenharmony_ci * @map: the map that want to be remove and freed 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_civoid vhost_iotlb_map_free(struct vhost_iotlb *iotlb, 2962306a36Sopenharmony_ci struct vhost_iotlb_map *map) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci vhost_iotlb_itree_remove(map, &iotlb->root); 3262306a36Sopenharmony_ci list_del(&map->link); 3362306a36Sopenharmony_ci kfree(map); 3462306a36Sopenharmony_ci iotlb->nmaps--; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_map_free); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/** 3962306a36Sopenharmony_ci * vhost_iotlb_add_range_ctx - add a new range to vhost IOTLB 4062306a36Sopenharmony_ci * @iotlb: the IOTLB 4162306a36Sopenharmony_ci * @start: start of the IOVA range 4262306a36Sopenharmony_ci * @last: last of IOVA range 4362306a36Sopenharmony_ci * @addr: the address that is mapped to @start 4462306a36Sopenharmony_ci * @perm: access permission of this range 4562306a36Sopenharmony_ci * @opaque: the opaque pointer for the new mapping 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Returns an error last is smaller than start or memory allocation 4862306a36Sopenharmony_ci * fails 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ciint vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb, 5162306a36Sopenharmony_ci u64 start, u64 last, 5262306a36Sopenharmony_ci u64 addr, unsigned int perm, 5362306a36Sopenharmony_ci void *opaque) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct vhost_iotlb_map *map; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (last < start) 5862306a36Sopenharmony_ci return -EFAULT; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* If the range being mapped is [0, ULONG_MAX], split it into two entries 6162306a36Sopenharmony_ci * otherwise its size would overflow u64. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci if (start == 0 && last == ULONG_MAX) { 6462306a36Sopenharmony_ci u64 mid = last / 2; 6562306a36Sopenharmony_ci int err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr, 6662306a36Sopenharmony_ci perm, opaque); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (err) 6962306a36Sopenharmony_ci return err; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci addr += mid + 1; 7262306a36Sopenharmony_ci start = mid + 1; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (iotlb->limit && 7662306a36Sopenharmony_ci iotlb->nmaps == iotlb->limit && 7762306a36Sopenharmony_ci iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) { 7862306a36Sopenharmony_ci map = list_first_entry(&iotlb->list, typeof(*map), link); 7962306a36Sopenharmony_ci vhost_iotlb_map_free(iotlb, map); 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci map = kmalloc(sizeof(*map), GFP_ATOMIC); 8362306a36Sopenharmony_ci if (!map) 8462306a36Sopenharmony_ci return -ENOMEM; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci map->start = start; 8762306a36Sopenharmony_ci map->size = last - start + 1; 8862306a36Sopenharmony_ci map->last = last; 8962306a36Sopenharmony_ci map->addr = addr; 9062306a36Sopenharmony_ci map->perm = perm; 9162306a36Sopenharmony_ci map->opaque = opaque; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci iotlb->nmaps++; 9462306a36Sopenharmony_ci vhost_iotlb_itree_insert(map, &iotlb->root); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci INIT_LIST_HEAD(&map->link); 9762306a36Sopenharmony_ci list_add_tail(&map->link, &iotlb->list); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_add_range_ctx); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciint vhost_iotlb_add_range(struct vhost_iotlb *iotlb, 10462306a36Sopenharmony_ci u64 start, u64 last, 10562306a36Sopenharmony_ci u64 addr, unsigned int perm) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci return vhost_iotlb_add_range_ctx(iotlb, start, last, 10862306a36Sopenharmony_ci addr, perm, NULL); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_add_range); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * vhost_iotlb_del_range - delete overlapped ranges from vhost IOTLB 11462306a36Sopenharmony_ci * @iotlb: the IOTLB 11562306a36Sopenharmony_ci * @start: start of the IOVA range 11662306a36Sopenharmony_ci * @last: last of IOVA range 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_civoid vhost_iotlb_del_range(struct vhost_iotlb *iotlb, u64 start, u64 last) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct vhost_iotlb_map *map; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci while ((map = vhost_iotlb_itree_iter_first(&iotlb->root, 12362306a36Sopenharmony_ci start, last))) 12462306a36Sopenharmony_ci vhost_iotlb_map_free(iotlb, map); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_del_range); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * vhost_iotlb_init - initialize a vhost IOTLB 13062306a36Sopenharmony_ci * @iotlb: the IOTLB that needs to be initialized 13162306a36Sopenharmony_ci * @limit: maximum number of IOTLB entries 13262306a36Sopenharmony_ci * @flags: VHOST_IOTLB_FLAG_XXX 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_civoid vhost_iotlb_init(struct vhost_iotlb *iotlb, unsigned int limit, 13562306a36Sopenharmony_ci unsigned int flags) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci iotlb->root = RB_ROOT_CACHED; 13862306a36Sopenharmony_ci iotlb->limit = limit; 13962306a36Sopenharmony_ci iotlb->nmaps = 0; 14062306a36Sopenharmony_ci iotlb->flags = flags; 14162306a36Sopenharmony_ci INIT_LIST_HEAD(&iotlb->list); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_init); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/** 14662306a36Sopenharmony_ci * vhost_iotlb_alloc - add a new vhost IOTLB 14762306a36Sopenharmony_ci * @limit: maximum number of IOTLB entries 14862306a36Sopenharmony_ci * @flags: VHOST_IOTLB_FLAG_XXX 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * Returns an error is memory allocation fails 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistruct vhost_iotlb *vhost_iotlb_alloc(unsigned int limit, unsigned int flags) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct vhost_iotlb *iotlb = kzalloc(sizeof(*iotlb), GFP_KERNEL); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!iotlb) 15762306a36Sopenharmony_ci return NULL; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci vhost_iotlb_init(iotlb, limit, flags); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return iotlb; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_alloc); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * vhost_iotlb_reset - reset vhost IOTLB (free all IOTLB entries) 16762306a36Sopenharmony_ci * @iotlb: the IOTLB to be reset 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_civoid vhost_iotlb_reset(struct vhost_iotlb *iotlb) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci vhost_iotlb_del_range(iotlb, 0ULL, 0ULL - 1); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_reset); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/** 17662306a36Sopenharmony_ci * vhost_iotlb_free - reset and free vhost IOTLB 17762306a36Sopenharmony_ci * @iotlb: the IOTLB to be freed 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_civoid vhost_iotlb_free(struct vhost_iotlb *iotlb) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci if (iotlb) { 18262306a36Sopenharmony_ci vhost_iotlb_reset(iotlb); 18362306a36Sopenharmony_ci kfree(iotlb); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_free); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/** 18962306a36Sopenharmony_ci * vhost_iotlb_itree_first - return the first overlapped range 19062306a36Sopenharmony_ci * @iotlb: the IOTLB 19162306a36Sopenharmony_ci * @start: start of IOVA range 19262306a36Sopenharmony_ci * @last: last byte in IOVA range 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_cistruct vhost_iotlb_map * 19562306a36Sopenharmony_civhost_iotlb_itree_first(struct vhost_iotlb *iotlb, u64 start, u64 last) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci return vhost_iotlb_itree_iter_first(&iotlb->root, start, last); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_itree_first); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/** 20262306a36Sopenharmony_ci * vhost_iotlb_itree_next - return the next overlapped range 20362306a36Sopenharmony_ci * @map: the starting map node 20462306a36Sopenharmony_ci * @start: start of IOVA range 20562306a36Sopenharmony_ci * @last: last byte IOVA range 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_cistruct vhost_iotlb_map * 20862306a36Sopenharmony_civhost_iotlb_itree_next(struct vhost_iotlb_map *map, u64 start, u64 last) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return vhost_iotlb_itree_iter_next(map, start, last); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vhost_iotlb_itree_next); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ciMODULE_VERSION(MOD_VERSION); 21562306a36Sopenharmony_ciMODULE_DESCRIPTION(MOD_DESC); 21662306a36Sopenharmony_ciMODULE_AUTHOR(MOD_AUTHOR); 21762306a36Sopenharmony_ciMODULE_LICENSE(MOD_LICENSE); 218