Skip to content

Commit 880153e

Browse files
Jacopo Mondipelwell
authored andcommitted
media: pisp_be: Split jobs creation and scheduling
Currently the 'pispbe_schedule()' function does two things: 1) Tries to assemble a job by inspecting all the video node queues to make sure all the required buffers are available 2) Submit the job to the hardware The pispbe_schedule() function is called at: - video device start_streaming() time - video device qbuf() time - irq handler As assembling a job requires inspecting all queues, it is a rather time consuming operation which is better not run in IRQ context. To avoid executing the time consuming job creation in interrupt context split the job creation and job scheduling in two distinct operations. When a well-formed job is created, append it to the newly introduced 'pispbe->job_queue' where it will be dequeued from by the scheduling routine. As the per-node 'ready_queue' buffer list is only accessed in vb2 ops callbacks, protected by the node->queue_lock mutex, it is not necessary to guard it with a dedicated spinlock so drop it. Also use the spin_lock_irq() variant in all functions not called from an IRQ context where the spin_lock_irqsave() version was used. Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
1 parent 95808c5 commit 880153e

File tree

1 file changed

+93
-85
lines changed
  • drivers/media/platform/raspberrypi/pisp_be

1 file changed

+93
-85
lines changed

drivers/media/platform/raspberrypi/pisp_be/pisp_be.c

