Commit 93d3fb35 authored by Hans de Goede's avatar Hans de Goede Committed by Mauro Carvalho Chehab

media: atomisp: Remove watchdog timer

The watchdog timer code to recover from the ISP getting stuck has several
major issues:

1. There is no way to do fault injection and normally the ISP does not
get stuck, so is it is impossible to test it.

2. It in essence just stops all streams, resets the ISP and then brings
everything back up. Userspace can easily do this itself by using a
timeout on dqbuf and then closing (which causes a poweroff) +
re-opening the device. Doing this in userspace (if it ever turns out
to be necessary) greatly simplifies the kernel code and in general
will be a more robust solution.

Even just a quick look at the code finds several more issues:

3. The need to sync-cancel the timers + work on streamoff requires
isp->mutex to be dropped halfway during the ioctl opening all sorts of
races.

4. The atomisp code supports setting up 2 pipelines, streaming from
two sensors at the same time. But there is only a single wdt_work
and stopping one of the 2 streams will cancel the timers + work,
stopping the wdt even though the other stream might still be running.

5. In case atomisp_css_flush() the sync cancel is done while keeping
isp->mutex locked, causing a deadlock when racing with wdt_work which
also takes isp->mutex.

6. Even though the watchdog is purely a software/driver thing which
just checkes that new frames keep coming in, there are 2 completely
different implementations for the ISP2400/ISP2401 which is not
necessary at all.

So all in all I believe that it is better to just remove the current
watchdog implementation. Fixing all the issues with the current
implementation will be so much work, that if it turns out that we do
need something like this then doing a clean re-implementation from
scratch will be better anyways.

wdt_work was also (ab)used to reset the ISP after the firmware signalled
an fw-assert error through the irq, add a new assert_recover_work to
replace this.
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent f315c1ac
......@@ -65,8 +65,7 @@ bool atomisp_buffers_queued_pipe(struct atomisp_video_pipe *pipe);
/* Interrupt functions */
void atomisp_msi_irq_init(struct atomisp_device *isp);
void atomisp_msi_irq_uninit(struct atomisp_device *isp);
void atomisp_wdt_work(struct work_struct *work);
void atomisp_wdt(struct timer_list *t);
void atomisp_assert_recovery_work(struct work_struct *work);
void atomisp_setup_flash(struct atomisp_sub_device *asd);
irqreturn_t atomisp_isr(int irq, void *dev);
irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr);
......
......@@ -3796,8 +3796,6 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
enum atomisp_input_stream_id stream_id = 0;
struct atomisp_css_event current_event;
struct atomisp_sub_device *asd;
bool reset_wdt_timer[MAX_STREAM_NUM] = {false};
int i;
lockdep_assert_held(&isp->mutex);
......@@ -3813,14 +3811,8 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
__func__,
current_event.event.fw_assert_module_id,
current_event.event.fw_assert_line_no);
for (i = 0; i < isp->num_of_streams; i++)
atomisp_wdt_stop(&isp->asd[i], 0);
if (!IS_ISP2401)
atomisp_wdt(&isp->asd[0].wdt);
else
queue_work(isp->wdt_work_queue, &isp->wdt_work);
queue_work(system_long_wq, &isp->assert_recovery_work);
return -EINVAL;
} else if (current_event.event.type == IA_CSS_EVENT_TYPE_FW_WARNING) {
dev_warn(isp->dev, "%s: ISP reports warning, code is %d, exp_id %d\n",
......@@ -3849,20 +3841,12 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
frame_done_found[asd->index] = true;
atomisp_buf_done(asd, 0, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME,
current_event.pipe, true, stream_id);
if (!IS_ISP2401)
reset_wdt_timer[asd->index] = true; /* ISP running */
break;
case IA_CSS_EVENT_TYPE_SECOND_OUTPUT_FRAME_DONE:
dev_dbg(isp->dev, "event: Second output frame done");
frame_done_found[asd->index] = true;
atomisp_buf_done(asd, 0, IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME,
current_event.pipe, true, stream_id);
if (!IS_ISP2401)
reset_wdt_timer[asd->index] = true; /* ISP running */
break;
case IA_CSS_EVENT_TYPE_3A_STATISTICS_DONE:
dev_dbg(isp->dev, "event: 3A stats frame done");
......@@ -3883,19 +3867,12 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
atomisp_buf_done(asd, 0,
IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME,
current_event.pipe, true, stream_id);
if (!IS_ISP2401)
reset_wdt_timer[asd->index] = true; /* ISP running */
break;
case IA_CSS_EVENT_TYPE_SECOND_VF_OUTPUT_FRAME_DONE:
dev_dbg(isp->dev, "event: second VF output frame done");
atomisp_buf_done(asd, 0,
IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME,
current_event.pipe, true, stream_id);
if (!IS_ISP2401)
reset_wdt_timer[asd->index] = true; /* ISP running */
break;
case IA_CSS_EVENT_TYPE_DIS_STATISTICS_DONE:
dev_dbg(isp->dev, "event: dis stats frame done");
......@@ -3918,24 +3895,6 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
}
}
if (IS_ISP2401)
return 0;
/* ISP2400: If there are no buffers queued then delete wdt timer. */
for (i = 0; i < isp->num_of_streams; i++) {
asd = &isp->asd[i];
if (!asd)
continue;
if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
continue;
if (!atomisp_buffers_queued(asd))
atomisp_wdt_stop(asd, false);
else if (reset_wdt_timer[i])
/* SOF irq should not reset wdt timer. */
atomisp_wdt_refresh(asd,
ATOMISP_WDT_KEEP_CURRENT_DELAY);
}
return 0;
}
......
......@@ -258,13 +258,7 @@ struct atomisp_device {
/* isp timeout status flag */
bool isp_timeout;
bool isp_fatal_error;
struct workqueue_struct *wdt_work_queue;
struct work_struct wdt_work;
/* ISP2400 */
atomic_t wdt_count;
atomic_t wdt_work_queued;
struct work_struct assert_recovery_work;
spinlock_t lock; /* Protects asd[i].streaming */
......@@ -282,20 +276,4 @@ struct atomisp_device {
extern struct device *atomisp_dev;
#define atomisp_is_wdt_running(a) timer_pending(&(a)->wdt)
/* ISP2401 */
void atomisp_wdt_refresh_pipe(struct atomisp_video_pipe *pipe,
unsigned int delay);
void atomisp_wdt_refresh(struct atomisp_sub_device *asd, unsigned int delay);
/* ISP2400 */
void atomisp_wdt_start(struct atomisp_sub_device *asd);
/* ISP2401 */
void atomisp_wdt_start_pipe(struct atomisp_video_pipe *pipe);
void atomisp_wdt_stop_pipe(struct atomisp_video_pipe *pipe, bool sync);
void atomisp_wdt_stop(struct atomisp_sub_device *asd, bool sync);
#endif /* __ATOMISP_INTERNAL_H__ */
......@@ -1363,15 +1363,6 @@ static int atomisp_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
atomisp_handle_parameter_and_buffer(pipe);
} else {
atomisp_qbuffers_to_css(asd);
if (!IS_ISP2401) {
if (!atomisp_is_wdt_running(asd) && atomisp_buffers_queued(asd))
atomisp_wdt_start(asd);
} else {
if (!atomisp_is_wdt_running(pipe) &&
atomisp_buffers_queued_pipe(pipe))
atomisp_wdt_start_pipe(pipe);
}
}
}
......@@ -1594,33 +1585,6 @@ int atomisp_stream_on_master_slave_sensor(struct atomisp_device *isp,
return 0;
}
/* FIXME! ISP2400 */
static void __wdt_on_master_slave_sensor(struct atomisp_device *isp,
unsigned int wdt_duration)
{
if (atomisp_buffers_queued(&isp->asd[0]))
atomisp_wdt_refresh(&isp->asd[0], wdt_duration);
if (atomisp_buffers_queued(&isp->asd[1]))
atomisp_wdt_refresh(&isp->asd[1], wdt_duration);
}
/* FIXME! ISP2401 */
static void __wdt_on_master_slave_sensor_pipe(struct atomisp_video_pipe *pipe,
unsigned int wdt_duration,
bool enable)
{
static struct atomisp_video_pipe *pipe0;
if (enable) {
if (atomisp_buffers_queued_pipe(pipe0))
atomisp_wdt_refresh_pipe(pipe0, wdt_duration);
if (atomisp_buffers_queued_pipe(pipe))
atomisp_wdt_refresh_pipe(pipe, wdt_duration);
} else {
pipe0 = pipe;
}
}
static void atomisp_pause_buffer_event(struct atomisp_device *isp)
{
struct v4l2_event event = {0};
......@@ -1670,7 +1634,6 @@ static int atomisp_streamon(struct file *file, void *fh,
struct pci_dev *pdev = to_pci_dev(isp->dev);
enum ia_css_pipe_id css_pipe_id;
unsigned int sensor_start_stream;
unsigned int wdt_duration = ATOMISP_ISP_TIMEOUT_DURATION;
unsigned long irqflags;
int ret;
......@@ -1845,15 +1808,9 @@ static int atomisp_streamon(struct file *file, void *fh,
dev_err(isp->dev, "master slave sensor stream on failed!\n");
goto out;
}
if (!IS_ISP2401)
__wdt_on_master_slave_sensor(isp, wdt_duration);
else
__wdt_on_master_slave_sensor_pipe(pipe, wdt_duration, true);
goto start_delay_wq;
} else if (asd->depth_mode->val && (atomisp_streaming_count(isp) <
ATOMISP_DEPTH_SENSOR_STREAMON_COUNT)) {
if (IS_ISP2401)
__wdt_on_master_slave_sensor_pipe(pipe, wdt_duration, false);
goto start_delay_wq;
}
......@@ -1875,14 +1832,6 @@ static int atomisp_streamon(struct file *file, void *fh,
goto out;
}
if (!IS_ISP2401) {
if (atomisp_buffers_queued(asd))
atomisp_wdt_refresh(asd, wdt_duration);
} else {
if (atomisp_buffers_queued_pipe(pipe))
atomisp_wdt_refresh_pipe(pipe, wdt_duration);
}
start_delay_wq:
if (asd->continuous_mode->val) {
struct v4l2_mbus_framefmt *sink;
......@@ -1986,16 +1935,7 @@ int __atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
asd->streaming = ATOMISP_DEVICE_STREAMING_STOPPING;
first_streamoff = true;
}
spin_unlock_irqrestore(&isp->lock, flags);
if (first_streamoff) {
/* if other streams are running, should not disable watch dog */
mutex_unlock(&isp->mutex);
atomisp_wdt_stop(asd, true);
mutex_lock(&isp->mutex);
}
spin_lock_irqsave(&isp->lock, flags);
if (atomisp_subdev_streaming_count(asd) == 1)
asd->streaming = ATOMISP_DEVICE_STREAMING_DISABLED;
spin_unlock_irqrestore(&isp->lock, flags);
......
......@@ -108,15 +108,6 @@ struct atomisp_video_pipe {
*/
unsigned int frame_request_config_id[VIDEO_MAX_FRAME];
struct atomisp_css_params_with_list *frame_params[VIDEO_MAX_FRAME];
/*
* move wdt from asd struct to create wdt for each pipe
*/
/* ISP2401 */
struct timer_list wdt;
unsigned int wdt_duration; /* in jiffies */
unsigned long wdt_expires;
atomic_t wdt_count;
};
struct atomisp_pad_format {
......@@ -360,11 +351,6 @@ struct atomisp_sub_device {
int raw_buffer_locked_count;
spinlock_t raw_buffer_bitmap_lock;
/* ISP 2400 */
struct timer_list wdt;
unsigned int wdt_duration; /* in jiffies */
unsigned long wdt_expires;
/* ISP2401 */
bool re_trigger_capture;
......
......@@ -1433,39 +1433,6 @@ static bool is_valid_device(struct pci_dev *pdev, const struct pci_device_id *id
return true;
}
static int init_atomisp_wdts(struct atomisp_device *isp)
{
int i, err;
atomic_set(&isp->wdt_work_queued, 0);
isp->wdt_work_queue = alloc_workqueue(isp->v4l2_dev.name, 0, 1);
if (!isp->wdt_work_queue) {
dev_err(isp->dev, "Failed to initialize wdt work queue\n");
err = -ENOMEM;
goto alloc_fail;
}
INIT_WORK(&isp->wdt_work, atomisp_wdt_work);
for (i = 0; i < isp->num_of_streams; i++) {
struct atomisp_sub_device *asd = &isp->asd[i];
if (!IS_ISP2401) {
timer_setup(&asd->wdt, atomisp_wdt, 0);
} else {
timer_setup(&asd->video_out_capture.wdt,
atomisp_wdt, 0);
timer_setup(&asd->video_out_preview.wdt,
atomisp_wdt, 0);
timer_setup(&asd->video_out_vf.wdt, atomisp_wdt, 0);
timer_setup(&asd->video_out_video_capture.wdt,
atomisp_wdt, 0);
}
}
return 0;
alloc_fail:
return err;
}
#define ATOM_ISP_PCI_BAR 0
static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
......@@ -1698,10 +1665,8 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
dev_err(&pdev->dev, "atomisp_register_entities failed (%d)\n", err);
goto register_entities_fail;
}
/* init atomisp wdts */
err = init_atomisp_wdts(isp);
if (err != 0)
goto wdt_work_queue_fail;
INIT_WORK(&isp->assert_recovery_work, atomisp_assert_recovery_work);
/* save the iunit context only once after all the values are init'ed. */
atomisp_save_iunit_reg(isp);
......@@ -1748,8 +1713,6 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
request_irq_fail:
hmm_cleanup();
pm_runtime_get_noresume(&pdev->dev);
destroy_workqueue(isp->wdt_work_queue);
wdt_work_queue_fail:
atomisp_unregister_entities(isp);
register_entities_fail:
atomisp_uninitialize_modules(isp);
......@@ -1809,8 +1772,6 @@ static void atomisp_pci_remove(struct pci_dev *pdev)
atomisp_msi_irq_uninit(isp);
atomisp_unregister_entities(isp);
destroy_workqueue(isp->wdt_work_queue);
release_firmware(isp->firmware);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment