[PATCH 05/14] staging: unisys: visorinput: use spinlock for channel_interrupt() locking

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux