Interrupts processed on the CPU can be divided into "hardware interrupts" and "software interrupts", such as the interrupt generated by the NIC is called a hardware interrupt, and if the software is using instructions such as "int 0x10" (X86 platform) to produce interrupts called software interrupts, the hardware interrupts are asynchronous and the timing of their occurrence is unknown. But software interrupts are synchronous, and the CPU is "exactly" aware of the timing of its occurrence.
Similarly, on the GPU, interrupts can be divided into "hardware interrupts" and "software interrupts", such as hot-plug events or Vblank events that produce "hardware interrupts," which appear to be asynchronous on the GPU, and the GPU does not know when these things will happen. The GPU can also generate interrupts using CPU-like int directives, considering a scenario in which the driver sends a drawing command to the hardware and must wait until the hardware has finished executing these commands before it can perform subsequent operations, or the next command that continues after the last command of the hardware is not completed will result in an error. The previous introduction of the scratch register can be added at the end of the command to write a scratch register command, send the command after the drive to poll the scratch register, of course, the use of polling is certainly not appropriate, in fact, the video card can adopt a soft interrupt mechanism, Executing a command like "int xx" After completing the drawing command produces an interrupt, where the GPU is "exact"----when the drawing command is completed.
The fence mentioned above is the specific application of this "software interruption".
As seen in the previous blog, fence is used in the following steps:
Radeon_fence_create->radeon_fence_emit->radeon_fence_wait
The fence mechanism in the Radeon driver is used to synchronize the GPU and cpu,fence mechanisms that rely on GPU-generated soft interrupts and scratch registers. The CP completes a drawing operation and executes the command that generates the interrupt, sending an interrupt signal to the CPU, where the "Interrupt Generation Command" is actually written cp_int_stat register.
In the Radeon driver code, the Radeon_fence_emit function (referred to as the code in the GPU Command Pack chapter) is called after completion of filling the drawing command into the ring buffer, and the R600_fence_ring_emit function is eventually called on the R600 graphics card. The function has the following code:
2327 void R600_fence_ring_emit (struct radeon_device *rdev,
2328 struct radeon_fence *fence)
......
2347/* Emit fence Sequence & Fire IRQ */
2348 Radeon_ring_write (Rdev, PACKET3 (Packet3_set_config_reg, 1));
2349 Radeon_ring_write (Rdev, (Rdev->fence_drv.scratch_reg-
Packet3_set_config_reg_offset) >> 2));
2350 radeon_ring_write (Rdev, FENCE->SEQ);
2351/* Cp_interrupt Packet 3 no longer exists, use packet 0 */
2352 radeon_ring_write (Rdev, PACKET0 (cp_int_status, 0));
2353 Radeon_ring_write (Rdev, Rb_int_stat);
This lets the GPU execute commands (2352-2353 lines of code) similar to the "int xx" instruction that the CPU executes in the operating system, which means to write the Cp_int_status register, but notice that the register Cp_int_status is an interrupt state register, It is not possible to write this register by driving through Mmio, but if CP writes this register it will produce a "software interrupt" (the current observed phenomenon is this, is correct).
Usually the hardware will have some registers to represent the interrupt related information, writes the relevant information in the Register when the hardware produces the interrupt, the driver reads these registers to be able to know and interrupts the related specific information. The scratch register can be useful in the Radeon GPU in addition to such registers indicating the type of interrupt.
In the kernel Radeon driver, each fence is assigned a unique ID number (SEQ), which has the following code in Radeon_fence_emit:
radeon_fence_emit int (struct radeon_device *rdev, struct radeon_fence *fence)
......
Fence->seq = Atomic_add_return (1, &RDEV->FENCE_DRV.SEQ);
The value of the 2340-2350 line code FENCE->SEQ is written to a scratch register, so the scratch register is set to this unique ID number before the drawing command completes the interrupt. Reading the scratch register will tell you which interrupt was generated by the drawing command.
The fence interrupt handler function is radeon_fence_poll_locked. First read the fence number, know that the fence operation caused the interruption, when the resulting interrupt fence number is the last number, you need to assign the last fence number to the current number, while updating the fence timer. If the interrupt is not the last fence number, the timer needs to be judged and then the fence interrupt queue is awakened.
PS. This article describes only the hardware mechanism of software interrupts, and the Linux kernel DRM driver code for the graphics interrupt has a more complex framework that requires in-depth analysis to understand the full picture.
Graphics system in "original" Linux environment and AMD R600 graphics programming (7)--AMD software interrupt for graphics card