[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <69c30e85-4a72-a0e1-1e56-4ffbd0df5aba@huawei.com>
Date: Mon, 18 Jan 2021 09:24:17 +0000
From: John Garry <john.garry@...wei.com>
To: Jean-Philippe Brucker <jean-philippe@...aro.org>
CC: <joro@...tes.org>, <will@...nel.org>,
<linux-kernel@...r.kernel.org>, <linuxarm@...wei.com>,
<iommu@...ts.linux-foundation.org>, <robin.murphy@....com>
Subject: Re: [PATCH v4 2/3] iommu/iova: Avoid double-negatives in magazine
helpers
On 15/01/2021 17:30, Jean-Philippe Brucker wrote:
> On Thu, Dec 10, 2020 at 02:23:08AM +0800, John Garry wrote:
>> A similar crash to the following could be observed if initial CPU rcache
>> magazine allocations fail in init_iova_rcaches():
>
thanks for having a look
> Any idea why that's happening? This fix seems ok but if we're expecting
> allocation failures for the loaded magazine then we could easily get it
> for cpu_rcaches too, and get a similar abort at runtime.
It's not specifically that we expect them (allocation failures for the
loaded magazine), rather we should make safe against it.
So could you be more specific in your concern for the cpu_rcache
failure? cpu_rcache magazine assignment comes from this logic.
Anyway, logic like "if not full" or "if not empty" is poor as the
outcome for NULL is ambiguous (maybe there's a better word) and the code
is not safe against it, and so I replace with "if space" or "if have an
IOVA", respectively.
Thanks,
John
>
> Thanks,
> Jean
>
>> Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
>> Mem abort info:
>>
>> free_iova_fast+0xfc/0x280
>> iommu_dma_free_iova+0x64/0x70
>> __iommu_dma_unmap+0x9c/0xf8
>> iommu_dma_unmap_sg+0xa8/0xc8
>> dma_unmap_sg_attrs+0x28/0x50
>> cq_thread_v3_hw+0x2dc/0x528
>> irq_thread_fn+0x2c/0xa0
>> irq_thread+0x130/0x1e0
>> kthread+0x154/0x158
>> ret_from_fork+0x10/0x34
>>
>> The issue is that expression !iova_magazine_full(NULL) evaluates true; this
>> falls over in __iova_rcache_insert() when we attempt to cache a mag and
>> cpu_rcache->loaded == NULL:
>>
>> if (!iova_magazine_full(cpu_rcache->loaded)) {
>> can_insert = true;
>> ...
>>
>> if (can_insert)
>> iova_magazine_push(cpu_rcache->loaded, iova_pfn);
>>
>> As above, can_insert is evaluated true, which it shouldn't be, and we try
>> to insert pfns in a NULL mag, which is not safe.
>>
>> To avoid this, stop using double-negatives, like !iova_magazine_full() and
>> !iova_magazine_empty(), and use positive tests, like
>> iova_magazine_has_space() and iova_magazine_has_pfns(), respectively; these
>> can safely deal with cpu_rcache->{loaded, prev} = NULL.
>>
>> Signed-off-by: John Garry <john.garry@...wei.com>
>> Tested-by: Xiang Chen <chenxiang66@...ilicon.com>
>> Reviewed-by: Zhen Lei <thunder.leizhen@...wei.com>
>
>> ---
>> drivers/iommu/iova.c | 29 +++++++++++++++++------------
>> 1 file changed, 17 insertions(+), 12 deletions(-)
>>
>> diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
>> index cf1aacda2fe4..732ee687e0e2 100644
>> --- a/drivers/iommu/iova.c
>> +++ b/drivers/iommu/iova.c
>> @@ -767,14 +767,18 @@ iova_magazine_free_pfns(struct iova_magazine *mag, struct iova_domain *iovad)
>> mag->size = 0;
>> }
>>
>> -static bool iova_magazine_full(struct iova_magazine *mag)
>> +static bool iova_magazine_has_space(struct iova_magazine *mag)
>> {
>> - return (mag && mag->size == IOVA_MAG_SIZE);
>> + if (!mag)
>> + return false;
>> + return mag->size < IOVA_MAG_SIZE;
>> }
>>
>> -static bool iova_magazine_empty(struct iova_magazine *mag)
>> +static bool iova_magazine_has_pfns(struct iova_magazine *mag)
>> {
>> - return (!mag || mag->size == 0);
>> + if (!mag)
>> + return false;
>> + return mag->size;
>> }
>>
>> static unsigned long iova_magazine_pop(struct iova_magazine *mag,
>> @@ -783,7 +787,7 @@ static unsigned long iova_magazine_pop(struct iova_magazine *mag,
>> int i;
>> unsigned long pfn;
>>
>> - BUG_ON(iova_magazine_empty(mag));
>> + BUG_ON(!iova_magazine_has_pfns(mag));
>>
>> /* 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--)
>> @@ -799,7 +803,7 @@ static unsigned long iova_magazine_pop(struct iova_magazine *mag,
>>
>> static void iova_magazine_push(struct iova_magazine *mag, unsigned long pfn)
>> {
>> - BUG_ON(iova_magazine_full(mag));
>> + BUG_ON(!iova_magazine_has_space(mag));
>>
>> mag->pfns[mag->size++] = pfn;
>> }
>> @@ -845,9 +849,9 @@ static bool __iova_rcache_insert(struct iova_domain *iovad,
>> cpu_rcache = raw_cpu_ptr(rcache->cpu_rcaches);
>> spin_lock_irqsave(&cpu_rcache->lock, flags);
>>
>> - if (!iova_magazine_full(cpu_rcache->loaded)) {
>> + if (iova_magazine_has_space(cpu_rcache->loaded)) {
>> can_insert = true;
>> - } else if (!iova_magazine_full(cpu_rcache->prev)) {
>> + } else if (iova_magazine_has_space(cpu_rcache->prev)) {
>> swap(cpu_rcache->prev, cpu_rcache->loaded);
>> can_insert = true;
>> } else {
>> @@ -856,8 +860,9 @@ static bool __iova_rcache_insert(struct iova_domain *iovad,
>> if (new_mag) {
>> spin_lock(&rcache->lock);
>> if (rcache->depot_size < MAX_GLOBAL_MAGS) {
>> - rcache->depot[rcache->depot_size++] =
>> - cpu_rcache->loaded;
>> + if (cpu_rcache->loaded)
>> + rcache->depot[rcache->depot_size++] =
>> + cpu_rcache->loaded;
>> } else {
>> mag_to_free = cpu_rcache->loaded;
>> }
>> @@ -908,9 +913,9 @@ static unsigned long __iova_rcache_get(struct iova_rcache *rcache,
>> cpu_rcache = raw_cpu_ptr(rcache->cpu_rcaches);
>> spin_lock_irqsave(&cpu_rcache->lock, flags);
>>
>> - if (!iova_magazine_empty(cpu_rcache->loaded)) {
>> + if (iova_magazine_has_pfns(cpu_rcache->loaded)) {
>> has_pfn = true;
>> - } else if (!iova_magazine_empty(cpu_rcache->prev)) {
>> + } else if (iova_magazine_has_pfns(cpu_rcache->prev)) {
>> swap(cpu_rcache->prev, cpu_rcache->loaded);
>> has_pfn = true;
>> } else {
>> --
>> 2.26.2
>>
>> _______________________________________________
>> iommu mailing list
>> iommu@...ts.linux-foundation.org
>> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> .
>
Powered by blists - more mailing lists