drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c
Extension
.c
Size
5486 bytes
Lines
169
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (unlikely(!node->kfd->ih_wq)) {
			kfifo_free(&node->ih_fifo);
			dev_err(node->adev->dev, "Failed to allocate KFD IH workqueue\n");
			return -ENOMEM;
		}
	}
	spin_lock_init(&node->interrupt_lock);

	INIT_WORK(&node->interrupt_work, interrupt_wq);

	node->interrupts_active = true;

	/*
	 * After this function returns, the interrupt will be enabled. This
	 * barrier ensures that the interrupt running on a different processor
	 * sees all the above writes.
	 */
	smp_wmb();

	return 0;
}

void kfd_interrupt_exit(struct kfd_node *node)
{
	/*
	 * Stop the interrupt handler from writing to the ring and scheduling
	 * workqueue items. The spinlock ensures that any interrupt running
	 * after we have unlocked sees interrupts_active = false.
	 */
	unsigned long flags;

	spin_lock_irqsave(&node->interrupt_lock, flags);
	node->interrupts_active = false;
	spin_unlock_irqrestore(&node->interrupt_lock, flags);
	kfifo_free(&node->ih_fifo);
}

/*
 * Assumption: single reader/writer. This function is not re-entrant
 */
bool enqueue_ih_ring_entry(struct kfd_node *node, const void *ih_ring_entry)
{
	if (kfifo_is_full(&node->ih_fifo)) {
		dev_warn_ratelimited(node->adev->dev, "KFD node %d ih_fifo overflow\n",
				     node->node_id);
		return false;
	}

	kfifo_in(&node->ih_fifo, ih_ring_entry, node->kfd->device_info.ih_ring_entry_size);
	return true;
}

/*
 * Assumption: single reader/writer. This function is not re-entrant
 */
static bool dequeue_ih_ring_entry(struct kfd_node *node, u32 **ih_ring_entry)
{
	int count;

	if (kfifo_is_empty(&node->ih_fifo))
		return false;

	count = kfifo_out_linear_ptr(&node->ih_fifo, ih_ring_entry,
				     node->kfd->device_info.ih_ring_entry_size);
	WARN_ON(count != node->kfd->device_info.ih_ring_entry_size);
	return count == node->kfd->device_info.ih_ring_entry_size;
}

static void interrupt_wq(struct work_struct *work)
{
	struct kfd_node *dev = container_of(work, struct kfd_node, interrupt_work);
	uint32_t *ih_ring_entry;
	unsigned long start_jiffies = jiffies;

	while (dequeue_ih_ring_entry(dev, &ih_ring_entry)) {
		dev->kfd->device_info.event_interrupt_class->interrupt_wq(dev,
								ih_ring_entry);
		kfifo_skip_count(&dev->ih_fifo, dev->kfd->device_info.ih_ring_entry_size);

		if (time_is_before_jiffies(start_jiffies + HZ)) {
			/* If we spent more than a second processing signals,
			 * reschedule the worker to avoid soft-lockup warnings
			 */
			queue_work(dev->kfd->ih_wq, &dev->interrupt_work);
			break;
		}
	}
}

bool interrupt_is_wanted(struct kfd_node *dev,

Annotation

Implementation Notes