char: xillybus: Ignore and report unsolicited interrupts

During initialization, the hardware should issue interrupts only in
response to requests from the host. Ignore and log unexpected interrupts,
as these indicate misbehaving hardware, and return IRQ_NONE when the
interrupt appears to be spurious.

In the same spirit, in xilly_quiesce(), assign endpoint->num_channels = 0
before allowing the ISR, in order to expose whether the hardware
incorrectly sends messages related to data channels during shutdown.

Assisted-by: Deepseek:v4-pro Kimi:K2.6 ChatGPT:GPT-5.5 Claude:Sonnet-4.6
Assisted-by: Sashiko-0.2.5:gemini-3.1-pro-preview
Signed-off-by: Eli Billauer <eli.billauer@gmail.com>
Link: https://patch.msgid.link/20261009074557.19996-8-eli.billauer@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Eli Billauer 2026-10-09 09:45:57 +02:00 committed by Greg Kroah-Hartman
parent 1ce74f6f22
commit 7dc112d608
2 changed files with 49 additions and 1 deletions

View File

@ -94,6 +94,9 @@ struct xilly_endpoint {
__iomem void *registers;
int fatal_error;
bool allow_isr;
spinlock_t allow_isr_lock;
struct mutex register_mutex;
wait_queue_head_t ep_wait;

View File

@ -73,6 +73,8 @@ static struct workqueue_struct *xillybus_wq;
*
* rd_spinlock does the same with rd_*_buf_idx, rd_empty and end_offset.
*
* allow_isr_lock protects allow_isr.
*
* register_mutex is endpoint-specific, and is held when non-atomic
* register operations are performed. wr_mutex and rd_mutex may be
* held when register_mutex is taken, but none of the spinlocks. Note that
@ -85,7 +87,8 @@ static struct workqueue_struct *xillybus_wq;
* Only interruptible blocking is allowed on mutexes and wait queues.
*
* All in all, the locking order goes (with skips allowed, of course):
* wr_mutex -> rd_mutex -> register_mutex -> wr_spinlock -> rd_spinlock
* wr_mutex -> rd_mutex -> register_mutex ->
* allow_isr_lock -> wr_spinlock -> rd_spinlock
*/
static void malformed_message(struct xilly_endpoint *endpoint, u32 *buf)
@ -120,6 +123,13 @@ irqreturn_t xillybus_isr(int irq, void *data)
unsigned int msg_channel, msg_bufno, msg_data, msg_dir;
struct xilly_channel *channel;
guard(spinlock)(&ep->allow_isr_lock);
if (!ep->allow_isr) {
dev_err_ratelimited(ep->dev, "Unexpected interrupt! Something is wrong with the hardware.\n");
return IRQ_NONE;
}
buf = ep->msgbuf_addr;
buf_size = ep->msg_buf_size/sizeof(u32);
@ -138,6 +148,7 @@ irqreturn_t xillybus_isr(int irq, void *data)
if (++ep->failed_messages > 10) {
dev_err(ep->dev,
"Lost sync with interrupt messages. Stopping.\n");
return IRQ_NONE;
} else {
dma_sync_single_for_device(ep->dev,
ep->msgbuf_dma_addr,
@ -284,6 +295,19 @@ irqreturn_t xillybus_isr(int irq, void *data)
}
EXPORT_SYMBOL(xillybus_isr);
/*
* xilly_allow_isr() is similar to enabling / disabling the interrupt,
* with the difference that if an interrupt is issued while ep->allow_isr
* is false, this is visible in the kernel log.
*/
static void xilly_allow_isr(struct xilly_endpoint *ep, bool newstate)
{
guard(spinlock_irqsave)(&ep->allow_isr_lock);
ep->allow_isr = newstate;
}
/*
* A few trivial memory management functions.
* NOTE: These functions are used only on probe and remove, and therefore
@ -652,6 +676,8 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint)
channel->wr_sleepy = 1;
xilly_allow_isr(endpoint, true);
iowrite32(1 |
(3 << 24), /* Opcode 3 for channel 0 = Send IDT */
endpoint->registers + fpga_buf_ctrl_reg);
@ -660,6 +686,8 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint)
(!channel->wr_sleepy),
XILLY_TIMEOUT);
xilly_allow_isr(endpoint, false);
if (t <= 0) {
dev_err(endpoint->dev, "Failed to obtain IDT. Aborting.\n");
@ -1838,6 +1866,9 @@ struct xilly_endpoint *xillybus_init_endpoint(struct device *dev)
endpoint->failed_messages = 0;
endpoint->fatal_error = 0;
endpoint->allow_isr = false;
spin_lock_init(&endpoint->allow_isr_lock);
init_waitqueue_head(&endpoint->ep_wait);
mutex_init(&endpoint->register_mutex);
@ -1850,6 +1881,9 @@ static int xilly_quiesce(struct xilly_endpoint *endpoint)
long t;
endpoint->idtlen = -1;
endpoint->num_channels = 0;
xilly_allow_isr(endpoint, true);
iowrite32((u32) (endpoint->dma_using_dac & 0x0001),
endpoint->registers + fpga_dma_control_reg);
@ -1857,6 +1891,9 @@ static int xilly_quiesce(struct xilly_endpoint *endpoint)
t = wait_event_interruptible_timeout(endpoint->ep_wait,
(endpoint->idtlen >= 0),
XILLY_TIMEOUT);
xilly_allow_isr(endpoint, false);
if (t <= 0) {
dev_err(endpoint->dev,
"Failed to quiesce the device on exit.\n");
@ -1910,6 +1947,8 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
endpoint->idtlen = -1;
xilly_allow_isr(endpoint, true);
/*
* Set DMA 32/64 bit mode, quiesce the device (?!) and get IDT
* buffer size.
@ -1920,6 +1959,9 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
t = wait_event_interruptible_timeout(endpoint->ep_wait,
(endpoint->idtlen >= 0),
XILLY_TIMEOUT);
xilly_allow_isr(endpoint, false);
if (t <= 0) {
dev_err(endpoint->dev, "No response from FPGA. Aborting.\n");
return -ENODEV;
@ -1946,6 +1988,7 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
if (rc)
goto failed_idt;
/* xilly_obtain_idt() allows and then disallows the ISR */
rc = xilly_obtain_idt(endpoint);
if (rc)
goto failed_idt;
@ -1964,6 +2007,8 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
if (rc)
goto failed_idt;
xilly_allow_isr(endpoint, true);
rc = xillybus_init_chrdev(dev, &xillybus_fops,
endpoint->owner, endpoint,
idt_handle.names,