Commit 12ac1d0f authored by Thomas Gleixner's avatar Thomas Gleixner

genirq: Make sparse_irq_lock protect what it should protect

for_each_active_irq() iterates the sparse irq allocation bitmap. The caller
must hold sparse_irq_lock. Several code pathes expect that an active bit in
the sparse bitmap also has a valid interrupt descriptor.

Unfortunately that's not true. The (de)allocation is a two step process,
which holds the sparse_irq_lock only across the queue/remove from the radix
tree and the set/clear in the allocation bitmap.

If a iteration locks sparse_irq_lock between the two steps, then it might
see an active bit but the corresponding irq descriptor is NULL. If that is
dereferenced unconditionally, then the kernel oopses. Of course, all
iterator sites could be audited and fixed, but....

There is no reason why the sparse_irq_lock needs to be dropped between the
two steps, in fact the code becomes simpler when the mutex is held across
both and the semantics become more straight forward, so future problems of
missing NULL pointer checks in the iteration are avoided and all existing
sites are fixed in one go.

Expand the lock held sections so both operations are covered and the bitmap
and the radixtree are in sync.

Fixes: a05a900a ("genirq: Make sparse_lock a mutex")
Reported-and-tested-by: default avatarHuang Ying <ying.huang@intel.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
parent 596a7a1d
...@@ -421,10 +421,8 @@ static void free_desc(unsigned int irq) ...@@ -421,10 +421,8 @@ static void free_desc(unsigned int irq)
* The sysfs entry must be serialized against a concurrent * The sysfs entry must be serialized against a concurrent
* irq_sysfs_init() as well. * irq_sysfs_init() as well.
*/ */
mutex_lock(&sparse_irq_lock);
kobject_del(&desc->kobj); kobject_del(&desc->kobj);
delete_irq_desc(irq); delete_irq_desc(irq);
mutex_unlock(&sparse_irq_lock);
/* /*
* We free the descriptor, masks and stat fields via RCU. That * We free the descriptor, masks and stat fields via RCU. That
...@@ -462,20 +460,15 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node, ...@@ -462,20 +460,15 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
desc = alloc_desc(start + i, node, flags, mask, owner); desc = alloc_desc(start + i, node, flags, mask, owner);
if (!desc) if (!desc)
goto err; goto err;
mutex_lock(&sparse_irq_lock);
irq_insert_desc(start + i, desc); irq_insert_desc(start + i, desc);
irq_sysfs_add(start + i, desc); irq_sysfs_add(start + i, desc);
mutex_unlock(&sparse_irq_lock);
} }
bitmap_set(allocated_irqs, start, cnt);
return start; return start;
err: err:
for (i--; i >= 0; i--) for (i--; i >= 0; i--)
free_desc(start + i); free_desc(start + i);
mutex_lock(&sparse_irq_lock);
bitmap_clear(allocated_irqs, start, cnt);
mutex_unlock(&sparse_irq_lock);
return -ENOMEM; return -ENOMEM;
} }
...@@ -575,6 +568,7 @@ static inline int alloc_descs(unsigned int start, unsigned int cnt, int node, ...@@ -575,6 +568,7 @@ static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
desc->owner = owner; desc->owner = owner;
} }
bitmap_set(allocated_irqs, start, cnt);
return start; return start;
} }
...@@ -670,10 +664,10 @@ void irq_free_descs(unsigned int from, unsigned int cnt) ...@@ -670,10 +664,10 @@ void irq_free_descs(unsigned int from, unsigned int cnt)
if (from >= nr_irqs || (from + cnt) > nr_irqs) if (from >= nr_irqs || (from + cnt) > nr_irqs)
return; return;
mutex_lock(&sparse_irq_lock);
for (i = 0; i < cnt; i++) for (i = 0; i < cnt; i++)
free_desc(from + i); free_desc(from + i);
mutex_lock(&sparse_irq_lock);
bitmap_clear(allocated_irqs, from, cnt); bitmap_clear(allocated_irqs, from, cnt);
mutex_unlock(&sparse_irq_lock); mutex_unlock(&sparse_irq_lock);
} }
...@@ -720,19 +714,15 @@ __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, ...@@ -720,19 +714,15 @@ __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
from, cnt, 0); from, cnt, 0);
ret = -EEXIST; ret = -EEXIST;
if (irq >=0 && start != irq) if (irq >=0 && start != irq)
goto err; goto unlock;
if (start + cnt > nr_irqs) { if (start + cnt > nr_irqs) {
ret = irq_expand_nr_irqs(start + cnt); ret = irq_expand_nr_irqs(start + cnt);
if (ret) if (ret)
goto err; goto unlock;
} }
ret = alloc_descs(start, cnt, node, affinity, owner);
bitmap_set(allocated_irqs, start, cnt); unlock:
mutex_unlock(&sparse_irq_lock);
return alloc_descs(start, cnt, node, affinity, owner);
err:
mutex_unlock(&sparse_irq_lock); mutex_unlock(&sparse_irq_lock);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment