Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c1089bdc authored by Jean Delvare's avatar Jean Delvare Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB (10939): ir-kbd-i2c: Prevent general protection fault on rmmod



The removal of the timer which polls the infrared input is racy.
Replacing the timer with a delayed work solves the problem.

Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent f263bac9
Loading
Loading
Loading
Loading
+5 −15
Original line number Diff line number Diff line
@@ -279,15 +279,9 @@ static void ir_key_poll(struct IR_i2c *ir)
	}
}

static void ir_timer(unsigned long data)
{
	struct IR_i2c *ir = (struct IR_i2c*)data;
	schedule_work(&ir->work);
}

static void ir_work(struct work_struct *work)
{
	struct IR_i2c *ir = container_of(work, struct IR_i2c, work);
	struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work);
	int polling_interval = 100;

	/* MSI TV@nywhere Plus requires more frequent polling
@@ -296,7 +290,7 @@ static void ir_work(struct work_struct *work)
		polling_interval = 50;

	ir_key_poll(ir);
	mod_timer(&ir->timer, jiffies + msecs_to_jiffies(polling_interval));
	schedule_delayed_work(&ir->work, msecs_to_jiffies(polling_interval));
}

/* ----------------------------------------------------------------------- */
@@ -452,11 +446,8 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
	       ir->input->name, ir->input->phys, adap->name);

	/* start polling via eventd */
	INIT_WORK(&ir->work, ir_work);
	init_timer(&ir->timer);
	ir->timer.function = ir_timer;
	ir->timer.data     = (unsigned long)ir;
	schedule_work(&ir->work);
	INIT_DELAYED_WORK(&ir->work, ir_work);
	schedule_delayed_work(&ir->work, 0);

	return 0;

@@ -473,8 +464,7 @@ static int ir_detach(struct i2c_client *client)
	struct IR_i2c *ir = i2c_get_clientdata(client);

	/* kill outstanding polls */
	del_timer_sync(&ir->timer);
	flush_scheduled_work();
	cancel_delayed_work_sync(&ir->work);

	/* unregister devices */
	input_unregister_device(ir->input);
+1 −2
Original line number Diff line number Diff line
@@ -14,8 +14,7 @@ struct IR_i2c {
	/* Used to avoid fast repeating */
	unsigned char          old;

	struct work_struct     work;
	struct timer_list      timer;
	struct delayed_work    work;
	char                   phys[32];
	int                    (*get_key)(struct IR_i2c*, u32*, u32*);
};