Hi Yong,
On Fri, Mar 30, 2018 at 11:15 AM Yong Zhi <[email protected]> wrote:
> +/*
> + * Queue as many buffers to CSS as possible. If all buffers don't fit into
> + * CSS buffer queues, they remain unqueued and will be queued later.
> + */
> +int imgu_queue_buffers(struct imgu_device *imgu, bool initial)
> +{
> + unsigned int node;
> + int r = 0;
> + struct imgu_buffer *ibuf;
> +
> + if (!ipu3_css_is_streaming(&imgu->css))
> + return 0;
> +
> + mutex_lock(&imgu->lock);
> +
> + /* Buffer set is queued to FW only when input buffer is ready */
> + if (!imgu_queue_getbuf(imgu, IMGU_NODE_IN)) {
> + mutex_unlock(&imgu->lock);
> + return 0;
> + }
> + for (node = IMGU_NODE_IN + 1; 1; node = (node + 1) % IMGU_NODE_NUM) {
Shouldn't we make (node != IMGU_NODE_IN || imgu_queue_getbuf(imgu,
IMGU_NODE_IN)) the condition here, rather than 1?
This would also let us remove the explicit call to imgu_queue_getbuf()
above the loop.
> + if (node == IMGU_NODE_VF &&
> + (imgu->css.pipe_id == IPU3_CSS_PIPE_ID_CAPTURE ||
> + !imgu->nodes[IMGU_NODE_VF].enabled)) {
> + continue;
> + } else if (node == IMGU_NODE_PV &&
> + (imgu->css.pipe_id == IPU3_CSS_PIPE_ID_VIDEO ||
> + !imgu->nodes[IMGU_NODE_PV].enabled)) {
> + continue;
> + } else if (imgu->queue_enabled[node]) {
> + struct ipu3_css_buffer *buf =
> + imgu_queue_getbuf(imgu, node);
> + int dummy;
> +
> + if (!buf)
> + break;
> +
> + r = ipu3_css_buf_queue(&imgu->css, buf);
> + if (r)
> + break;
> + dummy = imgu_dummybufs_check(imgu, buf);
> + if (!dummy)
> + ibuf = container_of(buf, struct imgu_buffer,
> + css_buf);
> + dev_dbg(&imgu->pci_dev->dev,
> + "queue %s %s buffer %d to css da: 0x%08x\n",
> + dummy ? "dummy" : "user",
> + imgu_node_map[node].name,
> + dummy ? 0 : ibuf->vid_buf.vbb.vb2_buf.index,
> + (u32)buf->daddr);
> + }
> + if (node == IMGU_NODE_IN &&
> + !imgu_queue_getbuf(imgu, IMGU_NODE_IN))
> + break;
My suggestion to the for loop condition is based on this.
> + }
> + mutex_unlock(&imgu->lock);
> +
> + if (r && r != -EBUSY)
> + goto failed;
> +
> + return 0;
> +
> +failed:
> + /*
> + * On error, mark all buffers as failed which are not
> + * yet queued to CSS
> + */
> + dev_err(&imgu->pci_dev->dev,
> + "failed to queue buffer to CSS on queue %i (%d)\n",
> + node, r);
> +
> + if (initial)
> + /* If we were called from streamon(), no need to finish bufs
> */
> + return r;
> +
> + for (node = 0; node < IMGU_NODE_NUM; node++) {
> + struct imgu_buffer *buf, *buf0;
> +
> + if (!imgu->queue_enabled[node])
> + continue; /* Skip disabled queues */
> +
> + mutex_lock(&imgu->lock);
> + list_for_each_entry_safe(buf, buf0,
> &imgu->nodes[node].buffers,
> + vid_buf.list) {
> + if (ipu3_css_buf_state(&buf->css_buf) ==
> + IPU3_CSS_BUFFER_QUEUED)
> + continue; /* Was already queued, skip */
> +
> + ipu3_v4l2_buffer_done(&buf->vid_buf.vbb.vb2_buf,
> + VB2_BUF_STATE_ERROR);
> + }
> + mutex_unlock(&imgu->lock);
> + }
> +
> + return r;
> +}
[snip]
> +static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr)
> +{
> + struct imgu_device *imgu = imgu_ptr;
> +
> + /* Dequeue / queue buffers */
> + do {
> + u64 ns = ktime_get_ns();
> + struct ipu3_css_buffer *b;
> + struct imgu_buffer *buf;
> + unsigned int node;
> + bool dummy;
> +
> + do {
> + mutex_lock(&imgu->lock);
> + b = ipu3_css_buf_dequeue(&imgu->css);
> + mutex_unlock(&imgu->lock);
> + } while (PTR_ERR(b) == -EAGAIN);
> +
> + if (IS_ERR_OR_NULL(b)) {
> + if (!b || PTR_ERR(b) == -EBUSY) /* All done */
> + break;
> + dev_err(&imgu->pci_dev->dev,
> + "failed to dequeue buffers (%ld)\n",
> + PTR_ERR(b));
> + break;
> + }
> +
> + node = imgu_map_node(imgu, b->queue);
> + dummy = imgu_dummybufs_check(imgu, b);
> + if (!dummy)
> + buf = container_of(b, struct imgu_buffer, css_buf);
> + dev_dbg(&imgu->pci_dev->dev,
> + "dequeue %s %s buffer %d from css\n",
> + dummy ? "dummy" : "user",
> + imgu_node_map[node].name,
> + dummy ? 0 : buf->vid_buf.vbb.vb2_buf.index);
> +
> + if (dummy)
> + /* It was a dummy buffer, skip it */
> + continue;
> +
> + /* Fill vb2 buffer entries and tell it's ready */
> + if (!imgu->nodes[node].output) {
> + buf->vid_buf.vbb.vb2_buf.timestamp = ns;
> + buf->vid_buf.vbb.field = V4L2_FIELD_NONE;
> + buf->vid_buf.vbb.sequence =
> +
> atomic_inc_return(&imgu->nodes[node].sequence);
> + }
> + imgu_buffer_done(imgu, &buf->vid_buf.vbb.vb2_buf,
> + ipu3_css_buf_state(&buf->css_buf) ==
> + IPU3_CSS_BUFFER_DONE ?
> + VB2_BUF_STATE_DONE :
> + VB2_BUF_STATE_ERROR);
> + } while (1);
> +
> + /*
> + * Try to queue more buffers for CSS.
> + * qbuf_barrier is used to disable new buffers
> + * to be queued to CSS.
> + */
> + if (!atomic_read(&imgu->qbuf_barrier))
> + imgu_queue_buffers(imgu, false);
> +
> + return IRQ_NONE;
This is a serious bug. An interrupt handler must not return IRQ_NONE
unless it's really sure that the device it handles did not generate
any interrupt. This threaded handler is called as a result of the
hardirq handler actually finding an interrupt to be handled, so we
must always return IRQ_HANDLED here.
[snip]
> +static int __maybe_unused imgu_suspend(struct device *dev)
> +{
> + struct pci_dev *pci_dev = to_pci_dev(dev);
> + struct imgu_device *imgu = pci_get_drvdata(pci_dev);
> + unsigned long expire;
> +
> + dev_dbg(dev, "enter %s\n", __func__);
> + imgu->suspend_in_stream = ipu3_css_is_streaming(&imgu->css);
> + if (!imgu->suspend_in_stream)
> + goto out;
> + /* Block new buffers to be queued to CSS. */
> + atomic_set(&imgu->qbuf_barrier, 1);
> + /*
> + * Wait for currently running irq handler to be done so that
> + * no new buffers will be queued to fw later.
> + */
> + synchronize_irq(pci_dev->irq);
> + /* Wait until all buffers in CSS are done. */
> + expire = jiffies + msecs_to_jiffies(1000);
> + while (!ipu3_css_queue_empty(&imgu->css)) {
> + if (time_is_before_jiffies(expire)) {
> + dev_err(dev, "wait buffer drain timeout.\n");
> + break;
> + }
> + }
Uhm. We struggle to save some power by suspending the device only to
end up with an ugly busy wait that could take even a second here. This
doesn't make any sense.
We had a working solution using a wait queue in previous revision [1].
What happened to it?
[1]
https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/1029594/2/drivers/media/pci/intel/ipu3/ipu3.c#b913
(see the left side)
> + ipu3_css_stop_streaming(&imgu->css);
> + atomic_set(&imgu->qbuf_barrier, 0);
> + imgu_powerdown(imgu);
> + pm_runtime_force_suspend(dev);
> +out:
> + dev_dbg(dev, "leave %s\n", __func__);
> + return 0;
> +}
Best regards,
Tomasz