> 21.01.2022 19:24, Akhil R пишет: > >>>>>>> +static int tegra_dma_terminate_all(struct dma_chan *dc) { > >>>>>>> + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); > >>>>>>> + unsigned long flags; > >>>>>>> + LIST_HEAD(head); > >>>>>>> + int err; > >>>>>>> + > >>>>>>> + if (tdc->dma_desc) { > >>>>>> > >>>>>> Needs locking protection against racing with the interrupt handler. > >>>>> tegra_dma_stop_client() waits for the in-flight transfer to > >>>>> complete and prevents any additional transfer to start. > >>>>> Wouldn't it manage the race? Do you see any potential issue there? > >>>> > >>>> You should consider interrupt handler like a process running in a > >>>> parallel thread. The interrupt handler sets tdc->dma_desc to NULL, > >>>> hence you'll get NULL dereference in tegra_dma_stop_client(). > >>> > >>> Is it better if I remove the below part from tegra_dma_stop_client() > >>> so that dma_desc is not accessed at all? > >>> > >>> + wcount = tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT); > >>> + tdc->dma_desc->bytes_transferred += > >>> + tdc->dma_desc->bytes_requested - (wcount * 4); > >>> > >>> Because I don't see a point in updating the value there. dma_desc is > >>> set to NULL in the next step in terminate_all() anyway. > >> > >> That isn't going help you much because you also can't release DMA > >> descriptor while interrupt handler still may be running and using > >> that descriptor. > > > > Does the below functions look good to resolve the issue, provided > > tegra_dma_stop_client() doesn't access dma_desc? > > Stop shall not race with the start. > > > +static int tegra_dma_terminate_all(struct dma_chan *dc) { > > + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); > > + unsigned long flags; > > + LIST_HEAD(head); > > + int err; > > + > > + err = tegra_dma_stop_client(tdc); > > + if (err) > > + return err; > > + > > + tegra_dma_stop(tdc); > > + > > + spin_lock_irqsave(&tdc->vc.lock, flags); > > + tegra_dma_sid_free(tdc); > > + tdc->dma_desc = NULL; > > + > > + vchan_get_all_descriptors(&tdc->vc, &head); > > + spin_unlock_irqrestore(&tdc->vc.lock, flags); > > + > > + vchan_dma_desc_free_list(&tdc->vc, &head); > > + > > + return 0; > > +} > > > > +static irqreturn_t tegra_dma_isr(int irq, void *dev_id) { > > + struct tegra_dma_channel *tdc = dev_id; > > + struct tegra_dma_desc *dma_desc = tdc->dma_desc; > > + struct tegra_dma_sg_req *sg_req; > > + u32 status; > > + > > + /* Check channel error status register */ > > + status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS); > > + if (status) { > > + tegra_dma_chan_decode_error(tdc, status); > > + tegra_dma_dump_chan_regs(tdc); > > + tdc_write(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS, 0xFFFFFFFF); > > + } > > + > > + status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS); > > + if (!(status & TEGRA_GPCDMA_STATUS_ISE_EOC)) > > + return IRQ_HANDLED; > > + > > + tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS, > > + TEGRA_GPCDMA_STATUS_ISE_EOC); > > + > > + spin_lock(&tdc->vc.lock); > > + if (!dma_desc) > All checks and assignments must be done inside of critical section. Okay. So, the lock should be held throughout the function. Do you think tegra_dma_pause should also hold a lock and remove irq_synchronize? That function also writes to CSR register. Regards, Akhil