diff options
author | Jean Delvare <khali@linux-fr.org> | 2009-03-07 05:42:12 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-03-30 11:43:17 -0400 |
commit | 569b7ec73abf576f9a9e4070d213aadf2cce73cb (patch) | |
tree | b84ef4286f669db0e072745f0ceb99bd619035b2 /drivers/media/video/cx88 | |
parent | fb6991d417a9f06978119ea597dc5955b3eb784d (diff) |
V4L/DVB (10943): cx88: Prevent general protection fault on rmmod
When unloading the cx8800 driver I sometimes get a general protection
fault. Analysis revealed a race in cx88_ir_stop(). It can be solved by
using a delayed work instead of a timer for infrared input polling.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/cx88')
-rw-r--r-- | drivers/media/video/cx88/cx88-input.c | 25 |
1 files changed, 7 insertions, 18 deletions
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index ad0842136d8..ec05312a9b6 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c | |||
@@ -48,8 +48,7 @@ struct cx88_IR { | |||
48 | 48 | ||
49 | /* poll external decoder */ | 49 | /* poll external decoder */ |
50 | int polling; | 50 | int polling; |
51 | struct work_struct work; | 51 | struct delayed_work work; |
52 | struct timer_list timer; | ||
53 | u32 gpio_addr; | 52 | u32 gpio_addr; |
54 | u32 last_gpio; | 53 | u32 last_gpio; |
55 | u32 mask_keycode; | 54 | u32 mask_keycode; |
@@ -143,27 +142,19 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) | |||
143 | } | 142 | } |
144 | } | 143 | } |
145 | 144 | ||
146 | static void ir_timer(unsigned long data) | ||
147 | { | ||
148 | struct cx88_IR *ir = (struct cx88_IR *)data; | ||
149 | |||
150 | schedule_work(&ir->work); | ||
151 | } | ||
152 | |||
153 | static void cx88_ir_work(struct work_struct *work) | 145 | static void cx88_ir_work(struct work_struct *work) |
154 | { | 146 | { |
155 | struct cx88_IR *ir = container_of(work, struct cx88_IR, work); | 147 | struct cx88_IR *ir = container_of(work, struct cx88_IR, work.work); |
156 | 148 | ||
157 | cx88_ir_handle_key(ir); | 149 | cx88_ir_handle_key(ir); |
158 | mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); | 150 | schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); |
159 | } | 151 | } |
160 | 152 | ||
161 | void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) | 153 | void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) |
162 | { | 154 | { |
163 | if (ir->polling) { | 155 | if (ir->polling) { |
164 | setup_timer(&ir->timer, ir_timer, (unsigned long)ir); | 156 | INIT_DELAYED_WORK(&ir->work, cx88_ir_work); |
165 | INIT_WORK(&ir->work, cx88_ir_work); | 157 | schedule_delayed_work(&ir->work, 0); |
166 | schedule_work(&ir->work); | ||
167 | } | 158 | } |
168 | if (ir->sampling) { | 159 | if (ir->sampling) { |
169 | core->pci_irqmask |= PCI_INT_IR_SMPINT; | 160 | core->pci_irqmask |= PCI_INT_IR_SMPINT; |
@@ -179,10 +170,8 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) | |||
179 | core->pci_irqmask &= ~PCI_INT_IR_SMPINT; | 170 | core->pci_irqmask &= ~PCI_INT_IR_SMPINT; |
180 | } | 171 | } |
181 | 172 | ||
182 | if (ir->polling) { | 173 | if (ir->polling) |
183 | del_timer_sync(&ir->timer); | 174 | cancel_delayed_work_sync(&ir->work); |
184 | flush_scheduled_work(); | ||
185 | } | ||
186 | } | 175 | } |
187 | 176 | ||
188 | /* ---------------------------------------------------------------------- */ | 177 | /* ---------------------------------------------------------------------- */ |