2019-05-30 02:57:35 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2007-10-22 03:41:48 +04:00
|
|
|
/*
|
2009-07-01 21:49:06 +04:00
|
|
|
* Copyright © 2006-2009, Intel Corporation.
|
2007-10-22 03:41:48 +04:00
|
|
|
*
|
2008-02-24 02:23:35 +03:00
|
|
|
* Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
|
2007-10-22 03:41:48 +04:00
|
|
|
*/
|
|
|
|
|
2008-09-09 19:37:29 +04:00
|
|
|
#include <linux/iova.h>
|
2015-07-13 14:31:30 +03:00
|
|
|
#include <linux/module.h>
|
2015-01-12 20:51:14 +03:00
|
|
|
#include <linux/slab.h>
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/bitops.h>
|
2017-06-27 19:16:47 +03:00
|
|
|
#include <linux/cpu.h>
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
|
2017-09-21 18:52:46 +03:00
|
|
|
/* The anchor node sits above the top of the usable address space */
|
|
|
|
#define IOVA_ANCHOR ~0UL
|
|
|
|
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
static bool iova_rcache_insert(struct iova_domain *iovad,
|
|
|
|
unsigned long pfn,
|
|
|
|
unsigned long size);
|
|
|
|
static unsigned long iova_rcache_get(struct iova_domain *iovad,
|
|
|
|
unsigned long size,
|
|
|
|
unsigned long limit_pfn);
|
|
|
|
static void init_iova_rcaches(struct iova_domain *iovad);
|
2021-03-25 15:30:00 +03:00
|
|
|
static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
static void free_iova_rcaches(struct iova_domain *iovad);
|
2017-08-10 16:49:44 +03:00
|
|
|
static void fq_destroy_all_entries(struct iova_domain *iovad);
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 00:43:17 +03:00
|
|
|
static void fq_flush_timeout(struct timer_list *t);
|
2021-03-25 15:29:58 +03:00
|
|
|
|
|
|
|
static int iova_cpuhp_dead(unsigned int cpu, struct hlist_node *node)
|
|
|
|
{
|
|
|
|
struct iova_domain *iovad;
|
|
|
|
|
|
|
|
iovad = hlist_entry_safe(node, struct iova_domain, cpuhp_dead);
|
|
|
|
|
|
|
|
free_cpu_cached_iovas(cpu, iovad);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-30 10:44:24 +03:00
|
|
|
static void free_global_cached_iovas(struct iova_domain *iovad);
|
2015-01-12 20:51:14 +03:00
|
|
|
|
2021-03-05 19:35:22 +03:00
|
|
|
static struct iova *to_iova(struct rb_node *node)
|
|
|
|
{
|
|
|
|
return rb_entry(node, struct iova, node);
|
|
|
|
}
|
|
|
|
|
2007-10-22 03:41:48 +04:00
|
|
|
void
|
2015-01-12 20:51:16 +03:00
|
|
|
init_iova_domain(struct iova_domain *iovad, unsigned long granule,
|
2017-09-21 18:52:45 +03:00
|
|
|
unsigned long start_pfn)
|
2007-10-22 03:41:48 +04:00
|
|
|
{
|
2015-01-12 20:51:16 +03:00
|
|
|
/*
|
|
|
|
* IOVA granularity will normally be equal to the smallest
|
|
|
|
* supported IOMMU page size; both *must* be capable of
|
|
|
|
* representing individual CPU pages exactly.
|
|
|
|
*/
|
|
|
|
BUG_ON((granule > PAGE_SIZE) || !is_power_of_2(granule));
|
|
|
|
|
2007-10-22 03:41:48 +04:00
|
|
|
spin_lock_init(&iovad->iova_rbtree_lock);
|
|
|
|
iovad->rbroot = RB_ROOT;
|
2017-09-21 18:52:47 +03:00
|
|
|
iovad->cached_node = &iovad->anchor.node;
|
|
|
|
iovad->cached32_node = &iovad->anchor.node;
|
2015-01-12 20:51:16 +03:00
|
|
|
iovad->granule = granule;
|
2015-01-12 20:51:15 +03:00
|
|
|
iovad->start_pfn = start_pfn;
|
2017-09-21 18:52:45 +03:00
|
|
|
iovad->dma_32bit_pfn = 1UL << (32 - iova_shift(iovad));
|
2018-09-05 07:27:36 +03:00
|
|
|
iovad->max32_alloc_size = iovad->dma_32bit_pfn;
|
2017-08-10 15:44:28 +03:00
|
|
|
iovad->flush_cb = NULL;
|
|
|
|
iovad->fq = NULL;
|
2017-09-21 18:52:46 +03:00
|
|
|
iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR;
|
|
|
|
rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node);
|
|
|
|
rb_insert_color(&iovad->anchor.node, &iovad->rbroot);
|
2021-03-25 15:29:58 +03:00
|
|
|
cpuhp_state_add_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD, &iovad->cpuhp_dead);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
init_iova_rcaches(iovad);
|
2007-10-22 03:41:48 +04:00
|
|
|
}
|
2015-07-13 14:31:29 +03:00
|
|
|
EXPORT_SYMBOL_GPL(init_iova_domain);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
2021-01-06 16:35:06 +03:00
|
|
|
static bool has_iova_flush_queue(struct iova_domain *iovad)
|
iommu/vt-d: Don't queue_iova() if there is no flush queue
Intel VT-d driver was reworked to use common deferred flushing
implementation. Previously there was one global per-cpu flush queue,
afterwards - one per domain.
Before deferring a flush, the queue should be allocated and initialized.
Currently only domains with IOMMU_DOMAIN_DMA type initialize their flush
queue. It's probably worth to init it for static or unmanaged domains
too, but it may be arguable - I'm leaving it to iommu folks.
Prevent queuing an iova flush if the domain doesn't have a queue.
The defensive check seems to be worth to keep even if queue would be
initialized for all kinds of domains. And is easy backportable.
On 4.19.43 stable kernel it has a user-visible effect: previously for
devices in si domain there were crashes, on sata devices:
BUG: spinlock bad magic on CPU#6, swapper/0/1
lock: 0xffff88844f582008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 6 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #1
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x45/0x115
intel_unmap+0x107/0x113
intel_unmap_sg+0x6b/0x76
__ata_qc_complete+0x7f/0x103
ata_qc_complete+0x9b/0x26a
ata_qc_complete_multiple+0xd0/0xe3
ahci_handle_port_interrupt+0x3ee/0x48a
ahci_handle_port_intr+0x73/0xa9
ahci_single_level_irq_intr+0x40/0x60
__handle_irq_event_percpu+0x7f/0x19a
handle_irq_event_percpu+0x32/0x72
handle_irq_event+0x38/0x56
handle_edge_irq+0x102/0x121
handle_irq+0x147/0x15c
do_IRQ+0x66/0xf2
common_interrupt+0xf/0xf
RIP: 0010:__do_softirq+0x8c/0x2df
The same for usb devices that use ehci-pci:
BUG: spinlock bad magic on CPU#0, swapper/0/1
lock: 0xffff88844f402008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #4
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x77/0x145
intel_unmap+0x107/0x113
intel_unmap_page+0xe/0x10
usb_hcd_unmap_urb_setup_for_dma+0x53/0x9d
usb_hcd_unmap_urb_for_dma+0x17/0x100
unmap_urb_for_dma+0x22/0x24
__usb_hcd_giveback_urb+0x51/0xc3
usb_giveback_urb_bh+0x97/0xde
tasklet_action_common.isra.4+0x5f/0xa1
tasklet_action+0x2d/0x30
__do_softirq+0x138/0x2df
irq_exit+0x7d/0x8b
smp_apic_timer_interrupt+0x10f/0x151
apic_timer_interrupt+0xf/0x20
</IRQ>
RIP: 0010:_raw_spin_unlock_irqrestore+0x17/0x39
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Lu Baolu <baolu.lu@linux.intel.com>
Cc: iommu@lists.linux-foundation.org
Cc: <stable@vger.kernel.org> # 4.14+
Fixes: 13cf01744608 ("iommu/vt-d: Make use of iova deferred flushing")
Signed-off-by: Dmitry Safonov <dima@arista.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2019-07-17 00:38:05 +03:00
|
|
|
{
|
|
|
|
return !!iovad->fq;
|
|
|
|
}
|
|
|
|
|
2017-08-10 15:44:28 +03:00
|
|
|
static void free_iova_flush_queue(struct iova_domain *iovad)
|
|
|
|
{
|
iommu/vt-d: Don't queue_iova() if there is no flush queue
Intel VT-d driver was reworked to use common deferred flushing
implementation. Previously there was one global per-cpu flush queue,
afterwards - one per domain.
Before deferring a flush, the queue should be allocated and initialized.
Currently only domains with IOMMU_DOMAIN_DMA type initialize their flush
queue. It's probably worth to init it for static or unmanaged domains
too, but it may be arguable - I'm leaving it to iommu folks.
Prevent queuing an iova flush if the domain doesn't have a queue.
The defensive check seems to be worth to keep even if queue would be
initialized for all kinds of domains. And is easy backportable.
On 4.19.43 stable kernel it has a user-visible effect: previously for
devices in si domain there were crashes, on sata devices:
BUG: spinlock bad magic on CPU#6, swapper/0/1
lock: 0xffff88844f582008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 6 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #1
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x45/0x115
intel_unmap+0x107/0x113
intel_unmap_sg+0x6b/0x76
__ata_qc_complete+0x7f/0x103
ata_qc_complete+0x9b/0x26a
ata_qc_complete_multiple+0xd0/0xe3
ahci_handle_port_interrupt+0x3ee/0x48a
ahci_handle_port_intr+0x73/0xa9
ahci_single_level_irq_intr+0x40/0x60
__handle_irq_event_percpu+0x7f/0x19a
handle_irq_event_percpu+0x32/0x72
handle_irq_event+0x38/0x56
handle_edge_irq+0x102/0x121
handle_irq+0x147/0x15c
do_IRQ+0x66/0xf2
common_interrupt+0xf/0xf
RIP: 0010:__do_softirq+0x8c/0x2df
The same for usb devices that use ehci-pci:
BUG: spinlock bad magic on CPU#0, swapper/0/1
lock: 0xffff88844f402008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #4
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x77/0x145
intel_unmap+0x107/0x113
intel_unmap_page+0xe/0x10
usb_hcd_unmap_urb_setup_for_dma+0x53/0x9d
usb_hcd_unmap_urb_for_dma+0x17/0x100
unmap_urb_for_dma+0x22/0x24
__usb_hcd_giveback_urb+0x51/0xc3
usb_giveback_urb_bh+0x97/0xde
tasklet_action_common.isra.4+0x5f/0xa1
tasklet_action+0x2d/0x30
__do_softirq+0x138/0x2df
irq_exit+0x7d/0x8b
smp_apic_timer_interrupt+0x10f/0x151
apic_timer_interrupt+0xf/0x20
</IRQ>
RIP: 0010:_raw_spin_unlock_irqrestore+0x17/0x39
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Lu Baolu <baolu.lu@linux.intel.com>
Cc: iommu@lists.linux-foundation.org
Cc: <stable@vger.kernel.org> # 4.14+
Fixes: 13cf01744608 ("iommu/vt-d: Make use of iova deferred flushing")
Signed-off-by: Dmitry Safonov <dima@arista.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2019-07-17 00:38:05 +03:00
|
|
|
if (!has_iova_flush_queue(iovad))
|
2017-08-10 15:44:28 +03:00
|
|
|
return;
|
|
|
|
|
2017-08-10 17:58:18 +03:00
|
|
|
if (timer_pending(&iovad->fq_timer))
|
|
|
|
del_timer(&iovad->fq_timer);
|
|
|
|
|
2017-08-10 16:49:44 +03:00
|
|
|
fq_destroy_all_entries(iovad);
|
2017-08-10 17:58:18 +03:00
|
|
|
|
2017-08-10 15:44:28 +03:00
|
|
|
free_percpu(iovad->fq);
|
|
|
|
|
|
|
|
iovad->fq = NULL;
|
|
|
|
iovad->flush_cb = NULL;
|
|
|
|
iovad->entry_dtor = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int init_iova_flush_queue(struct iova_domain *iovad,
|
|
|
|
iova_flush_cb flush_cb, iova_entry_dtor entry_dtor)
|
|
|
|
{
|
iommu/vt-d: Don't queue_iova() if there is no flush queue
Intel VT-d driver was reworked to use common deferred flushing
implementation. Previously there was one global per-cpu flush queue,
afterwards - one per domain.
Before deferring a flush, the queue should be allocated and initialized.
Currently only domains with IOMMU_DOMAIN_DMA type initialize their flush
queue. It's probably worth to init it for static or unmanaged domains
too, but it may be arguable - I'm leaving it to iommu folks.
Prevent queuing an iova flush if the domain doesn't have a queue.
The defensive check seems to be worth to keep even if queue would be
initialized for all kinds of domains. And is easy backportable.
On 4.19.43 stable kernel it has a user-visible effect: previously for
devices in si domain there were crashes, on sata devices:
BUG: spinlock bad magic on CPU#6, swapper/0/1
lock: 0xffff88844f582008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 6 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #1
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x45/0x115
intel_unmap+0x107/0x113
intel_unmap_sg+0x6b/0x76
__ata_qc_complete+0x7f/0x103
ata_qc_complete+0x9b/0x26a
ata_qc_complete_multiple+0xd0/0xe3
ahci_handle_port_interrupt+0x3ee/0x48a
ahci_handle_port_intr+0x73/0xa9
ahci_single_level_irq_intr+0x40/0x60
__handle_irq_event_percpu+0x7f/0x19a
handle_irq_event_percpu+0x32/0x72
handle_irq_event+0x38/0x56
handle_edge_irq+0x102/0x121
handle_irq+0x147/0x15c
do_IRQ+0x66/0xf2
common_interrupt+0xf/0xf
RIP: 0010:__do_softirq+0x8c/0x2df
The same for usb devices that use ehci-pci:
BUG: spinlock bad magic on CPU#0, swapper/0/1
lock: 0xffff88844f402008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #4
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x77/0x145
intel_unmap+0x107/0x113
intel_unmap_page+0xe/0x10
usb_hcd_unmap_urb_setup_for_dma+0x53/0x9d
usb_hcd_unmap_urb_for_dma+0x17/0x100
unmap_urb_for_dma+0x22/0x24
__usb_hcd_giveback_urb+0x51/0xc3
usb_giveback_urb_bh+0x97/0xde
tasklet_action_common.isra.4+0x5f/0xa1
tasklet_action+0x2d/0x30
__do_softirq+0x138/0x2df
irq_exit+0x7d/0x8b
smp_apic_timer_interrupt+0x10f/0x151
apic_timer_interrupt+0xf/0x20
</IRQ>
RIP: 0010:_raw_spin_unlock_irqrestore+0x17/0x39
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Lu Baolu <baolu.lu@linux.intel.com>
Cc: iommu@lists.linux-foundation.org
Cc: <stable@vger.kernel.org> # 4.14+
Fixes: 13cf01744608 ("iommu/vt-d: Make use of iova deferred flushing")
Signed-off-by: Dmitry Safonov <dima@arista.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2019-07-17 00:38:05 +03:00
|
|
|
struct iova_fq __percpu *queue;
|
2017-08-10 15:44:28 +03:00
|
|
|
int cpu;
|
|
|
|
|
2017-08-10 17:14:59 +03:00
|
|
|
atomic64_set(&iovad->fq_flush_start_cnt, 0);
|
|
|
|
atomic64_set(&iovad->fq_flush_finish_cnt, 0);
|
|
|
|
|
iommu/vt-d: Don't queue_iova() if there is no flush queue
Intel VT-d driver was reworked to use common deferred flushing
implementation. Previously there was one global per-cpu flush queue,
afterwards - one per domain.
Before deferring a flush, the queue should be allocated and initialized.
Currently only domains with IOMMU_DOMAIN_DMA type initialize their flush
queue. It's probably worth to init it for static or unmanaged domains
too, but it may be arguable - I'm leaving it to iommu folks.
Prevent queuing an iova flush if the domain doesn't have a queue.
The defensive check seems to be worth to keep even if queue would be
initialized for all kinds of domains. And is easy backportable.
On 4.19.43 stable kernel it has a user-visible effect: previously for
devices in si domain there were crashes, on sata devices:
BUG: spinlock bad magic on CPU#6, swapper/0/1
lock: 0xffff88844f582008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 6 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #1
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x45/0x115
intel_unmap+0x107/0x113
intel_unmap_sg+0x6b/0x76
__ata_qc_complete+0x7f/0x103
ata_qc_complete+0x9b/0x26a
ata_qc_complete_multiple+0xd0/0xe3
ahci_handle_port_interrupt+0x3ee/0x48a
ahci_handle_port_intr+0x73/0xa9
ahci_single_level_irq_intr+0x40/0x60
__handle_irq_event_percpu+0x7f/0x19a
handle_irq_event_percpu+0x32/0x72
handle_irq_event+0x38/0x56
handle_edge_irq+0x102/0x121
handle_irq+0x147/0x15c
do_IRQ+0x66/0xf2
common_interrupt+0xf/0xf
RIP: 0010:__do_softirq+0x8c/0x2df
The same for usb devices that use ehci-pci:
BUG: spinlock bad magic on CPU#0, swapper/0/1
lock: 0xffff88844f402008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #4
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x77/0x145
intel_unmap+0x107/0x113
intel_unmap_page+0xe/0x10
usb_hcd_unmap_urb_setup_for_dma+0x53/0x9d
usb_hcd_unmap_urb_for_dma+0x17/0x100
unmap_urb_for_dma+0x22/0x24
__usb_hcd_giveback_urb+0x51/0xc3
usb_giveback_urb_bh+0x97/0xde
tasklet_action_common.isra.4+0x5f/0xa1
tasklet_action+0x2d/0x30
__do_softirq+0x138/0x2df
irq_exit+0x7d/0x8b
smp_apic_timer_interrupt+0x10f/0x151
apic_timer_interrupt+0xf/0x20
</IRQ>
RIP: 0010:_raw_spin_unlock_irqrestore+0x17/0x39
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Lu Baolu <baolu.lu@linux.intel.com>
Cc: iommu@lists.linux-foundation.org
Cc: <stable@vger.kernel.org> # 4.14+
Fixes: 13cf01744608 ("iommu/vt-d: Make use of iova deferred flushing")
Signed-off-by: Dmitry Safonov <dima@arista.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2019-07-17 00:38:05 +03:00
|
|
|
queue = alloc_percpu(struct iova_fq);
|
|
|
|
if (!queue)
|
2017-08-10 15:44:28 +03:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
iovad->flush_cb = flush_cb;
|
|
|
|
iovad->entry_dtor = entry_dtor;
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
struct iova_fq *fq;
|
|
|
|
|
iommu/vt-d: Don't queue_iova() if there is no flush queue
Intel VT-d driver was reworked to use common deferred flushing
implementation. Previously there was one global per-cpu flush queue,
afterwards - one per domain.
Before deferring a flush, the queue should be allocated and initialized.
Currently only domains with IOMMU_DOMAIN_DMA type initialize their flush
queue. It's probably worth to init it for static or unmanaged domains
too, but it may be arguable - I'm leaving it to iommu folks.
Prevent queuing an iova flush if the domain doesn't have a queue.
The defensive check seems to be worth to keep even if queue would be
initialized for all kinds of domains. And is easy backportable.
On 4.19.43 stable kernel it has a user-visible effect: previously for
devices in si domain there were crashes, on sata devices:
BUG: spinlock bad magic on CPU#6, swapper/0/1
lock: 0xffff88844f582008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 6 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #1
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x45/0x115
intel_unmap+0x107/0x113
intel_unmap_sg+0x6b/0x76
__ata_qc_complete+0x7f/0x103
ata_qc_complete+0x9b/0x26a
ata_qc_complete_multiple+0xd0/0xe3
ahci_handle_port_interrupt+0x3ee/0x48a
ahci_handle_port_intr+0x73/0xa9
ahci_single_level_irq_intr+0x40/0x60
__handle_irq_event_percpu+0x7f/0x19a
handle_irq_event_percpu+0x32/0x72
handle_irq_event+0x38/0x56
handle_edge_irq+0x102/0x121
handle_irq+0x147/0x15c
do_IRQ+0x66/0xf2
common_interrupt+0xf/0xf
RIP: 0010:__do_softirq+0x8c/0x2df
The same for usb devices that use ehci-pci:
BUG: spinlock bad magic on CPU#0, swapper/0/1
lock: 0xffff88844f402008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #4
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x77/0x145
intel_unmap+0x107/0x113
intel_unmap_page+0xe/0x10
usb_hcd_unmap_urb_setup_for_dma+0x53/0x9d
usb_hcd_unmap_urb_for_dma+0x17/0x100
unmap_urb_for_dma+0x22/0x24
__usb_hcd_giveback_urb+0x51/0xc3
usb_giveback_urb_bh+0x97/0xde
tasklet_action_common.isra.4+0x5f/0xa1
tasklet_action+0x2d/0x30
__do_softirq+0x138/0x2df
irq_exit+0x7d/0x8b
smp_apic_timer_interrupt+0x10f/0x151
apic_timer_interrupt+0xf/0x20
</IRQ>
RIP: 0010:_raw_spin_unlock_irqrestore+0x17/0x39
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Lu Baolu <baolu.lu@linux.intel.com>
Cc: iommu@lists.linux-foundation.org
Cc: <stable@vger.kernel.org> # 4.14+
Fixes: 13cf01744608 ("iommu/vt-d: Make use of iova deferred flushing")
Signed-off-by: Dmitry Safonov <dima@arista.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2019-07-17 00:38:05 +03:00
|
|
|
fq = per_cpu_ptr(queue, cpu);
|
2017-08-10 15:44:28 +03:00
|
|
|
fq->head = 0;
|
|
|
|
fq->tail = 0;
|
2017-08-10 17:31:17 +03:00
|
|
|
|
|
|
|
spin_lock_init(&fq->lock);
|
2017-08-10 15:44:28 +03:00
|
|
|
}
|
iommu/vt-d: Don't queue_iova() if there is no flush queue
Intel VT-d driver was reworked to use common deferred flushing
implementation. Previously there was one global per-cpu flush queue,
afterwards - one per domain.
Before deferring a flush, the queue should be allocated and initialized.
Currently only domains with IOMMU_DOMAIN_DMA type initialize their flush
queue. It's probably worth to init it for static or unmanaged domains
too, but it may be arguable - I'm leaving it to iommu folks.
Prevent queuing an iova flush if the domain doesn't have a queue.
The defensive check seems to be worth to keep even if queue would be
initialized for all kinds of domains. And is easy backportable.
On 4.19.43 stable kernel it has a user-visible effect: previously for
devices in si domain there were crashes, on sata devices:
BUG: spinlock bad magic on CPU#6, swapper/0/1
lock: 0xffff88844f582008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 6 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #1
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x45/0x115
intel_unmap+0x107/0x113
intel_unmap_sg+0x6b/0x76
__ata_qc_complete+0x7f/0x103
ata_qc_complete+0x9b/0x26a
ata_qc_complete_multiple+0xd0/0xe3
ahci_handle_port_interrupt+0x3ee/0x48a
ahci_handle_port_intr+0x73/0xa9
ahci_single_level_irq_intr+0x40/0x60
__handle_irq_event_percpu+0x7f/0x19a
handle_irq_event_percpu+0x32/0x72
handle_irq_event+0x38/0x56
handle_edge_irq+0x102/0x121
handle_irq+0x147/0x15c
do_IRQ+0x66/0xf2
common_interrupt+0xf/0xf
RIP: 0010:__do_softirq+0x8c/0x2df
The same for usb devices that use ehci-pci:
BUG: spinlock bad magic on CPU#0, swapper/0/1
lock: 0xffff88844f402008, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.19.43 #4
Call Trace:
<IRQ>
dump_stack+0x61/0x7e
spin_bug+0x9d/0xa3
do_raw_spin_lock+0x22/0x8e
_raw_spin_lock_irqsave+0x32/0x3a
queue_iova+0x77/0x145
intel_unmap+0x107/0x113
intel_unmap_page+0xe/0x10
usb_hcd_unmap_urb_setup_for_dma+0x53/0x9d
usb_hcd_unmap_urb_for_dma+0x17/0x100
unmap_urb_for_dma+0x22/0x24
__usb_hcd_giveback_urb+0x51/0xc3
usb_giveback_urb_bh+0x97/0xde
tasklet_action_common.isra.4+0x5f/0xa1
tasklet_action+0x2d/0x30
__do_softirq+0x138/0x2df
irq_exit+0x7d/0x8b
smp_apic_timer_interrupt+0x10f/0x151
apic_timer_interrupt+0xf/0x20
</IRQ>
RIP: 0010:_raw_spin_unlock_irqrestore+0x17/0x39
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Lu Baolu <baolu.lu@linux.intel.com>
Cc: iommu@lists.linux-foundation.org
Cc: <stable@vger.kernel.org> # 4.14+
Fixes: 13cf01744608 ("iommu/vt-d: Make use of iova deferred flushing")
Signed-off-by: Dmitry Safonov <dima@arista.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2019-07-17 00:38:05 +03:00
|
|
|
|
|
|
|
iovad->fq = queue;
|
2017-08-10 15:44:28 +03:00
|
|
|
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 00:43:17 +03:00
|
|
|
timer_setup(&iovad->fq_timer, fq_flush_timeout, 0);
|
2017-08-10 17:58:18 +03:00
|
|
|
atomic_set(&iovad->fq_timer_on, 0);
|
|
|
|
|
2017-08-10 15:44:28 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-22 03:41:48 +04:00
|
|
|
static struct rb_node *
|
2017-09-21 18:52:47 +03:00
|
|
|
__get_cached_rbnode(struct iova_domain *iovad, unsigned long limit_pfn)
|
2007-10-22 03:41:48 +04:00
|
|
|
{
|
2017-09-21 18:52:47 +03:00
|
|
|
if (limit_pfn <= iovad->dma_32bit_pfn)
|
|
|
|
return iovad->cached32_node;
|
2017-09-21 18:52:44 +03:00
|
|
|
|
2017-09-21 18:52:47 +03:00
|
|
|
return iovad->cached_node;
|
2007-10-22 03:41:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-09-21 18:52:44 +03:00
|
|
|
__cached_rbnode_insert_update(struct iova_domain *iovad, struct iova *new)
|
2007-10-22 03:41:48 +04:00
|
|
|
{
|
2017-09-21 18:52:44 +03:00
|
|
|
if (new->pfn_hi < iovad->dma_32bit_pfn)
|
|
|
|
iovad->cached32_node = &new->node;
|
|
|
|
else
|
|
|
|
iovad->cached_node = &new->node;
|
2007-10-22 03:41:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
__cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free)
|
|
|
|
{
|
|
|
|
struct iova *cached_iova;
|
|
|
|
|
2021-03-05 19:35:22 +03:00
|
|
|
cached_iova = to_iova(iovad->cached32_node);
|
2019-07-20 21:08:48 +03:00
|
|
|
if (free == cached_iova ||
|
|
|
|
(free->pfn_hi < iovad->dma_32bit_pfn &&
|
|
|
|
free->pfn_lo >= cached_iova->pfn_lo)) {
|
2017-09-21 18:52:44 +03:00
|
|
|
iovad->cached32_node = rb_next(&free->node);
|
2018-09-05 07:27:36 +03:00
|
|
|
iovad->max32_alloc_size = iovad->dma_32bit_pfn;
|
|
|
|
}
|
2017-09-21 18:52:44 +03:00
|
|
|
|
2021-03-05 19:35:22 +03:00
|
|
|
cached_iova = to_iova(iovad->cached_node);
|
2017-09-21 18:52:47 +03:00
|
|
|
if (free->pfn_lo >= cached_iova->pfn_lo)
|
2017-09-21 18:52:44 +03:00
|
|
|
iovad->cached_node = rb_next(&free->node);
|
2007-10-22 03:41:48 +04:00
|
|
|
}
|
|
|
|
|
2021-03-05 19:35:23 +03:00
|
|
|
static struct rb_node *iova_find_limit(struct iova_domain *iovad, unsigned long limit_pfn)
|
|
|
|
{
|
|
|
|
struct rb_node *node, *next;
|
|
|
|
/*
|
|
|
|
* Ideally what we'd like to judge here is whether limit_pfn is close
|
|
|
|
* enough to the highest-allocated IOVA that starting the allocation
|
|
|
|
* walk from the anchor node will be quicker than this initial work to
|
|
|
|
* find an exact starting point (especially if that ends up being the
|
|
|
|
* anchor node anyway). This is an incredibly crude approximation which
|
|
|
|
* only really helps the most likely case, but is at least trivially easy.
|
|
|
|
*/
|
|
|
|
if (limit_pfn > iovad->dma_32bit_pfn)
|
|
|
|
return &iovad->anchor.node;
|
|
|
|
|
|
|
|
node = iovad->rbroot.rb_node;
|
|
|
|
while (to_iova(node)->pfn_hi < limit_pfn)
|
|
|
|
node = node->rb_right;
|
|
|
|
|
|
|
|
search_left:
|
|
|
|
while (node->rb_left && to_iova(node->rb_left)->pfn_lo >= limit_pfn)
|
|
|
|
node = node->rb_left;
|
|
|
|
|
|
|
|
if (!node->rb_left)
|
|
|
|
return node;
|
|
|
|
|
|
|
|
next = node->rb_left;
|
|
|
|
while (next->rb_right) {
|
|
|
|
next = next->rb_right;
|
|
|
|
if (to_iova(next)->pfn_lo >= limit_pfn) {
|
|
|
|
node = next;
|
|
|
|
goto search_left;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2017-02-24 14:13:37 +03:00
|
|
|
/* Insert the iova into domain rbtree by holding writer lock */
|
|
|
|
static void
|
|
|
|
iova_insert_rbtree(struct rb_root *root, struct iova *iova,
|
|
|
|
struct rb_node *start)
|
|
|
|
{
|
|
|
|
struct rb_node **new, *parent = NULL;
|
|
|
|
|
|
|
|
new = (start) ? &start : &(root->rb_node);
|
|
|
|
/* Figure out where to put new node */
|
|
|
|
while (*new) {
|
2021-03-05 19:35:22 +03:00
|
|
|
struct iova *this = to_iova(*new);
|
2017-02-24 14:13:37 +03:00
|
|
|
|
|
|
|
parent = *new;
|
|
|
|
|
|
|
|
if (iova->pfn_lo < this->pfn_lo)
|
|
|
|
new = &((*new)->rb_left);
|
|
|
|
else if (iova->pfn_lo > this->pfn_lo)
|
|
|
|
new = &((*new)->rb_right);
|
|
|
|
else {
|
|
|
|
WARN_ON(1); /* this should not happen */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Add new node and rebalance tree. */
|
|
|
|
rb_link_node(&iova->node, parent, new);
|
|
|
|
rb_insert_color(&iova->node, root);
|
|
|
|
}
|
|
|
|
|
2008-03-05 02:22:04 +03:00
|
|
|
static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
|
|
|
|
unsigned long size, unsigned long limit_pfn,
|
|
|
|
struct iova *new, bool size_aligned)
|
2007-10-22 03:41:48 +04:00
|
|
|
{
|
2017-09-21 18:52:47 +03:00
|
|
|
struct rb_node *curr, *prev;
|
|
|
|
struct iova *curr_iova;
|
2007-10-22 03:41:48 +04:00
|
|
|
unsigned long flags;
|
2020-09-30 10:44:23 +03:00
|
|
|
unsigned long new_pfn, retry_pfn;
|
2017-09-21 18:52:43 +03:00
|
|
|
unsigned long align_mask = ~0UL;
|
2020-09-30 10:44:23 +03:00
|
|
|
unsigned long high_pfn = limit_pfn, low_pfn = iovad->start_pfn;
|
2017-09-21 18:52:43 +03:00
|
|
|
|
|
|
|
if (size_aligned)
|
|
|
|
align_mask <<= fls_long(size - 1);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
|
|
|
/* Walk the tree backwards */
|
|
|
|
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
|
2018-09-05 07:27:36 +03:00
|
|
|
if (limit_pfn <= iovad->dma_32bit_pfn &&
|
|
|
|
size >= iovad->max32_alloc_size)
|
|
|
|
goto iova32_full;
|
|
|
|
|
2017-09-21 18:52:47 +03:00
|
|
|
curr = __get_cached_rbnode(iovad, limit_pfn);
|
2021-03-05 19:35:22 +03:00
|
|
|
curr_iova = to_iova(curr);
|
2020-09-30 10:44:23 +03:00
|
|
|
retry_pfn = curr_iova->pfn_hi + 1;
|
|
|
|
|
|
|
|
retry:
|
2017-09-21 18:52:47 +03:00
|
|
|
do {
|
2020-09-30 10:44:23 +03:00
|
|
|
high_pfn = min(high_pfn, curr_iova->pfn_lo);
|
|
|
|
new_pfn = (high_pfn - size) & align_mask;
|
2008-03-05 02:22:04 +03:00
|
|
|
prev = curr;
|
2007-10-22 03:41:48 +04:00
|
|
|
curr = rb_prev(curr);
|
2021-03-05 19:35:22 +03:00
|
|
|
curr_iova = to_iova(curr);
|
2020-09-30 10:44:23 +03:00
|
|
|
} while (curr && new_pfn <= curr_iova->pfn_hi && new_pfn >= low_pfn);
|
|
|
|
|
|
|
|
if (high_pfn < size || new_pfn < low_pfn) {
|
|
|
|
if (low_pfn == iovad->start_pfn && retry_pfn < limit_pfn) {
|
|
|
|
high_pfn = limit_pfn;
|
|
|
|
low_pfn = retry_pfn;
|
2021-03-05 19:35:23 +03:00
|
|
|
curr = iova_find_limit(iovad, limit_pfn);
|
2021-03-05 19:35:22 +03:00
|
|
|
curr_iova = to_iova(curr);
|
2020-09-30 10:44:23 +03:00
|
|
|
goto retry;
|
|
|
|
}
|
2019-03-20 21:57:23 +03:00
|
|
|
iovad->max32_alloc_size = size;
|
2018-09-05 07:27:36 +03:00
|
|
|
goto iova32_full;
|
2019-03-20 21:57:23 +03:00
|
|
|
}
|
2007-10-22 03:41:58 +04:00
|
|
|
|
|
|
|
/* pfn_lo will point to size aligned address if size_aligned is set */
|
2017-09-21 18:52:43 +03:00
|
|
|
new->pfn_lo = new_pfn;
|
2007-10-22 03:41:58 +04:00
|
|
|
new->pfn_hi = new->pfn_lo + size - 1;
|
2007-10-22 03:41:48 +04:00
|
|
|
|
2017-02-24 14:13:37 +03:00
|
|
|
/* If we have 'prev', it's a valid place to start the insertion. */
|
|
|
|
iova_insert_rbtree(&iovad->rbroot, new, prev);
|
2017-09-21 18:52:44 +03:00
|
|
|
__cached_rbnode_insert_update(iovad, new);
|
2008-03-05 02:22:04 +03:00
|
|
|
|
2007-10-22 03:41:48 +04:00
|
|
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
|
|
|
return 0;
|
2018-09-05 07:27:36 +03:00
|
|
|
|
|
|
|
iova32_full:
|
|
|
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
|
|
|
return -ENOMEM;
|
2007-10-22 03:41:48 +04:00
|
|
|
}
|
|
|
|
|
2015-07-13 14:31:28 +03:00
|
|
|
static struct kmem_cache *iova_cache;
|
|
|
|
static unsigned int iova_cache_users;
|
|
|
|
static DEFINE_MUTEX(iova_cache_mutex);
|
|
|
|
|
2020-12-03 21:34:51 +03:00
|
|
|
static struct iova *alloc_iova_mem(void)
|
2015-07-13 14:31:28 +03:00
|
|
|
{
|
iommu/iova: Silence warnings under memory pressure
When running heavy memory pressure workloads, this 5+ old system is
throwing endless warnings below because disk IO is too slow to recover
from swapping. Since the volume from alloc_iova_fast() could be large,
once it calls printk(), it will trigger disk IO (writing to the log
files) and pending softirqs which could cause an infinite loop and make
no progress for days by the ongoimng memory reclaim. This is the counter
part for Intel where the AMD part has already been merged. See the
commit 3d708895325b ("iommu/amd: Silence warnings under memory
pressure"). Since the allocation failure will be reported in
intel_alloc_iova(), so just call dev_err_once() there because even the
"ratelimited" is too much, and silence the one in alloc_iova_mem() to
avoid the expensive warn_alloc().
hpsa 0000:03:00.0: DMAR: Allocating 1-page iova failed
hpsa 0000:03:00.0: DMAR: Allocating 1-page iova failed
hpsa 0000:03:00.0: DMAR: Allocating 1-page iova failed
hpsa 0000:03:00.0: DMAR: Allocating 1-page iova failed
hpsa 0000:03:00.0: DMAR: Allocating 1-page iova failed
hpsa 0000:03:00.0: DMAR: Allocating 1-page iova failed
hpsa 0000:03:00.0: DMAR: Allocating 1-page iova failed
hpsa 0000:03:00.0: DMAR: Allocating 1-page iova failed
slab_out_of_memory: 66 callbacks suppressed
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
cache: iommu_iova, object size: 40, buffer size: 448, default order:
0, min order: 0
node 0: slabs: 1822, objs: 16398, free: 0
node 1: slabs: 2051, objs: 18459, free: 31
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
cache: iommu_iova, object size: 40, buffer size: 448, default order:
0, min order: 0
node 0: slabs: 1822, objs: 16398, free: 0
node 1: slabs: 2051, objs: 18459, free: 31
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
cache: iommu_iova, object size: 40, buffer size: 448, default order:
0, min order: 0
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
cache: skbuff_head_cache, object size: 208, buffer size: 640, default
order: 0, min order: 0
cache: skbuff_head_cache, object size: 208, buffer size: 640, default
order: 0, min order: 0
cache: skbuff_head_cache, object size: 208, buffer size: 640, default
order: 0, min order: 0
cache: skbuff_head_cache, object size: 208, buffer size: 640, default
order: 0, min order: 0
node 0: slabs: 697, objs: 4182, free: 0
node 0: slabs: 697, objs: 4182, free: 0
node 0: slabs: 697, objs: 4182, free: 0
node 0: slabs: 697, objs: 4182, free: 0
node 1: slabs: 381, objs: 2286, free: 27
node 1: slabs: 381, objs: 2286, free: 27
node 1: slabs: 381, objs: 2286, free: 27
node 1: slabs: 381, objs: 2286, free: 27
node 0: slabs: 1822, objs: 16398, free: 0
cache: skbuff_head_cache, object size: 208, buffer size: 640, default
order: 0, min order: 0
node 1: slabs: 2051, objs: 18459, free: 31
node 0: slabs: 697, objs: 4182, free: 0
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
node 1: slabs: 381, objs: 2286, free: 27
cache: skbuff_head_cache, object size: 208, buffer size: 640, default
order: 0, min order: 0
node 0: slabs: 697, objs: 4182, free: 0
node 1: slabs: 381, objs: 2286, free: 27
hpsa 0000:03:00.0: DMAR: Allocating 1-page iova failed
warn_alloc: 96 callbacks suppressed
kworker/11:1H: page allocation failure: order:0,
mode:0xa20(GFP_ATOMIC), nodemask=(null),cpuset=/,mems_allowed=0-1
CPU: 11 PID: 1642 Comm: kworker/11:1H Tainted: G B
Hardware name: HP ProLiant XL420 Gen9/ProLiant XL420 Gen9, BIOS U19
12/27/2015
Workqueue: kblockd blk_mq_run_work_fn
Call Trace:
dump_stack+0xa0/0xea
warn_alloc.cold.94+0x8a/0x12d
__alloc_pages_slowpath+0x1750/0x1870
__alloc_pages_nodemask+0x58a/0x710
alloc_pages_current+0x9c/0x110
alloc_slab_page+0xc9/0x760
allocate_slab+0x48f/0x5d0
new_slab+0x46/0x70
___slab_alloc+0x4ab/0x7b0
__slab_alloc+0x43/0x70
kmem_cache_alloc+0x2dd/0x450
SLUB: Unable to allocate memory on node -1, gfp=0xa20(GFP_ATOMIC)
alloc_iova+0x33/0x210
cache: skbuff_head_cache, object size: 208, buffer size: 640, default
order: 0, min order: 0
node 0: slabs: 697, objs: 4182, free: 0
alloc_iova_fast+0x62/0x3d1
node 1: slabs: 381, objs: 2286, free: 27
intel_alloc_iova+0xce/0xe0
intel_map_sg+0xed/0x410
scsi_dma_map+0xd7/0x160
scsi_queue_rq+0xbf7/0x1310
blk_mq_dispatch_rq_list+0x4d9/0xbc0
blk_mq_sched_dispatch_requests+0x24a/0x300
__blk_mq_run_hw_queue+0x156/0x230
blk_mq_run_work_fn+0x3b/0x40
process_one_work+0x579/0xb90
worker_thread+0x63/0x5b0
kthread+0x1e6/0x210
ret_from_fork+0x3a/0x50
Mem-Info:
active_anon:2422723 inactive_anon:361971 isolated_anon:34403
active_file:2285 inactive_file:1838 isolated_file:0
unevictable:0 dirty:1 writeback:5 unstable:0
slab_reclaimable:13972 slab_unreclaimable:453879
mapped:2380 shmem:154 pagetables:6948 bounce:0
free:19133 free_pcp:7363 free_cma:0
Signed-off-by: Qian Cai <cai@lca.pw>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2019-11-22 22:16:54 +03:00
|
|
|
return kmem_cache_zalloc(iova_cache, GFP_ATOMIC | __GFP_NOWARN);
|
2015-07-13 14:31:28 +03:00
|
|
|
}
|
|
|
|
|
2020-12-03 21:34:52 +03:00
|
|
|
static void free_iova_mem(struct iova *iova)
|
2015-07-13 14:31:28 +03:00
|
|
|
{
|
2017-09-21 18:52:46 +03:00
|
|
|
if (iova->pfn_lo != IOVA_ANCHOR)
|
|
|
|
kmem_cache_free(iova_cache, iova);
|
2015-07-13 14:31:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int iova_cache_get(void)
|
|
|
|
{
|
|
|
|
mutex_lock(&iova_cache_mutex);
|
|
|
|
if (!iova_cache_users) {
|
2021-03-25 15:29:58 +03:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = cpuhp_setup_state_multi(CPUHP_IOMMU_IOVA_DEAD, "iommu/iova:dead", NULL,
|
|
|
|
iova_cpuhp_dead);
|
|
|
|
if (ret) {
|
|
|
|
mutex_unlock(&iova_cache_mutex);
|
|
|
|
pr_err("Couldn't register cpuhp handler\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-07-13 14:31:28 +03:00
|
|
|
iova_cache = kmem_cache_create(
|
|
|
|
"iommu_iova", sizeof(struct iova), 0,
|
|
|
|
SLAB_HWCACHE_ALIGN, NULL);
|
|
|
|
if (!iova_cache) {
|
2021-03-25 15:29:58 +03:00
|
|
|
cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
|
2015-07-13 14:31:28 +03:00
|
|
|
mutex_unlock(&iova_cache_mutex);
|
2020-05-07 19:18:03 +03:00
|
|
|
pr_err("Couldn't create iova cache\n");
|
2015-07-13 14:31:28 +03:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
iova_cache_users++;
|
|
|
|
mutex_unlock(&iova_cache_mutex);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-07-13 14:31:29 +03:00
|
|
|
EXPORT_SYMBOL_GPL(iova_cache_get);
|
2015-07-13 14:31:28 +03:00
|
|
|
|
|
|
|
void iova_cache_put(void)
|
|
|
|
{
|
|
|
|
mutex_lock(&iova_cache_mutex);
|
|
|
|
if (WARN_ON(!iova_cache_users)) {
|
|
|
|
mutex_unlock(&iova_cache_mutex);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
iova_cache_users--;
|
2021-03-25 15:29:58 +03:00
|
|
|
if (!iova_cache_users) {
|
|
|
|
cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
|
2015-07-13 14:31:28 +03:00
|
|
|
kmem_cache_destroy(iova_cache);
|
2021-03-25 15:29:58 +03:00
|
|
|
}
|
2015-07-13 14:31:28 +03:00
|
|
|
mutex_unlock(&iova_cache_mutex);
|
|
|
|
}
|
2015-07-13 14:31:29 +03:00
|
|
|
EXPORT_SYMBOL_GPL(iova_cache_put);
|
2015-07-13 14:31:28 +03:00
|
|
|
|
2007-10-22 03:41:48 +04:00
|
|
|
/**
|
|
|
|
* alloc_iova - allocates an iova
|
2012-07-21 21:21:32 +04:00
|
|
|
* @iovad: - iova domain in question
|
|
|
|
* @size: - size of page frames to allocate
|
|
|
|
* @limit_pfn: - max limit address
|
|
|
|
* @size_aligned: - set if size_aligned address range is required
|
2015-01-12 20:51:15 +03:00
|
|
|
* This function allocates an iova in the range iovad->start_pfn to limit_pfn,
|
|
|
|
* searching top-down from limit_pfn to iovad->start_pfn. If the size_aligned
|
2007-10-22 03:41:58 +04:00
|
|
|
* flag is set then the allocated address iova->pfn_lo will be naturally
|
|
|
|
* aligned on roundup_power_of_two(size).
|
2007-10-22 03:41:48 +04:00
|
|
|
*/
|
|
|
|
struct iova *
|
|
|
|
alloc_iova(struct iova_domain *iovad, unsigned long size,
|
2007-10-22 03:41:58 +04:00
|
|
|
unsigned long limit_pfn,
|
|
|
|
bool size_aligned)
|
2007-10-22 03:41:48 +04:00
|
|
|
{
|
|
|
|
struct iova *new_iova;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
new_iova = alloc_iova_mem();
|
|
|
|
if (!new_iova)
|
|
|
|
return NULL;
|
|
|
|
|
2017-05-16 14:26:48 +03:00
|
|
|
ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn + 1,
|
2008-03-05 02:22:04 +03:00
|
|
|
new_iova, size_aligned);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
free_iova_mem(new_iova);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_iova;
|
|
|
|
}
|
2015-07-13 14:31:29 +03:00
|
|
|
EXPORT_SYMBOL_GPL(alloc_iova);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
static struct iova *
|
|
|
|
private_find_iova(struct iova_domain *iovad, unsigned long pfn)
|
2007-10-22 03:41:48 +04:00
|
|
|
{
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
struct rb_node *node = iovad->rbroot.rb_node;
|
|
|
|
|
|
|
|
assert_spin_locked(&iovad->iova_rbtree_lock);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
|
|
|
while (node) {
|
2021-03-05 19:35:22 +03:00
|
|
|
struct iova *iova = to_iova(node);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
|
|
|
if (pfn < iova->pfn_lo)
|
|
|
|
node = node->rb_left;
|
2017-09-21 18:52:42 +03:00
|
|
|
else if (pfn > iova->pfn_hi)
|
2007-10-22 03:41:48 +04:00
|
|
|
node = node->rb_right;
|
2017-09-21 18:52:42 +03:00
|
|
|
else
|
|
|
|
return iova; /* pfn falls within iova's range */
|
2007-10-22 03:41:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
|
2021-05-10 14:53:02 +03:00
|
|
|
static void remove_iova(struct iova_domain *iovad, struct iova *iova)
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
{
|
|
|
|
assert_spin_locked(&iovad->iova_rbtree_lock);
|
|
|
|
__cached_rbnode_delete_update(iovad, iova);
|
|
|
|
rb_erase(&iova->node, &iovad->rbroot);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* find_iova - finds an iova for a given pfn
|
|
|
|
* @iovad: - iova domain in question.
|
|
|
|
* @pfn: - page frame number
|
|
|
|
* This function finds and returns an iova belonging to the
|
2020-12-22 19:42:32 +03:00
|
|
|
* given domain which matches the given pfn.
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
*/
|
|
|
|
struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
struct iova *iova;
|
|
|
|
|
|
|
|
/* Take the lock so that no other thread is manipulating the rbtree */
|
|
|
|
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
|
|
|
|
iova = private_find_iova(iovad, pfn);
|
|
|
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
|
|
|
return iova;
|
|
|
|
}
|
2015-07-13 14:31:29 +03:00
|
|
|
EXPORT_SYMBOL_GPL(find_iova);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* __free_iova - frees the given iova
|
|
|
|
* @iovad: iova domain in question.
|
|
|
|
* @iova: iova in question.
|
|
|
|
* Frees the given iova belonging to the giving domain
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
__free_iova(struct iova_domain *iovad, struct iova *iova)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
|
2021-05-10 14:53:02 +03:00
|
|
|
remove_iova(iovad, iova);
|
2007-10-22 03:41:48 +04:00
|
|
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
2021-05-10 14:53:02 +03:00
|
|
|
free_iova_mem(iova);
|
2007-10-22 03:41:48 +04:00
|
|
|
}
|
2015-07-13 14:31:29 +03:00
|
|
|
EXPORT_SYMBOL_GPL(__free_iova);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* free_iova - finds and frees the iova for a given pfn
|
|
|
|
* @iovad: - iova domain in question.
|
|
|
|
* @pfn: - pfn that is allocated previously
|
|
|
|
* This functions finds an iova for a given pfn and then
|
|
|
|
* frees the iova from that domain.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
free_iova(struct iova_domain *iovad, unsigned long pfn)
|
|
|
|
{
|
2020-11-17 13:25:34 +03:00
|
|
|
unsigned long flags;
|
|
|
|
struct iova *iova;
|
2015-04-17 07:32:47 +03:00
|
|
|
|
2020-11-17 13:25:34 +03:00
|
|
|
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
|
|
|
|
iova = private_find_iova(iovad, pfn);
|
2021-05-10 14:53:02 +03:00
|
|
|
if (!iova) {
|
|
|
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
remove_iova(iovad, iova);
|
2020-11-17 13:25:34 +03:00
|
|
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
2021-05-10 14:53:02 +03:00
|
|
|
free_iova_mem(iova);
|
2007-10-22 03:41:48 +04:00
|
|
|
}
|
2015-07-13 14:31:29 +03:00
|
|
|
EXPORT_SYMBOL_GPL(free_iova);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
/**
|
|
|
|
* alloc_iova_fast - allocates an iova from rcache
|
|
|
|
* @iovad: - iova domain in question
|
|
|
|
* @size: - size of page frames to allocate
|
|
|
|
* @limit_pfn: - max limit address
|
2017-09-20 11:52:02 +03:00
|
|
|
* @flush_rcache: - set to flush rcache on regular allocation failure
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
* This function tries to satisfy an iova allocation from the rcache,
|
2017-09-20 11:52:02 +03:00
|
|
|
* and falls back to regular allocation on failure. If regular allocation
|
|
|
|
* fails too and the flush_rcache flag is set then the rcache will be flushed.
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
*/
|
|
|
|
unsigned long
|
|
|
|
alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
|
2017-09-20 11:52:02 +03:00
|
|
|
unsigned long limit_pfn, bool flush_rcache)
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
{
|
|
|
|
unsigned long iova_pfn;
|
|
|
|
struct iova *new_iova;
|
|
|
|
|
2017-09-19 16:48:40 +03:00
|
|
|
iova_pfn = iova_rcache_get(iovad, size, limit_pfn + 1);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
if (iova_pfn)
|
|
|
|
return iova_pfn;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
new_iova = alloc_iova(iovad, size, limit_pfn, true);
|
|
|
|
if (!new_iova) {
|
|
|
|
unsigned int cpu;
|
|
|
|
|
2017-09-20 11:52:02 +03:00
|
|
|
if (!flush_rcache)
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Try replenishing IOVAs by flushing rcache. */
|
2017-09-20 11:52:02 +03:00
|
|
|
flush_rcache = false;
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
for_each_online_cpu(cpu)
|
|
|
|
free_cpu_cached_iovas(cpu, iovad);
|
2020-09-30 10:44:24 +03:00
|
|
|
free_global_cached_iovas(iovad);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_iova->pfn_lo;
|
|
|
|
}
|
2021-08-31 13:36:22 +03:00
|
|
|
EXPORT_SYMBOL_GPL(alloc_iova_fast);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* free_iova_fast - free iova pfn range into rcache
|
|
|
|
* @iovad: - iova domain in question.
|
|
|
|
* @pfn: - pfn that is allocated previously
|
|
|
|
* @size: - # of pages in range
|
|
|
|
* This functions frees an iova range by trying to put it into the rcache,
|
|
|
|
* falling back to regular iova deallocation via free_iova() if this fails.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size)
|
|
|
|
{
|
|
|
|
if (iova_rcache_insert(iovad, pfn, size))
|
|
|
|
return;
|
|
|
|
|
|
|
|
free_iova(iovad, pfn);
|
|
|
|
}
|
2021-08-31 13:36:22 +03:00
|
|
|
EXPORT_SYMBOL_GPL(free_iova_fast);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
|
2017-08-10 16:49:44 +03:00
|
|
|
#define fq_ring_for_each(i, fq) \
|
|
|
|
for ((i) = (fq)->head; (i) != (fq)->tail; (i) = ((i) + 1) % IOVA_FQ_SIZE)
|
|
|
|
|
|
|
|
static inline bool fq_full(struct iova_fq *fq)
|
|
|
|
{
|
2017-08-10 17:31:17 +03:00
|
|
|
assert_spin_locked(&fq->lock);
|
2017-08-10 16:49:44 +03:00
|
|
|
return (((fq->tail + 1) % IOVA_FQ_SIZE) == fq->head);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned fq_ring_add(struct iova_fq *fq)
|
|
|
|
{
|
|
|
|
unsigned idx = fq->tail;
|
|
|
|
|
2017-08-10 17:31:17 +03:00
|
|
|
assert_spin_locked(&fq->lock);
|
|
|
|
|
2017-08-10 16:49:44 +03:00
|
|
|
fq->tail = (idx + 1) % IOVA_FQ_SIZE;
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fq_ring_free(struct iova_domain *iovad, struct iova_fq *fq)
|
|
|
|
{
|
2017-08-10 17:14:59 +03:00
|
|
|
u64 counter = atomic64_read(&iovad->fq_flush_finish_cnt);
|
2017-08-10 16:49:44 +03:00
|
|
|
unsigned idx;
|
|
|
|
|
2017-08-10 17:31:17 +03:00
|
|
|
assert_spin_locked(&fq->lock);
|
|
|
|
|
2017-08-10 16:49:44 +03:00
|
|
|
fq_ring_for_each(idx, fq) {
|
|
|
|
|
2017-08-10 17:14:59 +03:00
|
|
|
if (fq->entries[idx].counter >= counter)
|
|
|
|
break;
|
|
|
|
|
2017-08-10 16:49:44 +03:00
|
|
|
if (iovad->entry_dtor)
|
|
|
|
iovad->entry_dtor(fq->entries[idx].data);
|
|
|
|
|
|
|
|
free_iova_fast(iovad,
|
|
|
|
fq->entries[idx].iova_pfn,
|
|
|
|
fq->entries[idx].pages);
|
2017-08-10 17:14:59 +03:00
|
|
|
|
|
|
|
fq->head = (fq->head + 1) % IOVA_FQ_SIZE;
|
2017-08-10 16:49:44 +03:00
|
|
|
}
|
2017-08-10 17:14:59 +03:00
|
|
|
}
|
2017-08-10 16:49:44 +03:00
|
|
|
|
2017-08-10 17:14:59 +03:00
|
|
|
static void iova_domain_flush(struct iova_domain *iovad)
|
|
|
|
{
|
|
|
|
atomic64_inc(&iovad->fq_flush_start_cnt);
|
|
|
|
iovad->flush_cb(iovad);
|
|
|
|
atomic64_inc(&iovad->fq_flush_finish_cnt);
|
2017-08-10 16:49:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void fq_destroy_all_entries(struct iova_domain *iovad)
|
|
|
|
{
|
|
|
|
int cpu;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This code runs when the iova_domain is being detroyed, so don't
|
|
|
|
* bother to free iovas, just call the entry_dtor on all remaining
|
|
|
|
* entries.
|
|
|
|
*/
|
|
|
|
if (!iovad->entry_dtor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
struct iova_fq *fq = per_cpu_ptr(iovad->fq, cpu);
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
fq_ring_for_each(idx, fq)
|
|
|
|
iovad->entry_dtor(fq->entries[idx].data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 00:43:17 +03:00
|
|
|
static void fq_flush_timeout(struct timer_list *t)
|
2017-08-10 17:58:18 +03:00
|
|
|
{
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 00:43:17 +03:00
|
|
|
struct iova_domain *iovad = from_timer(iovad, t, fq_timer);
|
2017-08-10 17:58:18 +03:00
|
|
|
int cpu;
|
|
|
|
|
|
|
|
atomic_set(&iovad->fq_timer_on, 0);
|
|
|
|
iova_domain_flush(iovad);
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
unsigned long flags;
|
|
|
|
struct iova_fq *fq;
|
|
|
|
|
|
|
|
fq = per_cpu_ptr(iovad->fq, cpu);
|
|
|
|
spin_lock_irqsave(&fq->lock, flags);
|
|
|
|
fq_ring_free(iovad, fq);
|
|
|
|
spin_unlock_irqrestore(&fq->lock, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-10 16:49:44 +03:00
|
|
|
void queue_iova(struct iova_domain *iovad,
|
|
|
|
unsigned long pfn, unsigned long pages,
|
|
|
|
unsigned long data)
|
|
|
|
{
|
2021-08-11 15:21:38 +03:00
|
|
|
struct iova_fq *fq;
|
2017-08-10 17:31:17 +03:00
|
|
|
unsigned long flags;
|
2017-08-10 16:49:44 +03:00
|
|
|
unsigned idx;
|
|
|
|
|
2021-08-11 15:21:28 +03:00
|
|
|
/*
|
|
|
|
* Order against the IOMMU driver's pagetable update from unmapping
|
|
|
|
* @pte, to guarantee that iova_domain_flush() observes that if called
|
2021-08-11 15:21:38 +03:00
|
|
|
* from a different CPU before we release the lock below. Full barrier
|
|
|
|
* so it also pairs with iommu_dma_init_fq() to avoid seeing partially
|
|
|
|
* written fq state here.
|
2021-08-11 15:21:28 +03:00
|
|
|
*/
|
2021-08-11 15:21:38 +03:00
|
|
|
smp_mb();
|
2021-08-11 15:21:28 +03:00
|
|
|
|
2021-08-11 15:21:38 +03:00
|
|
|
fq = raw_cpu_ptr(iovad->fq);
|
2017-08-10 17:31:17 +03:00
|
|
|
spin_lock_irqsave(&fq->lock, flags);
|
|
|
|
|
2017-08-10 17:14:59 +03:00
|
|
|
/*
|
|
|
|
* First remove all entries from the flush queue that have already been
|
|
|
|
* flushed out on another CPU. This makes the fq_full() check below less
|
|
|
|
* likely to be true.
|
|
|
|
*/
|
|
|
|
fq_ring_free(iovad, fq);
|
|
|
|
|
2017-08-10 16:49:44 +03:00
|
|
|
if (fq_full(fq)) {
|
2017-08-10 17:14:59 +03:00
|
|
|
iova_domain_flush(iovad);
|
2017-08-10 16:49:44 +03:00
|
|
|
fq_ring_free(iovad, fq);
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = fq_ring_add(fq);
|
|
|
|
|
|
|
|
fq->entries[idx].iova_pfn = pfn;
|
|
|
|
fq->entries[idx].pages = pages;
|
|
|
|
fq->entries[idx].data = data;
|
2017-08-10 17:14:59 +03:00
|
|
|
fq->entries[idx].counter = atomic64_read(&iovad->fq_flush_start_cnt);
|
2017-08-10 16:49:44 +03:00
|
|
|
|
2017-08-10 17:31:17 +03:00
|
|
|
spin_unlock_irqrestore(&fq->lock, flags);
|
2017-08-10 17:58:18 +03:00
|
|
|
|
2019-08-28 16:13:38 +03:00
|
|
|
/* Avoid false sharing as much as possible. */
|
|
|
|
if (!atomic_read(&iovad->fq_timer_on) &&
|
2020-08-27 11:43:54 +03:00
|
|
|
!atomic_xchg(&iovad->fq_timer_on, 1))
|
2017-08-10 17:58:18 +03:00
|
|
|
mod_timer(&iovad->fq_timer,
|
|
|
|
jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT));
|
2017-08-10 16:49:44 +03:00
|
|
|
}
|
|
|
|
|
2007-10-22 03:41:48 +04:00
|
|
|
/**
|
2020-12-22 19:42:32 +03:00
|
|
|
* put_iova_domain - destroys the iova domain
|
2007-10-22 03:41:48 +04:00
|
|
|
* @iovad: - iova domain in question.
|
|
|
|
* All the iova's in that domain are destroyed.
|
|
|
|
*/
|
|
|
|
void put_iova_domain(struct iova_domain *iovad)
|
|
|
|
{
|
2017-09-19 16:48:39 +03:00
|
|
|
struct iova *iova, *tmp;
|
2007-10-22 03:41:48 +04:00
|
|
|
|
2021-03-25 15:29:58 +03:00
|
|
|
cpuhp_state_remove_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD,
|
|
|
|
&iovad->cpuhp_dead);
|
|
|
|
|
2017-08-10 15:44:28 +03:00
|
|
|
free_iova_flush_queue(iovad);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
free_iova_rcaches(iovad);
|
2017-09-19 16:48:39 +03:00
|
|
|
rbtree_postorder_for_each_entry_safe(iova, tmp, &iovad->rbroot, node)
|
2007-10-22 03:41:48 +04:00
|
|
|
free_iova_mem(iova);
|
|
|
|
}
|
2015-07-13 14:31:29 +03:00
|
|
|
EXPORT_SYMBOL_GPL(put_iova_domain);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
|
|
|
static int
|
|
|
|
__is_range_overlap(struct rb_node *node,
|
|
|
|
unsigned long pfn_lo, unsigned long pfn_hi)
|
|
|
|
{
|
2021-03-05 19:35:22 +03:00
|
|
|
struct iova *iova = to_iova(node);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
|
|
|
if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-02-19 10:07:37 +04:00
|
|
|
static inline struct iova *
|
|
|
|
alloc_and_init_iova(unsigned long pfn_lo, unsigned long pfn_hi)
|
|
|
|
{
|
|
|
|
struct iova *iova;
|
|
|
|
|
|
|
|
iova = alloc_iova_mem();
|
|
|
|
if (iova) {
|
|
|
|
iova->pfn_lo = pfn_lo;
|
|
|
|
iova->pfn_hi = pfn_hi;
|
|
|
|
}
|
|
|
|
|
|
|
|
return iova;
|
|
|
|
}
|
|
|
|
|
2007-10-22 03:41:48 +04:00
|
|
|
static struct iova *
|
|
|
|
__insert_new_range(struct iova_domain *iovad,
|
|
|
|
unsigned long pfn_lo, unsigned long pfn_hi)
|
|
|
|
{
|
|
|
|
struct iova *iova;
|
|
|
|
|
2014-02-19 10:07:37 +04:00
|
|
|
iova = alloc_and_init_iova(pfn_lo, pfn_hi);
|
|
|
|
if (iova)
|
2017-02-24 14:13:37 +03:00
|
|
|
iova_insert_rbtree(&iovad->rbroot, iova, NULL);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
|
|
|
return iova;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
__adjust_overlap_range(struct iova *iova,
|
|
|
|
unsigned long *pfn_lo, unsigned long *pfn_hi)
|
|
|
|
{
|
|
|
|
if (*pfn_lo < iova->pfn_lo)
|
|
|
|
iova->pfn_lo = *pfn_lo;
|
|
|
|
if (*pfn_hi > iova->pfn_hi)
|
|
|
|
*pfn_lo = iova->pfn_hi + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* reserve_iova - reserves an iova in the given range
|
|
|
|
* @iovad: - iova domain pointer
|
|
|
|
* @pfn_lo: - lower page frame address
|
|
|
|
* @pfn_hi:- higher pfn adderss
|
|
|
|
* This function allocates reserves the address range from pfn_lo to pfn_hi so
|
|
|
|
* that this address is not dished out as part of alloc_iova.
|
|
|
|
*/
|
|
|
|
struct iova *
|
|
|
|
reserve_iova(struct iova_domain *iovad,
|
|
|
|
unsigned long pfn_lo, unsigned long pfn_hi)
|
|
|
|
{
|
|
|
|
struct rb_node *node;
|
|
|
|
unsigned long flags;
|
|
|
|
struct iova *iova;
|
|
|
|
unsigned int overlap = 0;
|
|
|
|
|
2017-09-21 18:52:46 +03:00
|
|
|
/* Don't allow nonsensical pfns */
|
|
|
|
if (WARN_ON((pfn_hi | pfn_lo) > (ULLONG_MAX >> iova_shift(iovad))))
|
|
|
|
return NULL;
|
|
|
|
|
2009-07-08 18:23:30 +04:00
|
|
|
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
|
2007-10-22 03:41:48 +04:00
|
|
|
for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
|
|
|
|
if (__is_range_overlap(node, pfn_lo, pfn_hi)) {
|
2021-03-05 19:35:22 +03:00
|
|
|
iova = to_iova(node);
|
2007-10-22 03:41:48 +04:00
|
|
|
__adjust_overlap_range(iova, &pfn_lo, &pfn_hi);
|
|
|
|
if ((pfn_lo >= iova->pfn_lo) &&
|
|
|
|
(pfn_hi <= iova->pfn_hi))
|
|
|
|
goto finish;
|
|
|
|
overlap = 1;
|
|
|
|
|
|
|
|
} else if (overlap)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-03-31 05:57:33 +04:00
|
|
|
/* We are here either because this is the first reserver node
|
2007-10-22 03:41:48 +04:00
|
|
|
* or need to insert remaining non overlap addr range
|
|
|
|
*/
|
|
|
|
iova = __insert_new_range(iovad, pfn_lo, pfn_hi);
|
|
|
|
finish:
|
|
|
|
|
2009-07-08 18:23:30 +04:00
|
|
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
2007-10-22 03:41:48 +04:00
|
|
|
return iova;
|
|
|
|
}
|
2015-07-13 14:31:29 +03:00
|
|
|
EXPORT_SYMBOL_GPL(reserve_iova);
|
2007-10-22 03:41:48 +04:00
|
|
|
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
/*
|
|
|
|
* Magazine caches for IOVA ranges. For an introduction to magazines,
|
|
|
|
* see the USENIX 2001 paper "Magazines and Vmem: Extending the Slab
|
|
|
|
* Allocator to Many CPUs and Arbitrary Resources" by Bonwick and Adams.
|
|
|
|
* For simplicity, we use a static magazine size and don't implement the
|
|
|
|
* dynamic size tuning described in the paper.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define IOVA_MAG_SIZE 128
|
|
|
|
|
|
|
|
struct iova_magazine {
|
|
|
|
unsigned long size;
|
|
|
|
unsigned long pfns[IOVA_MAG_SIZE];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct iova_cpu_rcache {
|
|
|
|
spinlock_t lock;
|
|
|
|
struct iova_magazine *loaded;
|
|
|
|
struct iova_magazine *prev;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct iova_magazine *iova_magazine_alloc(gfp_t flags)
|
|
|
|
{
|
|
|
|
return kzalloc(sizeof(struct iova_magazine), flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iova_magazine_free(struct iova_magazine *mag)
|
|
|
|
{
|
|
|
|
kfree(mag);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iova_magazine_free_pfns(struct iova_magazine *mag, struct iova_domain *iovad)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!mag)
|
|
|
|
return;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
|
|
|
|
|
|
|
|
for (i = 0 ; i < mag->size; ++i) {
|
|
|
|
struct iova *iova = private_find_iova(iovad, mag->pfns[i]);
|
|
|
|
|
2020-06-02 16:08:18 +03:00
|
|
|
if (WARN_ON(!iova))
|
|
|
|
continue;
|
|
|
|
|
2021-05-10 14:53:02 +03:00
|
|
|
remove_iova(iovad, iova);
|
|
|
|
free_iova_mem(iova);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
|
|
|
|
|
|
|
mag->size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool iova_magazine_full(struct iova_magazine *mag)
|
|
|
|
{
|
|
|
|
return (mag && mag->size == IOVA_MAG_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool iova_magazine_empty(struct iova_magazine *mag)
|
|
|
|
{
|
|
|
|
return (!mag || mag->size == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long iova_magazine_pop(struct iova_magazine *mag,
|
|
|
|
unsigned long limit_pfn)
|
|
|
|
{
|
2017-09-28 13:31:23 +03:00
|
|
|
int i;
|
|
|
|
unsigned long pfn;
|
|
|
|
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
BUG_ON(iova_magazine_empty(mag));
|
|
|
|
|
2017-09-28 13:31:23 +03:00
|
|
|
/* Only fall back to the rbtree if we have no suitable pfns at all */
|
|
|
|
for (i = mag->size - 1; mag->pfns[i] > limit_pfn; i--)
|
|
|
|
if (i == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Swap it to pop it */
|
|
|
|
pfn = mag->pfns[i];
|
|
|
|
mag->pfns[i] = mag->pfns[--mag->size];
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
|
2017-09-28 13:31:23 +03:00
|
|
|
return pfn;
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void iova_magazine_push(struct iova_magazine *mag, unsigned long pfn)
|
|
|
|
{
|
|
|
|
BUG_ON(iova_magazine_full(mag));
|
|
|
|
|
|
|
|
mag->pfns[mag->size++] = pfn;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init_iova_rcaches(struct iova_domain *iovad)
|
|
|
|
{
|
|
|
|
struct iova_cpu_rcache *cpu_rcache;
|
|
|
|
struct iova_rcache *rcache;
|
|
|
|
unsigned int cpu;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
|
|
|
|
rcache = &iovad->rcaches[i];
|
|
|
|
spin_lock_init(&rcache->lock);
|
|
|
|
rcache->depot_size = 0;
|
|
|
|
rcache->cpu_rcaches = __alloc_percpu(sizeof(*cpu_rcache), cache_line_size());
|
|
|
|
if (WARN_ON(!rcache->cpu_rcaches))
|
|
|
|
continue;
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu);
|
|
|
|
spin_lock_init(&cpu_rcache->lock);
|
|
|
|
cpu_rcache->loaded = iova_magazine_alloc(GFP_KERNEL);
|
|
|
|
cpu_rcache->prev = iova_magazine_alloc(GFP_KERNEL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try inserting IOVA range starting with 'iova_pfn' into 'rcache', and
|
|
|
|
* return true on success. Can fail if rcache is full and we can't free
|
|
|
|
* space, and free_iova() (our only caller) will then return the IOVA
|
|
|
|
* range to the rbtree instead.
|
|
|
|
*/
|
|
|
|
static bool __iova_rcache_insert(struct iova_domain *iovad,
|
|
|
|
struct iova_rcache *rcache,
|
|
|
|
unsigned long iova_pfn)
|
|
|
|
{
|
|
|
|
struct iova_magazine *mag_to_free = NULL;
|
|
|
|
struct iova_cpu_rcache *cpu_rcache;
|
|
|
|
bool can_insert = false;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2017-06-27 19:16:47 +03:00
|
|
|
cpu_rcache = raw_cpu_ptr(rcache->cpu_rcaches);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
spin_lock_irqsave(&cpu_rcache->lock, flags);
|
|
|
|
|
|
|
|
if (!iova_magazine_full(cpu_rcache->loaded)) {
|
|
|
|
can_insert = true;
|
|
|
|
} else if (!iova_magazine_full(cpu_rcache->prev)) {
|
|
|
|
swap(cpu_rcache->prev, cpu_rcache->loaded);
|
|
|
|
can_insert = true;
|
|
|
|
} else {
|
|
|
|
struct iova_magazine *new_mag = iova_magazine_alloc(GFP_ATOMIC);
|
|
|
|
|
|
|
|
if (new_mag) {
|
|
|
|
spin_lock(&rcache->lock);
|
|
|
|
if (rcache->depot_size < MAX_GLOBAL_MAGS) {
|
|
|
|
rcache->depot[rcache->depot_size++] =
|
|
|
|
cpu_rcache->loaded;
|
|
|
|
} else {
|
|
|
|
mag_to_free = cpu_rcache->loaded;
|
|
|
|
}
|
|
|
|
spin_unlock(&rcache->lock);
|
|
|
|
|
|
|
|
cpu_rcache->loaded = new_mag;
|
|
|
|
can_insert = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (can_insert)
|
|
|
|
iova_magazine_push(cpu_rcache->loaded, iova_pfn);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&cpu_rcache->lock, flags);
|
|
|
|
|
|
|
|
if (mag_to_free) {
|
|
|
|
iova_magazine_free_pfns(mag_to_free, iovad);
|
|
|
|
iova_magazine_free(mag_to_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
return can_insert;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool iova_rcache_insert(struct iova_domain *iovad, unsigned long pfn,
|
|
|
|
unsigned long size)
|
|
|
|
{
|
|
|
|
unsigned int log_size = order_base_2(size);
|
|
|
|
|
|
|
|
if (log_size >= IOVA_RANGE_CACHE_MAX_SIZE)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return __iova_rcache_insert(iovad, &iovad->rcaches[log_size], pfn);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller wants to allocate a new IOVA range from 'rcache'. If we can
|
|
|
|
* satisfy the request, return a matching non-NULL range and remove
|
|
|
|
* it from the 'rcache'.
|
|
|
|
*/
|
|
|
|
static unsigned long __iova_rcache_get(struct iova_rcache *rcache,
|
|
|
|
unsigned long limit_pfn)
|
|
|
|
{
|
|
|
|
struct iova_cpu_rcache *cpu_rcache;
|
|
|
|
unsigned long iova_pfn = 0;
|
|
|
|
bool has_pfn = false;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2017-06-27 19:16:47 +03:00
|
|
|
cpu_rcache = raw_cpu_ptr(rcache->cpu_rcaches);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
spin_lock_irqsave(&cpu_rcache->lock, flags);
|
|
|
|
|
|
|
|
if (!iova_magazine_empty(cpu_rcache->loaded)) {
|
|
|
|
has_pfn = true;
|
|
|
|
} else if (!iova_magazine_empty(cpu_rcache->prev)) {
|
|
|
|
swap(cpu_rcache->prev, cpu_rcache->loaded);
|
|
|
|
has_pfn = true;
|
|
|
|
} else {
|
|
|
|
spin_lock(&rcache->lock);
|
|
|
|
if (rcache->depot_size > 0) {
|
|
|
|
iova_magazine_free(cpu_rcache->loaded);
|
|
|
|
cpu_rcache->loaded = rcache->depot[--rcache->depot_size];
|
|
|
|
has_pfn = true;
|
|
|
|
}
|
|
|
|
spin_unlock(&rcache->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_pfn)
|
|
|
|
iova_pfn = iova_magazine_pop(cpu_rcache->loaded, limit_pfn);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&cpu_rcache->lock, flags);
|
|
|
|
|
|
|
|
return iova_pfn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to satisfy IOVA allocation range from rcache. Fail if requested
|
|
|
|
* size is too big or the DMA limit we are given isn't satisfied by the
|
|
|
|
* top element in the magazine.
|
|
|
|
*/
|
|
|
|
static unsigned long iova_rcache_get(struct iova_domain *iovad,
|
|
|
|
unsigned long size,
|
|
|
|
unsigned long limit_pfn)
|
|
|
|
{
|
|
|
|
unsigned int log_size = order_base_2(size);
|
|
|
|
|
|
|
|
if (log_size >= IOVA_RANGE_CACHE_MAX_SIZE)
|
|
|
|
return 0;
|
|
|
|
|
2017-09-19 16:48:40 +03:00
|
|
|
return __iova_rcache_get(&iovad->rcaches[log_size], limit_pfn - size);
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* free rcache data structures.
|
|
|
|
*/
|
|
|
|
static void free_iova_rcaches(struct iova_domain *iovad)
|
|
|
|
{
|
|
|
|
struct iova_rcache *rcache;
|
2017-09-19 16:48:39 +03:00
|
|
|
struct iova_cpu_rcache *cpu_rcache;
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
unsigned int cpu;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
|
|
|
|
rcache = &iovad->rcaches[i];
|
2017-09-19 16:48:39 +03:00
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu);
|
|
|
|
iova_magazine_free(cpu_rcache->loaded);
|
|
|
|
iova_magazine_free(cpu_rcache->prev);
|
|
|
|
}
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
free_percpu(rcache->cpu_rcaches);
|
2017-09-19 16:48:39 +03:00
|
|
|
for (j = 0; j < rcache->depot_size; ++j)
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
iova_magazine_free(rcache->depot[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* free all the IOVA ranges cached by a cpu (used when cpu is unplugged)
|
|
|
|
*/
|
2021-03-25 15:30:00 +03:00
|
|
|
static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad)
|
iommu/iova: introduce per-cpu caching to iova allocation
IOVA allocation has two problems that impede high-throughput I/O.
First, it can do a linear search over the allocated IOVA ranges.
Second, the rbtree spinlock that serializes IOVA allocations becomes
contended.
Address these problems by creating an API for caching allocated IOVA
ranges, so that the IOVA allocator isn't accessed frequently. This
patch adds a per-CPU cache, from which CPUs can alloc/free IOVAs
without taking the rbtree spinlock. The per-CPU caches are backed by
a global cache, to avoid invoking the (linear-time) IOVA allocator
without needing to make the per-CPU cache size excessive. This design
is based on magazines, as described in "Magazines and Vmem: Extending
the Slab Allocator to Many CPUs and Arbitrary Resources" (currently
available at https://www.usenix.org/legacy/event/usenix01/bonwick.html)
Adding caching on top of the existing rbtree allocator maintains the
property that IOVAs are densely packed in the IO virtual address space,
which is important for keeping IOMMU page table usage low.
To keep the cache size reasonable, we bound the IOVA space a CPU can
cache by 32 MiB (we cache a bounded number of IOVA ranges, and only
ranges of size <= 128 KiB). The shared global cache is bounded at
4 MiB of IOVA space.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, cleaned up and reworded the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
[dwmw2: split out VT-d part into a separate patch]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2016-04-20 11:34:11 +03:00
|
|
|
{
|
|
|
|
struct iova_cpu_rcache *cpu_rcache;
|
|
|
|
struct iova_rcache *rcache;
|
|
|
|
unsigned long flags;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
|
|
|
|
rcache = &iovad->rcaches[i];
|
|
|
|
cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu);
|
|
|
|
spin_lock_irqsave(&cpu_rcache->lock, flags);
|
|
|
|
iova_magazine_free_pfns(cpu_rcache->loaded, iovad);
|
|
|
|
iova_magazine_free_pfns(cpu_rcache->prev, iovad);
|
|
|
|
spin_unlock_irqrestore(&cpu_rcache->lock, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-30 10:44:24 +03:00
|
|
|
/*
|
|
|
|
* free all the IOVA ranges of global cache
|
|
|
|
*/
|
|
|
|
static void free_global_cached_iovas(struct iova_domain *iovad)
|
|
|
|
{
|
|
|
|
struct iova_rcache *rcache;
|
|
|
|
unsigned long flags;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
|
|
|
|
rcache = &iovad->rcaches[i];
|
|
|
|
spin_lock_irqsave(&rcache->lock, flags);
|
|
|
|
for (j = 0; j < rcache->depot_size; ++j) {
|
|
|
|
iova_magazine_free_pfns(rcache->depot[j], iovad);
|
|
|
|
iova_magazine_free(rcache->depot[j]);
|
|
|
|
}
|
|
|
|
rcache->depot_size = 0;
|
|
|
|
spin_unlock_irqrestore(&rcache->lock, flags);
|
|
|
|
}
|
|
|
|
}
|
2015-07-13 14:31:30 +03:00
|
|
|
MODULE_AUTHOR("Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>");
|
|
|
|
MODULE_LICENSE("GPL");
|