[media] omap3isp: Prevent pipelines that contain a crashed entity from starting
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Fri, 18 Nov 2011 14:28:24 +0000 (11:28 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 8 May 2012 16:50:29 +0000 (13:50 -0300)
The OMAP3 ISP preview engine will violate the L4 bus protocol if we try
to write some of its internal registers after it failed to stop
properly. This generates an external abort on non-linefetch fault,
triggering a fatal kernel oops.

We can't always prevent preview engine stop failures (they can for
instance be caused by a sensor crash), but we can improve the system
reliability by refusing to start streaming on a pipeline that contains
the preview engine if it failed to stop. The driver will then eventually
reset the ISP (when all applications will have closed their file handles
related to OMAP3 ISP device nodes), making the ISP usable again.

Fixes: NB#291334 - camera: Recover gracefully from ISP crash instead of oopsing

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
Reviewed-by: Phil Carmody <ext-phil.2.carmody@nokia.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/omap3isp/isp.c
drivers/media/video/omap3isp/isp.h
drivers/media/video/omap3isp/ispvideo.c
drivers/media/video/omap3isp/ispvideo.h

index 12d5f923e1d0fbf01ff18c08ce904cea53ab3fc4..3db8583497ee4b95a4b1a18142e99a8978461421 100644 (file)
@@ -739,6 +739,17 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
        unsigned long flags;
        int ret;
 
+       /* If the preview engine crashed it might not respond to read/write
+        * operations on the L4 bus. This would result in a bus fault and a
+        * kernel oops. Refuse to start streaming in that case. This check must
+        * be performed before the loop below to avoid starting entities if the
+        * pipeline won't start anyway (those entities would then likely fail to
+        * stop, making the problem worse).
+        */
+       if ((pipe->entities & isp->crashed) &
+           (1U << isp->isp_prev.subdev.entity.id))
+               return -EIO;
+
        spin_lock_irqsave(&pipe->lock, flags);
        pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
        spin_unlock_irqrestore(&pipe->lock, flags);
@@ -879,13 +890,15 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
 
                if (ret) {
                        dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
+                       /* If the entity failed to stopped, assume it has
+                        * crashed. Mark it as such, the ISP will be reset when
+                        * applications will release it.
+                        */
+                       isp->crashed |= 1U << subdev->entity.id;
                        failure = -ETIMEDOUT;
                }
        }
 
-       if (failure < 0)
-               isp->needs_reset = true;
-
        return failure;
 }
 
@@ -1069,6 +1082,7 @@ static int isp_reset(struct isp_device *isp)
                udelay(1);
        }
 
+       isp->crashed = 0;
        return 0;
 }
 
@@ -1496,10 +1510,11 @@ void omap3isp_put(struct isp_device *isp)
        if (--isp->ref_count == 0) {
                isp_disable_interrupts(isp);
                isp_save_ctx(isp);
-               if (isp->needs_reset) {
+               /* Reset the ISP if an entity has failed to stop. This is the
+                * only way to recover from such conditions.
+                */
+               if (isp->crashed)
                        isp_reset(isp);
-                       isp->needs_reset = false;
-               }
                isp_disable_clocks(isp);
        }
        mutex_unlock(&isp->isp_mutex);
index d96603eb0d17a7c1ca6d4f363b4c8f32089d7c03..f8d1f100fc19c6a3b3284c853bf18f14c951329e 100644 (file)
@@ -145,6 +145,7 @@ struct isp_platform_callback {
  * @raw_dmamask: Raw DMA mask
  * @stat_lock: Spinlock for handling statistics
  * @isp_mutex: Mutex for serializing requests to ISP.
+ * @crashed: Bitmask of crashed entities (indexed by entity ID)
  * @has_context: Context has been saved at least once and can be restored.
  * @ref_count: Reference count for handling multiple ISP requests.
  * @cam_ick: Pointer to camera interface clock structure.
@@ -184,7 +185,7 @@ struct isp_device {
        /* ISP Obj */
        spinlock_t stat_lock;   /* common lock for statistic drivers */
        struct mutex isp_mutex; /* For handling ref_count field */
-       bool needs_reset;
+       u32 crashed;
        int has_context;
        int ref_count;
        unsigned int autoidle;
index b02070057724a47263f732ad8a05a6df56ad59ea..2107d99d523a12357657cd84aef204fb934cdfb4 100644 (file)
@@ -292,6 +292,7 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
        int ret;
 
        pipe->max_rate = pipe->l3_ick;
+       pipe->entities = 0;
 
        subdev = isp_video_remote_subdev(pipe->output, NULL);
        if (subdev == NULL)
@@ -299,6 +300,9 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
 
        while (1) {
                unsigned int shifter_link;
+
+               pipe->entities |= 1U << subdev->entity.id;
+
                /* Retrieve the sink format */
                pad = &subdev->entity.pads[0];
                if (!(pad->flags & MEDIA_PAD_FL_SINK))
index d91bdb919be0c8c925499a99cbdeb311fc0efb2f..c9187cbc355783371ec798462b4f0242dacd4bb4 100644 (file)
@@ -88,6 +88,7 @@ enum isp_pipeline_state {
 /*
  * struct isp_pipeline - An ISP hardware pipeline
  * @error: A hardware error occurred during capture
+ * @entities: Bitmask of entities in the pipeline (indexed by entity ID)
  */
 struct isp_pipeline {
        struct media_pipeline pipe;
@@ -96,6 +97,7 @@ struct isp_pipeline {
        enum isp_pipeline_stream_state stream_state;
        struct isp_video *input;
        struct isp_video *output;
+       u32 entities;
        unsigned long l3_ick;
        unsigned int max_rate;
        atomic_t frame_number;