]>
Commit | Line | Data |
---|---|---|
627e3360 GKH |
1 | From f53641a6e849034a44bf80f50245a75d7a376025 Mon Sep 17 00:00:00 2001 |
2 | From: Ian Abbott <abbotti@mev.co.uk> | |
3 | Date: Wed, 14 Feb 2024 10:07:25 +0000 | |
4 | Subject: comedi: comedi_test: Prevent timers rescheduling during deletion | |
5 | ||
6 | From: Ian Abbott <abbotti@mev.co.uk> | |
7 | ||
8 | commit f53641a6e849034a44bf80f50245a75d7a376025 upstream. | |
9 | ||
10 | The comedi_test devices have a couple of timers (ai_timer and ao_timer) | |
11 | that can be started to simulate hardware interrupts. Their expiry | |
12 | functions normally reschedule the timer. The driver code calls either | |
13 | del_timer_sync() or del_timer() to delete the timers from the queue, but | |
14 | does not currently prevent the timers from rescheduling themselves so | |
15 | synchronized deletion may be ineffective. | |
16 | ||
17 | Add a couple of boolean members (one for each timer: ai_timer_enable and | |
18 | ao_timer_enable) to the device private data structure to indicate | |
19 | whether the timers are allowed to reschedule themselves. Set the member | |
20 | to true when adding the timer to the queue, and to false when deleting | |
21 | the timer from the queue in the waveform_ai_cancel() and | |
22 | waveform_ao_cancel() functions. | |
23 | ||
24 | The del_timer_sync() function is also called from the waveform_detach() | |
25 | function, but the timer enable members will already be set to false when | |
26 | that function is called, so no change is needed there. | |
27 | ||
28 | Fixes: 403fe7f34e33 ("staging: comedi: comedi_test: fix timer race conditions") | |
29 | Cc: stable@vger.kernel.org # 4.4+ | |
30 | Signed-off-by: Ian Abbott <abbotti@mev.co.uk> | |
31 | Link: https://lore.kernel.org/r/20240214100747.16203-1-abbotti@mev.co.uk | |
32 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
33 | --- | |
34 | drivers/staging/comedi/drivers/comedi_test.c | 30 +++++++++++++++++++++++---- | |
35 | 1 file changed, 26 insertions(+), 4 deletions(-) | |
36 | ||
37 | --- a/drivers/staging/comedi/drivers/comedi_test.c | |
38 | +++ b/drivers/staging/comedi/drivers/comedi_test.c | |
39 | @@ -87,6 +87,8 @@ struct waveform_private { | |
40 | struct comedi_device *dev; /* parent comedi device */ | |
41 | u64 ao_last_scan_time; /* time of previous AO scan in usec */ | |
42 | unsigned int ao_scan_period; /* AO scan period in usec */ | |
43 | + bool ai_timer_enable:1; /* should AI timer be running? */ | |
44 | + bool ao_timer_enable:1; /* should AO timer be running? */ | |
45 | unsigned short ao_loopbacks[N_CHANS]; | |
46 | }; | |
47 | ||
48 | @@ -236,8 +238,12 @@ static void waveform_ai_timer(struct tim | |
49 | time_increment = devpriv->ai_convert_time - now; | |
50 | else | |
51 | time_increment = 1; | |
52 | - mod_timer(&devpriv->ai_timer, | |
53 | - jiffies + usecs_to_jiffies(time_increment)); | |
54 | + spin_lock(&dev->spinlock); | |
55 | + if (devpriv->ai_timer_enable) { | |
56 | + mod_timer(&devpriv->ai_timer, | |
57 | + jiffies + usecs_to_jiffies(time_increment)); | |
58 | + } | |
59 | + spin_unlock(&dev->spinlock); | |
60 | } | |
61 | ||
62 | overrun: | |
63 | @@ -393,9 +399,12 @@ static int waveform_ai_cmd(struct comedi | |
64 | * Seem to need an extra jiffy here, otherwise timer expires slightly | |
65 | * early! | |
66 | */ | |
67 | + spin_lock_bh(&dev->spinlock); | |
68 | + devpriv->ai_timer_enable = true; | |
69 | devpriv->ai_timer.expires = | |
70 | jiffies + usecs_to_jiffies(devpriv->ai_convert_period) + 1; | |
71 | add_timer(&devpriv->ai_timer); | |
72 | + spin_unlock_bh(&dev->spinlock); | |
73 | return 0; | |
74 | } | |
75 | ||
76 | @@ -404,6 +413,9 @@ static int waveform_ai_cancel(struct com | |
77 | { | |
78 | struct waveform_private *devpriv = dev->private; | |
79 | ||
80 | + spin_lock_bh(&dev->spinlock); | |
81 | + devpriv->ai_timer_enable = false; | |
82 | + spin_unlock_bh(&dev->spinlock); | |
83 | if (in_softirq()) { | |
84 | /* Assume we were called from the timer routine itself. */ | |
85 | del_timer(&devpriv->ai_timer); | |
86 | @@ -495,8 +507,12 @@ static void waveform_ao_timer(struct tim | |
87 | unsigned int time_inc = devpriv->ao_last_scan_time + | |
88 | devpriv->ao_scan_period - now; | |
89 | ||
90 | - mod_timer(&devpriv->ao_timer, | |
91 | - jiffies + usecs_to_jiffies(time_inc)); | |
92 | + spin_lock(&dev->spinlock); | |
93 | + if (devpriv->ao_timer_enable) { | |
94 | + mod_timer(&devpriv->ao_timer, | |
95 | + jiffies + usecs_to_jiffies(time_inc)); | |
96 | + } | |
97 | + spin_unlock(&dev->spinlock); | |
98 | } | |
99 | ||
100 | underrun: | |
101 | @@ -517,9 +533,12 @@ static int waveform_ao_inttrig_start(str | |
102 | async->inttrig = NULL; | |
103 | ||
104 | devpriv->ao_last_scan_time = ktime_to_us(ktime_get()); | |
105 | + spin_lock_bh(&dev->spinlock); | |
106 | + devpriv->ao_timer_enable = true; | |
107 | devpriv->ao_timer.expires = | |
108 | jiffies + usecs_to_jiffies(devpriv->ao_scan_period); | |
109 | add_timer(&devpriv->ao_timer); | |
110 | + spin_unlock_bh(&dev->spinlock); | |
111 | ||
112 | return 1; | |
113 | } | |
114 | @@ -604,6 +623,9 @@ static int waveform_ao_cancel(struct com | |
115 | struct waveform_private *devpriv = dev->private; | |
116 | ||
117 | s->async->inttrig = NULL; | |
118 | + spin_lock_bh(&dev->spinlock); | |
119 | + devpriv->ao_timer_enable = false; | |
120 | + spin_unlock_bh(&dev->spinlock); | |
121 | if (in_softirq()) { | |
122 | /* Assume we were called from the timer routine itself. */ | |
123 | del_timer(&devpriv->ao_timer); |