Lines changed: 93 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/module.h>
1313
#include <linux/platform_device.h>
1414
#include <linux/pm_runtime.h>
15+
#include <linux/slab.h>
1516
#include <media/v4l2-device.h>
1617
#include <media/v4l2-ioctl.h>
1718
#include <media/videobuf2-dma-contig.h>
@@ -172,8 +173,6 @@ struct pispbe_node {
172173
struct mutex node_lock;
173174
/* vb2_queue lock */
174175
struct mutex queue_lock;
175-
/* Protect pispbe_node->ready_queue and pispbe_buffer->ready_list */
176-
spinlock_t ready_lock;
177176
struct list_head ready_queue;
178177
struct vb2_queue queue;
179178
struct v4l2_format format;
@@ -219,6 +218,9 @@ struct pispbe_hw_enables {
219218

220219
/* Records a job configuration and memory addresses. */
221220
struct pispbe_job_descriptor {
221+
struct list_head queue;
222+
struct pispbe_buffer *buffers[PISPBE_NUM_NODES];
223+
struct pispbe_node_group *node_group;
222224
dma_addr_t hw_dma_addrs[N_HW_ADDRESSES];
223225
struct pisp_be_tiles_config *config;
224226
struct pispbe_hw_enables hw_enables;
@@ -235,8 +237,10 @@ struct pispbe_dev {
235237
struct clk *clk;
236238
struct pispbe_node_group node_group[PISPBE_NUM_NODE_GROUPS];
237239
struct pispbe_job queued_job, running_job;
238-
spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */
240+
/* protects "hw_busy" flag, streaming_map and job_queue */
241+
spinlock_t hw_lock;
239242
bool hw_busy; /* non-zero if a job is queued or is being started */
243+
struct list_head job_queue;
240244
int irq;
241245
u32 hw_version;
242246
u8 done, started;
@@ -460,43 +464,49 @@ static void pispbe_xlate_addrs(struct pispbe_job_descriptor *job,
460464
* For Output0, Output1, Tdn and Stitch, a buffer only needs to be
461465
* available if the blocks are enabled in the config.
462466
*
463-
* Needs to be called with hw_lock held.
467+
* If all the buffers required to form a job are available, append the
468+
* job descriptor to the job queue to be later queued to the HW.
464469
*
465470
* Returns 0 if a job has been successfully prepared, < 0 otherwise.
466471
*/
467-
static int pispbe_prepare_job(struct pispbe_node_group *node_group,
468-
struct pispbe_job_descriptor *job)
472+
static int pispbe_prepare_job(struct pispbe_node_group *node_group)
469473
{
474+
struct pispbe_job_descriptor __free(kfree) *job = NULL;
470475
struct pispbe_buffer *buf[PISPBE_NUM_NODES] = {};
471476
struct pispbe_dev *pispbe = node_group->pispbe;
477+
unsigned int streaming_map;
472478
unsigned int config_index;
473479
struct pispbe_node *node;
474-
unsigned long flags;
475480

476-
lockdep_assert_held(&pispbe->hw_lock);
481+
lockdep_assert_irqs_enabled();
477482

478-
memset(job, 0, sizeof(struct pispbe_job_descriptor));
483+
scoped_guard(spinlock_irq, &pispbe->hw_lock) {
484+
static const u32 mask = BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE);
479485

480-
if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) &
481-
node_group->streaming_map) !=
482-
(BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)))
483-
return -ENODEV;
486+
if ((node_group->streaming_map & mask) != mask)
487+
return -ENODEV;
488+
489+
/*
490+
* Take a copy of streaming_map: nodes activated after this
491+
* point are ignored when preparing this job.
492+
*/
493+
streaming_map = node_group->streaming_map;
494+
}
495+
496+
job = kzalloc(sizeof(*job), GFP_KERNEL);
497+
if (!job)
498+
return -ENOMEM;
484499

485500
node = &node_group->node[CONFIG_NODE];
486-
spin_lock_irqsave(&node->ready_lock, flags);
487501
buf[CONFIG_NODE] = list_first_entry_or_null(&node->ready_queue,
488502
struct pispbe_buffer,
489503
ready_list);
490-
if (buf[CONFIG_NODE]) {
491-
list_del(&buf[CONFIG_NODE]->ready_list);
492-
pispbe->queued_job.buf[CONFIG_NODE] = buf[CONFIG_NODE];
493-
}
494-
spin_unlock_irqrestore(&node->ready_lock, flags);
495-
496-
/* Exit early if no config buffer has been queued. */
497504
if (!buf[CONFIG_NODE])
498505
return -ENODEV;
499506

507+
list_del(&buf[CONFIG_NODE]->ready_list);
508+
job->buffers[CONFIG_NODE] = buf[CONFIG_NODE];
509+
500510
config_index = buf[CONFIG_NODE]->vb.vb2_buf.index;
501511
job->config = &node_group->config[config_index];
502512
job->tiles = node_group->config_dma_addr +
@@ -516,7 +526,7 @@ static int pispbe_prepare_job(struct pispbe_node_group *node_group,
516526
continue;
517527

518528
buf[i] = NULL;
519-
if (!(node_group->streaming_map & BIT(i)))
529+
if (!(streaming_map & BIT(i)))
520530
continue;
521531

522532
if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) &&
@@ -543,25 +553,30 @@ static int pispbe_prepare_job(struct pispbe_node_group *node_group,
543553
node = &node_group->node[i];
544554

545555
/* Pull a buffer from each V4L2 queue to form the queued job */
546-
spin_lock_irqsave(&node->ready_lock, flags);
547556
buf[i] = list_first_entry_or_null(&node->ready_queue,
548557
struct pispbe_buffer,
549558
ready_list);
550559
if (buf[i]) {
551560
list_del(&buf[i]->ready_list);
552-
pispbe->queued_job.buf[i] = buf[i];
561+
job->buffers[i] = buf[i];
553562
}
554-
spin_unlock_irqrestore(&node->ready_lock, flags);
555563

556564
if (!buf[i] && !ignore_buffers)
557565
goto err_return_buffers;
558566
}
559567

560-
pispbe->queued_job.node_group = node_group;
568+
job->node_group = node_group;
561569

562570
/* Convert buffers to DMA addresses for the hardware */
563571
pispbe_xlate_addrs(job, buf, node_group);
564572

573+
scoped_guard(spinlock_irq, &pispbe->hw_lock) {
574+
list_add_tail(&job->queue, &pispbe->job_queue);
575+
}
576+
577+
/* Set job to NULL to avoid automatic release due to __free(). */
578+
job = NULL;
579+
565580
return 0;
566581

567582
err_return_buffers:
@@ -572,63 +587,51 @@ static int pispbe_prepare_job(struct pispbe_node_group *node_group,
572587
continue;
573588

574589
/* Return the buffer to the ready_list queue */
575-
spin_lock_irqsave(&n->ready_lock, flags);
576590
list_add(&buf[i]->ready_list, &n->ready_queue);
577-
spin_unlock_irqrestore(&n->ready_lock, flags);
578591
}
579592

580-
memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job));
581-
582593
return -ENODEV;
583594
}
584595

