Commit b960a828 authored by Keith Owens's avatar Keith Owens Committed by Tony Luck

[IA64] Avoid a rare deadlock during unwind

There is a rare deadlock condition during unwind script creation.  If
build_script() is interrupted in the middle of creating the script, it
holds the script write lock.  If the interrupt handler needs to call
unwind for some failure condition, unwind will try to read the
incomplete script and will deadlock on the script lock.

The fix is to disable interrupts while building the script, so
interrupt handlers never see partial scripts.

Promoting spin_lock_irqsave() from script_new() to find_save_locs()
changes the indentation, so the patch looks bigger than it really is.
Signed-off-by: default avatarKeith Owens <kaos@sgi.com>
Signed-off-by: default avatarTony Luck <tony.luck@intel.com>
parent 12ac2444
...@@ -1269,7 +1269,6 @@ script_new (unsigned long ip) ...@@ -1269,7 +1269,6 @@ script_new (unsigned long ip)
{ {
struct unw_script *script, *prev, *tmp; struct unw_script *script, *prev, *tmp;
unw_hash_index_t index; unw_hash_index_t index;
unsigned long flags;
unsigned short head; unsigned short head;
STAT(++unw.stat.script.news); STAT(++unw.stat.script.news);
...@@ -1278,13 +1277,9 @@ script_new (unsigned long ip) ...@@ -1278,13 +1277,9 @@ script_new (unsigned long ip)
* Can't (easily) use cmpxchg() here because of ABA problem * Can't (easily) use cmpxchg() here because of ABA problem
* that is intrinsic in cmpxchg()... * that is intrinsic in cmpxchg()...
*/ */
spin_lock_irqsave(&unw.lock, flags);
{
head = unw.lru_head; head = unw.lru_head;
script = unw.cache + head; script = unw.cache + head;
unw.lru_head = script->lru_chain; unw.lru_head = script->lru_chain;
}
spin_unlock(&unw.lock);
/* /*
* We'd deadlock here if we interrupted a thread that is holding a read lock on * We'd deadlock here if we interrupted a thread that is holding a read lock on
...@@ -1295,8 +1290,6 @@ script_new (unsigned long ip) ...@@ -1295,8 +1290,6 @@ script_new (unsigned long ip)
if (!write_trylock(&script->lock)) if (!write_trylock(&script->lock))
return NULL; return NULL;
spin_lock(&unw.lock);
{
/* re-insert script at the tail of the LRU chain: */ /* re-insert script at the tail of the LRU chain: */
unw.cache[unw.lru_tail].lru_chain = head; unw.cache[unw.lru_tail].lru_chain = head;
unw.lru_tail = head; unw.lru_tail = head;
...@@ -1330,8 +1323,6 @@ script_new (unsigned long ip) ...@@ -1330,8 +1323,6 @@ script_new (unsigned long ip)
script->ip = ip; /* set new IP while we're holding the locks */ script->ip = ip; /* set new IP while we're holding the locks */
STAT(if (script->coll_chain < UNW_CACHE_SIZE) ++unw.stat.script.collisions); STAT(if (script->coll_chain < UNW_CACHE_SIZE) ++unw.stat.script.collisions);
}
spin_unlock_irqrestore(&unw.lock, flags);
script->flags = 0; script->flags = 0;
script->hint = 0; script->hint = 0;
...@@ -1830,6 +1821,7 @@ find_save_locs (struct unw_frame_info *info) ...@@ -1830,6 +1821,7 @@ find_save_locs (struct unw_frame_info *info)
{ {
int have_write_lock = 0; int have_write_lock = 0;
struct unw_script *scr; struct unw_script *scr;
unsigned long flags = 0;
if ((info->ip & (local_cpu_data->unimpl_va_mask | 0xf)) || info->ip < TASK_SIZE) { if ((info->ip & (local_cpu_data->unimpl_va_mask | 0xf)) || info->ip < TASK_SIZE) {
/* don't let obviously bad addresses pollute the cache */ /* don't let obviously bad addresses pollute the cache */
...@@ -1841,8 +1833,10 @@ find_save_locs (struct unw_frame_info *info) ...@@ -1841,8 +1833,10 @@ find_save_locs (struct unw_frame_info *info)
scr = script_lookup(info); scr = script_lookup(info);
if (!scr) { if (!scr) {
spin_lock_irqsave(&unw.lock, flags);
scr = build_script(info); scr = build_script(info);
if (!scr) { if (!scr) {
spin_unlock_irqrestore(&unw.lock, flags);
UNW_DPRINT(0, UNW_DPRINT(0,
"unwind.%s: failed to locate/build unwind script for ip %lx\n", "unwind.%s: failed to locate/build unwind script for ip %lx\n",
__FUNCTION__, info->ip); __FUNCTION__, info->ip);
...@@ -1855,9 +1849,10 @@ find_save_locs (struct unw_frame_info *info) ...@@ -1855,9 +1849,10 @@ find_save_locs (struct unw_frame_info *info)
run_script(scr, info); run_script(scr, info);
if (have_write_lock) if (have_write_lock) {
write_unlock(&scr->lock); write_unlock(&scr->lock);
else spin_unlock_irqrestore(&unw.lock, flags);
} else
read_unlock(&scr->lock); read_unlock(&scr->lock);
return 0; return 0;
} }
......
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