Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* Copyright (C) 2020 Red Hat, Inc.
0003  * Author: Jason Wang <jasowang@redhat.com>
0004  *
0005  * IOTLB implementation for vhost.
0006  */
0007 #include <linux/slab.h>
0008 #include <linux/vhost_iotlb.h>
0009 #include <linux/module.h>
0010 
0011 #define MOD_VERSION  "0.1"
0012 #define MOD_DESC     "VHOST IOTLB"
0013 #define MOD_AUTHOR   "Jason Wang <jasowang@redhat.com>"
0014 #define MOD_LICENSE  "GPL v2"
0015 
0016 #define START(map) ((map)->start)
0017 #define LAST(map) ((map)->last)
0018 
0019 INTERVAL_TREE_DEFINE(struct vhost_iotlb_map,
0020              rb, __u64, __subtree_last,
0021              START, LAST, static inline, vhost_iotlb_itree);
0022 
0023 /**
0024  * vhost_iotlb_map_free - remove a map node and free it
0025  * @iotlb: the IOTLB
0026  * @map: the map that want to be remove and freed
0027  */
0028 void vhost_iotlb_map_free(struct vhost_iotlb *iotlb,
0029               struct vhost_iotlb_map *map)
0030 {
0031     vhost_iotlb_itree_remove(map, &iotlb->root);
0032     list_del(&map->link);
0033     kfree(map);
0034     iotlb->nmaps--;
0035 }
0036 EXPORT_SYMBOL_GPL(vhost_iotlb_map_free);
0037 
0038 /**
0039  * vhost_iotlb_add_range_ctx - add a new range to vhost IOTLB
0040  * @iotlb: the IOTLB
0041  * @start: start of the IOVA range
0042  * @last: last of IOVA range
0043  * @addr: the address that is mapped to @start
0044  * @perm: access permission of this range
0045  * @opaque: the opaque pointer for the new mapping
0046  *
0047  * Returns an error last is smaller than start or memory allocation
0048  * fails
0049  */
0050 int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb,
0051                   u64 start, u64 last,
0052                   u64 addr, unsigned int perm,
0053                   void *opaque)
0054 {
0055     struct vhost_iotlb_map *map;
0056 
0057     if (last < start)
0058         return -EFAULT;
0059 
0060     /* If the range being mapped is [0, ULONG_MAX], split it into two entries
0061      * otherwise its size would overflow u64.
0062      */
0063     if (start == 0 && last == ULONG_MAX) {
0064         u64 mid = last / 2;
0065         int err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr,
0066                 perm, opaque);
0067 
0068         if (err)
0069             return err;
0070 
0071         addr += mid + 1;
0072         start = mid + 1;
0073     }
0074 
0075     if (iotlb->limit &&
0076         iotlb->nmaps == iotlb->limit &&
0077         iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) {
0078         map = list_first_entry(&iotlb->list, typeof(*map), link);
0079         vhost_iotlb_map_free(iotlb, map);
0080     }
0081 
0082     map = kmalloc(sizeof(*map), GFP_ATOMIC);
0083     if (!map)
0084         return -ENOMEM;
0085 
0086     map->start = start;
0087     map->size = last - start + 1;
0088     map->last = last;
0089     map->addr = addr;
0090     map->perm = perm;
0091     map->opaque = opaque;
0092 
0093     iotlb->nmaps++;
0094     vhost_iotlb_itree_insert(map, &iotlb->root);
0095 
0096     INIT_LIST_HEAD(&map->link);
0097     list_add_tail(&map->link, &iotlb->list);
0098 
0099     return 0;
0100 }
0101 EXPORT_SYMBOL_GPL(vhost_iotlb_add_range_ctx);
0102 
0103 int vhost_iotlb_add_range(struct vhost_iotlb *iotlb,
0104               u64 start, u64 last,
0105               u64 addr, unsigned int perm)
0106 {
0107     return vhost_iotlb_add_range_ctx(iotlb, start, last,
0108                      addr, perm, NULL);
0109 }
0110 EXPORT_SYMBOL_GPL(vhost_iotlb_add_range);
0111 
0112 /**
0113  * vhost_iotlb_del_range - delete overlapped ranges from vhost IOTLB
0114  * @iotlb: the IOTLB
0115  * @start: start of the IOVA range
0116  * @last: last of IOVA range
0117  */
0118 void vhost_iotlb_del_range(struct vhost_iotlb *iotlb, u64 start, u64 last)
0119 {
0120     struct vhost_iotlb_map *map;
0121 
0122     while ((map = vhost_iotlb_itree_iter_first(&iotlb->root,
0123                            start, last)))
0124         vhost_iotlb_map_free(iotlb, map);
0125 }
0126 EXPORT_SYMBOL_GPL(vhost_iotlb_del_range);
0127 
0128 /**
0129  * vhost_iotlb_init - initialize a vhost IOTLB
0130  * @iotlb: the IOTLB that needs to be initialized
0131  * @limit: maximum number of IOTLB entries
0132  * @flags: VHOST_IOTLB_FLAG_XXX
0133  */
0134 void vhost_iotlb_init(struct vhost_iotlb *iotlb, unsigned int limit,
0135               unsigned int flags)
0136 {
0137     iotlb->root = RB_ROOT_CACHED;
0138     iotlb->limit = limit;
0139     iotlb->nmaps = 0;
0140     iotlb->flags = flags;
0141     INIT_LIST_HEAD(&iotlb->list);
0142 }
0143 EXPORT_SYMBOL_GPL(vhost_iotlb_init);
0144 
0145 /**
0146  * vhost_iotlb_alloc - add a new vhost IOTLB
0147  * @limit: maximum number of IOTLB entries
0148  * @flags: VHOST_IOTLB_FLAG_XXX
0149  *
0150  * Returns an error is memory allocation fails
0151  */
0152 struct vhost_iotlb *vhost_iotlb_alloc(unsigned int limit, unsigned int flags)
0153 {
0154     struct vhost_iotlb *iotlb = kzalloc(sizeof(*iotlb), GFP_KERNEL);
0155 
0156     if (!iotlb)
0157         return NULL;
0158 
0159     vhost_iotlb_init(iotlb, limit, flags);
0160 
0161     return iotlb;
0162 }
0163 EXPORT_SYMBOL_GPL(vhost_iotlb_alloc);
0164 
0165 /**
0166  * vhost_iotlb_reset - reset vhost IOTLB (free all IOTLB entries)
0167  * @iotlb: the IOTLB to be reset
0168  */
0169 void vhost_iotlb_reset(struct vhost_iotlb *iotlb)
0170 {
0171     vhost_iotlb_del_range(iotlb, 0ULL, 0ULL - 1);
0172 }
0173 EXPORT_SYMBOL_GPL(vhost_iotlb_reset);
0174 
0175 /**
0176  * vhost_iotlb_free - reset and free vhost IOTLB
0177  * @iotlb: the IOTLB to be freed
0178  */
0179 void vhost_iotlb_free(struct vhost_iotlb *iotlb)
0180 {
0181     if (iotlb) {
0182         vhost_iotlb_reset(iotlb);
0183         kfree(iotlb);
0184     }
0185 }
0186 EXPORT_SYMBOL_GPL(vhost_iotlb_free);
0187 
0188 /**
0189  * vhost_iotlb_itree_first - return the first overlapped range
0190  * @iotlb: the IOTLB
0191  * @start: start of IOVA range
0192  * @last: last byte in IOVA range
0193  */
0194 struct vhost_iotlb_map *
0195 vhost_iotlb_itree_first(struct vhost_iotlb *iotlb, u64 start, u64 last)
0196 {
0197     return vhost_iotlb_itree_iter_first(&iotlb->root, start, last);
0198 }
0199 EXPORT_SYMBOL_GPL(vhost_iotlb_itree_first);
0200 
0201 /**
0202  * vhost_iotlb_itree_next - return the next overlapped range
0203  * @map: the starting map node
0204  * @start: start of IOVA range
0205  * @last: last byte IOVA range
0206  */
0207 struct vhost_iotlb_map *
0208 vhost_iotlb_itree_next(struct vhost_iotlb_map *map, u64 start, u64 last)
0209 {
0210     return vhost_iotlb_itree_iter_next(map, start, last);
0211 }
0212 EXPORT_SYMBOL_GPL(vhost_iotlb_itree_next);
0213 
0214 MODULE_VERSION(MOD_VERSION);
0215 MODULE_DESCRIPTION(MOD_DESC);
0216 MODULE_AUTHOR(MOD_AUTHOR);
0217 MODULE_LICENSE(MOD_LICENSE);