585596
static void pispbe_schedule(struct pispbe_dev *pispbe,
586597
struct pispbe_node_group *node_group,
587598
bool clear_hw_busy)
588599
{
589-
struct pispbe_job_descriptor job;
590-
unsigned long flags;
600+
struct pispbe_job_descriptor *job;
591601

592-
spin_lock_irqsave(&pispbe->hw_lock, flags);
602+
scoped_guard(spinlock_irqsave, &pispbe->hw_lock) {
603+
if (clear_hw_busy)
604+
pispbe->hw_busy = false;
593605

594-
if (clear_hw_busy)
595-
pispbe->hw_busy = false;
596-
597-
if (pispbe->hw_busy)
598-
goto unlock_and_return;
606+
if (pispbe->hw_busy)
607+
return;
599608

600-
for (unsigned int i = 0; i < PISPBE_NUM_NODE_GROUPS; i++) {
601-
int ret;
609+
job = list_first_entry_or_null(&pispbe->job_queue,
610+
struct pispbe_job_descriptor,
611+
queue);
612+
if (!job)
613+
return;
602614

603-
/* Schedule jobs only for a specific group. */
604-
if (node_group && &pispbe->node_group[i] != node_group)
615+
if (node_group && job->node_group != node_group)
605616
continue;
606617

607-
/*
608-
* Prepare a job for this group, if the group is not ready
609-
* continue and try with the next one.
610-
*/
611-
ret = pispbe_prepare_job(&pispbe->node_group[i], &job);
612-
if (ret)
613-
continue;
614-
615-
/*
616-
* We can kick the job off without the hw_lock, as this can
617-
* never run again until hw_busy is cleared, which will happen
618-
* only when the following job has been queued and an interrupt
619-
* is rised.
620-
*/
621-
pispbe->hw_busy = true;
622-
spin_unlock_irqrestore(&pispbe->hw_lock, flags);
618+
list_del(&job->queue);
623619

624-
pispbe_queue_job(pispbe, &job);
620+
for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++)
621+
pispbe->queued_job.buf[i] = job->buffers[i];
622+
pispbe->queued_job.node_group = job->node_group;
625623

626-
return;
624+
pispbe->hw_busy = true;
627625
}
628626

629-
unlock_and_return:
630-
/* No job has been queued, just release the lock and return. */
631-
spin_unlock_irqrestore(&pispbe->hw_lock, flags);
627+
/*
628+
* We can kick the job off without the hw_lock, as this can
629+
* never run again until hw_busy is cleared, which will happen
630+
* only when the following job has been queued and an interrupt
631+
* is rised.
632+
*/
633+
pispbe_queue_job(pispbe, job);
634+
kfree(job);
632635
}
633636

634637
static void pispbe_isr_jobdone(struct pispbe_dev *pispbe,
@@ -881,18 +884,16 @@ static void pispbe_node_buffer_queue(struct vb2_buffer *buf)
881884
struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue);
882885
struct pispbe_node_group *node_group = node->node_group;
883886
struct pispbe_dev *pispbe = node->node_group->pispbe;
884-
unsigned long flags;
885887

886888
dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
887-
spin_lock_irqsave(&node->ready_lock, flags);
888889
list_add_tail(&buffer->ready_list, &node->ready_queue);
889-
spin_unlock_irqrestore(&node->ready_lock, flags);
890890

891891
/*
892892
* Every time we add a buffer, check if there's now some work for the hw
893893
* to do, but only for this client.
894894
*/
895-
pispbe_schedule(node_group->pispbe, node_group, false);
895+
if (!pispbe_prepare_job(node_group))
896+
pispbe_schedule(pispbe, node_group, false);
896897
}
897898

898899
static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
@@ -901,35 +902,33 @@ static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
901902
struct pispbe_node_group *node_group = node->node_group;
902903
struct pispbe_dev *pispbe = node_group->pispbe;
903904
struct pispbe_buffer *buf, *tmp;
904-
unsigned long flags;
905905
int ret;
906906

907907
ret = pm_runtime_resume_and_get(pispbe->dev);
908908
if (ret < 0)
909909
goto err_return_buffers;
910910

