Whenever I have a problem with something even remotely associated with interrupts, I go back and closely examine my interrupt stream.
There have been so many times I've found what's best called a 'self race' condition that now I always write my ISRs with that consideration in mind.
The mindset I use is to write the ISR so that it always can occur for no reason at all, or for multiple reasons. The structure is to do all the EOI processing at the beginning of the routine. THEN fall into a loop that loops until there is nothing to do. If you do something, stay in that loop. So what if you have to loop 5 or 6 times to get out, even getting multiple characters. Finally, nothing is there to be done, but you have an interrupt pending. You get out, bang, another interrupt, but nothing to do and you fall through quickly.
The other thing I do, and again this is a personal thing, is try to limit what happens in the ISR. For comm stuff, I set up a ring buffer, and all the ISR does is set data into the buffer IF there is room. If not, throw it away and set an overflow flag.
Unless something is holding parts of my body to a painful object, I really resist trying to do any management or processing of data in the ISR. Get in, get it, get out.
Mike