From: Tim Sell <Timothy.Sell@xxxxxxxxxx> Because visorinput_channel_interrupt() is now called from interrupt context, we can't use our lock_visor_dev semaphore there. Instead, we use a new lock_isr spinlock, which is essentially needed to prevent visorinput_dev from disappearing while we're accessing it in visorinput_channel_interrupt(). Signed-off-by: David Kershner <david.kershner@xxxxxxxxxx> Signed-off-by: Benjamin Romer <benjamin.romer@xxxxxxxxxx> --- drivers/staging/unisys/visorinput/visorinput.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/staging/unisys/visorinput/visorinput.c b/drivers/staging/unisys/visorinput/visorinput.c index 57193b3..1c2a210 100644 --- a/drivers/staging/unisys/visorinput/visorinput.c +++ b/drivers/staging/unisys/visorinput/visorinput.c @@ -102,6 +102,7 @@ struct visorinput_devdata { struct visor_device *dev; enum visorinput_device_type devtype; struct rw_semaphore lock_visor_dev; /* lock for dev */ + spinlock_t lock_isr; /* for data accessed in isr */ struct input_dev *visorinput_dev; bool paused; struct workqueue_struct *wq; @@ -417,6 +418,7 @@ static void devdata_put(struct visorinput_devdata *devdata) static void async_change_resolution(struct work_struct *work) { + struct input_dev *visorinput_dev = NULL; struct change_resolution_work *p_change_resolution_work = container_of(work, struct change_resolution_work, work); struct visorinput_devdata *devdata = @@ -425,13 +427,20 @@ static void async_change_resolution(struct work_struct *work) change_resolution_work_data); down_write(&devdata->lock_visor_dev); + spin_lock(&devdata->lock_isr); - if (devdata->paused) /* don't touch device/channel when paused */ - goto out_locked; - if (!devdata->visorinput_dev) - goto out_locked; + /* devdata->visorinput_dev can only go NULL when lock_isr is held */ - unregister_client_input(devdata->visorinput_dev); + if (devdata->paused || (!devdata->visorinput_dev)) { + spin_unlock(&devdata->lock_isr); + goto out; + } + visorinput_dev = devdata->visorinput_dev; /* can't unreg with lock */ + devdata->visorinput_dev = NULL; + + spin_unlock(&devdata->lock_isr); + + unregister_client_input(visorinput_dev); /* * input_set_abs_params is only effective prior to * input_register_device(). @@ -448,7 +457,7 @@ static void async_change_resolution(struct work_struct *work) dev_info(&devdata->dev->device, "created mouse %s\n", dev_name(&devdata->visorinput_dev->dev)); -out_locked: +out: up_write(&devdata->lock_visor_dev); devdata_put(devdata); /* from schedule_mouse_resolution_change() */ } @@ -481,6 +490,7 @@ devdata_create(struct visor_device *dev, enum visorinput_device_type devtype) INIT_WORK(&devdata->change_resolution_work_data.work, async_change_resolution); init_rwsem(&devdata->lock_visor_dev); + spin_lock_init(&devdata->lock_isr); down_write(&devdata->lock_visor_dev); /* @@ -665,7 +675,7 @@ visorinput_channel_interrupt(struct visor_device *dev) if (!devdata) return; - down_write(&devdata->lock_visor_dev); + spin_lock(&devdata->lock_isr); if (devdata->paused) /* don't touch device/channel when paused */ goto out_locked; @@ -771,7 +781,7 @@ visorinput_channel_interrupt(struct visor_device *dev) } out_locked: devdata_put(devdata); - up_write(&devdata->lock_visor_dev); + spin_unlock(&devdata->lock_isr); } static int -- 2.5.0 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel