diff --git a/include/vfn/support.h b/include/vfn/support.h index 050b317..be45f95 100644 --- a/include/vfn/support.h +++ b/include/vfn/support.h @@ -42,6 +42,7 @@ extern "C" { #include #include #include +#include #include #include diff --git a/include/vfn/support/meson.build b/include/vfn/support/meson.build index 6b37088..5a7195d 100644 --- a/include/vfn/support/meson.build +++ b/include/vfn/support/meson.build @@ -10,6 +10,7 @@ vfn_support_headers = files([ 'mem.h', 'mmio.h', 'mutex.h', + 'rwlock.h', 'ticks.h', 'timer.h', ]) diff --git a/include/vfn/support/rwlock.h b/include/vfn/support/rwlock.h new file mode 100644 index 0000000..f72b736 --- /dev/null +++ b/include/vfn/support/rwlock.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later or MIT */ + +/* + * This file is part of libvfn. + * + * Copyright (C) 2022 The libvfn Authors. All Rights Reserved. + * + * This library (libvfn) is dual licensed under the GNU Lesser General + * Public License version 2.1 or later or the MIT license. See the + * COPYING and LICENSE files for more information. + */ + +#ifndef LIBVFN_SUPPORT_RWLOCK_H +#define LIBVFN_SUPPORT_RWLOCK_H + +/* + * DOC: Autolockable read-write lock + * + * Define a __autolock() macro that will lock the given rwlock as well as ensure + * that it is unlocked when going out of scope. This is inspired by the + * polymorphic locking functions in QEMU (include/qemu/lockable.h), but this + * version only supports the pthread_rwlock_t. + */ + +static inline pthread_rwlock_t *__rwlock_auto_rdlock(pthread_rwlock_t *rwlock) +{ + pthread_rwlock_rdlock(rwlock); + return rwlock; +} + +static inline pthread_rwlock_t *__rwlock_auto_wrlock(pthread_rwlock_t *rwlock) +{ + pthread_rwlock_wrlock(rwlock); + return rwlock; +} + +static inline void __rwlock_auto_unlock(pthread_rwlock_t *rwlock) +{ + if (rwlock) + pthread_rwlock_unlock(rwlock); +} + +DEFINE_AUTOPTR(pthread_rwlock_t, __rwlock_auto_unlock) + +/** + * __autordlock - autolock rdlock + * @x: pointer to pthread rdlock + * + * Lock the rdlock and unlock it automatically when going out of scope. + */ +#define __autordlock(x) \ + __autoptr(pthread_rwlock_t) \ + glue(autordlock, __COUNTER__) __attribute__((__unused__)) = __rwlock_auto_rdlock(x) + +/** + * __autowrlock - autolock wrlock + * @x: pointer to pthread wrlock + * + * Lock the wrlock and unlock it automatically when going out of scope. + */ +#define __autowrlock(x) \ + __autoptr(pthread_rwlock_t) \ + glue(autowrlock, __COUNTER__) __attribute__((__unused__)) = __rwlock_auto_wrlock(x) + +#endif /* LIBVFN_SUPPORT_RWLOCK_H */ diff --git a/src/iommu/context.c b/src/iommu/context.c index a77a48c..da852f8 100644 --- a/src/iommu/context.c +++ b/src/iommu/context.c @@ -81,8 +81,6 @@ void iommu_ctx_init(struct iommu_ctx *ctx) ctx->iova_ranges[0].start = IOVA_MIN; ctx->iova_ranges[0].last = IOVA_MAX_39BITS - 1; - pthread_mutex_init(&ctx->lock, NULL); - skiplist_init(&ctx->map.list); - pthread_mutex_init(&ctx->map.lock, NULL); + pthread_rwlock_init(&ctx->map.lock, NULL); } diff --git a/src/iommu/context.h b/src/iommu/context.h index ef2cfee..a3b56c7 100644 --- a/src/iommu/context.h +++ b/src/iommu/context.h @@ -40,7 +40,7 @@ struct iova_mapping { }; struct iova_map { - pthread_mutex_t lock; + pthread_rwlock_t lock; struct skiplist list; }; @@ -48,7 +48,6 @@ struct iommu_ctx { struct iova_map map; struct iommu_ctx_ops ops; - pthread_mutex_t lock; int nranges; struct iommu_iova_range *iova_ranges; }; diff --git a/src/iommu/dma.c b/src/iommu/dma.c index f836588..7ab4d47 100644 --- a/src/iommu/dma.c +++ b/src/iommu/dma.c @@ -41,7 +41,7 @@ static int iova_cmp(const void *vaddr, const struct skiplist_node *n) static int iova_map_add(struct iova_map *map, void *vaddr, size_t len, uint64_t iova, unsigned long flags) { - __autolock(&map->lock); + __autowrlock(&map->lock); struct skiplist_node *update[SKIPLIST_LEVELS] = {}; struct iova_mapping *m; @@ -70,7 +70,7 @@ static int iova_map_add(struct iova_map *map, void *vaddr, size_t len, uint64_t static void iova_map_remove(struct iova_map *map, void *vaddr) { - __autolock(&map->lock); + __autowrlock(&map->lock); struct skiplist_node *n, *update[SKIPLIST_LEVELS] = {}; @@ -83,7 +83,7 @@ static void iova_map_remove(struct iova_map *map, void *vaddr) static struct iova_mapping *iova_map_find(struct iova_map *map, void *vaddr) { - __autolock(&map->lock); + __autordlock(&map->lock); return container_of_or_null(skiplist_find(&map->list, vaddr, iova_cmp, NULL), struct iova_mapping, list); @@ -91,7 +91,7 @@ static struct iova_mapping *iova_map_find(struct iova_map *map, void *vaddr) static void iova_map_clear_with(struct iova_map *map, skiplist_iter_fn fn, void *opaque) { - __autolock(&map->lock); + __autowrlock(&map->lock); skiplist_clear_with(&map->list, fn, opaque); }