Hi Paul, On Tue, Sep 06, 2022 at 07:44:37PM +0900, Paul Elder wrote: > The CSI hardware compatible with this driver handles buffers using a > ping-pong mechanism with two sets of destination addresses. Normally, > when an interrupt comes in to signal the completion of one buffer, say > FB0, it assigns the next buffer in the queue to the next FB0, and the > hardware starts to capture into FB1 in the meantime. > > In a buffer underrun situation, in the above example without loss of > generality, if a new buffer is queued before the interrupt for FB0 comes > in, we can program the buffer into FB1 (which is programmed with a dummy > buffer, as there is a buffer underrun). > > This of course races with the interrupt that signals FB0 completion, as > once that interrupt comes in, we are no longer guaranteed that the > programming of FB1 was in time and must assume it was too late. This > race is resolved by locking the programming of FB1. If it came after the > interrupt for FB0, then the variable that is used to determine which FB > to program would have been swapped by the interrupt handler, thus > resolving the race. > > Signed-off-by: Paul Elder <paul.elder@xxxxxxxxxxxxxxxx> Thanks a lot for this patch, and spending time commenting the issue in the code, and the good changelog. LGTM. Acked-by: Rui Miguel Silva <rmfrfs@xxxxxxxxx> Cheers, Rui > --- > drivers/staging/media/imx/imx7-media-csi.c | 49 ++++++++++++++++++++++ > 1 file changed, 49 insertions(+) > > diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c > index a0553c24cce4..06e50080ed31 100644 > --- a/drivers/staging/media/imx/imx7-media-csi.c > +++ b/drivers/staging/media/imx/imx7-media-csi.c > @@ -1296,11 +1296,60 @@ static int imx7_csi_video_buf_prepare(struct vb2_buffer *vb) > return 0; > } > > +static int imx7_csi_fast_track_buffer(struct imx7_csi *csi, > + struct imx7_csi_vb2_buffer *buf) > +{ > + unsigned long flags; > + dma_addr_t phys; > + int buf_num; > + int ret = -EBUSY; > + > + if (!csi->is_streaming) > + return ret; > + > + phys = vb2_dma_contig_plane_dma_addr(&buf->vbuf.vb2_buf, 0); > + > + /* > + * buf_num holds the fb id of the most recently (*not* the next > + * anticipated) triggered interrupt. Without loss of generality, if > + * buf_num is 0 and we get to this section before the irq for fb1, the > + * buffer that we are fast-tracking into fb0 should be programmed in > + * time to be captured into. If the irq for fb1 already happened, then > + * buf_num would be 1, and we would fast-track the buffer into fb1 > + * instead. This guarantees that we won't try to fast-track into fb0 > + * and race against the start-of-capture into fb0. > + * > + * We only fast-track the buffer if the currently programmed buffer is > + * a dummy buffer. We can check the active_vb2_buf instead as it is > + * always modified along with programming the fb[0,1] registers via the > + * lock (besides setup and cleanup). If it is not a dummy buffer then > + * we queue it normally, as fast-tracking is not an option. > + */ > + > + spin_lock_irqsave(&csi->irqlock, flags); > + > + buf_num = csi->buf_num; > + if (csi->active_vb2_buf[buf_num] == NULL) { > + csi->active_vb2_buf[buf_num] = buf; > + imx7_csi_update_buf(csi, phys, buf_num); > + ret = 0; > + } > + > + spin_unlock_irqrestore(&csi->irqlock, flags); > + > + return ret; > +} > + > static void imx7_csi_video_buf_queue(struct vb2_buffer *vb) > { > struct imx7_csi *csi = vb2_get_drv_priv(vb->vb2_queue); > struct imx7_csi_vb2_buffer *buf = to_imx7_csi_vb2_buffer(vb); > unsigned long flags; > + int ret; > + > + ret = imx7_csi_fast_track_buffer(csi, buf); > + if (!ret) > + return; > > spin_lock_irqsave(&csi->q_lock, flags); > > -- > 2.30.2 >