911-
spin_lock_irqsave(&pispbe->hw_lock, flags);
912-
node->node_group->streaming_map |= BIT(node->id);
913-
node->node_group->sequence = 0;
914-
spin_unlock_irqrestore(&pispbe->hw_lock, flags);
911+
scoped_guard(spinlock_irq, &pispbe->hw_lock) {
912+
node->node_group->streaming_map |= BIT(node->id);
913+
node->node_group->sequence = 0;
914+
}
915915

916916
dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n",
917917
__func__, NODE_NAME(node), count);
918918
dev_dbg(pispbe->dev, "Nodes streaming for this group now 0x%x\n",
919919
node->node_group->streaming_map);
920920

921921
/* Maybe we're ready to run. */
922-
pispbe_schedule(node_group->pispbe, node_group, false);
922+
if (!pispbe_prepare_job(node_group))
923+
pispbe_schedule(pispbe, node_group, false);
923924

924925
return 0;
925926

926927
err_return_buffers:
927-
spin_lock_irqsave(&pispbe->hw_lock, flags);
928928
list_for_each_entry_safe(buf, tmp, &node->ready_queue, ready_list) {
929929
list_del(&buf->ready_list);
930930
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
931931
}
932-
spin_unlock_irqrestore(&pispbe->hw_lock, flags);
933932

934933
return ret;
935934
}
@@ -939,8 +938,9 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
939938
struct pispbe_node *node = vb2_get_drv_priv(q);
940939
struct pispbe_node_group *node_group = node->node_group;
941940
struct pispbe_dev *pispbe = node_group->pispbe;
941+
struct pispbe_job_descriptor *job, *temp;
942942
struct pispbe_buffer *buf;
943-
unsigned long flags;
943+
LIST_HEAD(tmp_list);
944944

945945
/*
946946
* Now this is a bit awkward. In a simple M2M device we could just wait
@@ -952,27 +952,34 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
952952
* This may return buffers out of order.
953953
*/
954954
dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
955-
spin_lock_irqsave(&pispbe->hw_lock, flags);
956955
do {
957-
unsigned long flags1;
958-
959-
spin_lock_irqsave(&node->ready_lock, flags1);
960956
buf = list_first_entry_or_null(&node->ready_queue,
961957
struct pispbe_buffer,
962958
ready_list);
963959
if (buf) {
964960
list_del(&buf->ready_list);
965961
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
966962
}
967-
spin_unlock_irqrestore(&node->ready_lock, flags1);
968963
} while (buf);
969-
spin_unlock_irqrestore(&pispbe->hw_lock, flags);
970964

971965
vb2_wait_for_all_buffers(&node->queue);
972966

973-
spin_lock_irqsave(&pispbe->hw_lock, flags);
967+
spin_lock_irq(&pispbe->hw_lock);
974968
node_group->streaming_map &= ~BIT(node->id);
975-
spin_unlock_irqrestore(&pispbe->hw_lock, flags);
969+
970+
if (node_group->streaming_map == 0) {
971+
/*
972+
* If all nodes have stopped streaming release all jobs
973+
* without holding the lock.
974+
*/
975+
list_splice_init(&pispbe->job_queue, &tmp_list);
976+
}
977+
spin_unlock_irq(&pispbe->hw_lock);
978+
979+
list_for_each_entry_safe(job, temp, &tmp_list, queue) {
980+
list_del(&job->queue);
981+
kfree(job);
982+
}
976983

977984
pm_runtime_mark_last_busy(pispbe->dev);
978985
pm_runtime_put_autosuspend(pispbe->dev);
@@ -1432,7 +1439,6 @@ static int pispbe_init_node(struct pispbe_node_group *node_group,
14321439
mutex_init(&node->node_lock);
14331440
mutex_init(&node->queue_lock);
14341441
INIT_LIST_HEAD(&node->ready_queue);
1435-
spin_lock_init(&node->ready_lock);
14361442

14371443
node->format.type = node->buf_type;
14381444
pispbe_node_def_fmt(node);
@@ -1731,6 +1737,8 @@ static int pispbe_probe(struct platform_device *pdev)
17311737
if (!pispbe)
17321738
return -ENOMEM;
17331739

1740+
INIT_LIST_HEAD(&pispbe->job_queue);
1741+
17341742
dev_set_drvdata(&pdev->dev, pispbe);
17351743
pispbe->dev = &pdev->dev;
17361744
platform_set_drvdata(pdev, pispbe);

0 commit comments

Comments
 (0)