staging: unisys: visornic: prevent double-unlock of priv_lock
authorTim Sell <Timothy.Sell@unisys.com>
Fri, 8 Apr 2016 13:21:10 +0000 (09:21 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Apr 2016 04:15:51 +0000 (21:15 -0700)
Previously, devdata->priv_lock was being unlocked in visornic_serverdown()
both before calling visornic_serverdown_complete(), then again at the end
of the function.  This bug was corrected.

The structure of visornic_serverdown() was also improved to make it easier
to follow and to decrease the chance that such bugs will be introduced
again.  The main-path logic now falls thru down the left-side of the page,
with a common error-exit point to handle error conditions.

Signed-off-by: Tim Sell <Timothy.Sell@unisys.com>
Signed-off-by: David Kershner <david.kershner@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/unisys/visornic/visornic_main.c

index 763738d56c9ddb37d8c7d64c0a8160db3ee46f1d..0ec952ac0dac739e0bc4925644fa4dc49020e5f9 100644 (file)
@@ -356,28 +356,38 @@ visornic_serverdown(struct visornic_devdata *devdata,
                    visorbus_state_complete_func complete_func)
 {
        unsigned long flags;
+       int err;
 
        spin_lock_irqsave(&devdata->priv_lock, flags);
-       if (!devdata->server_down && !devdata->server_change_state) {
-               if (devdata->going_away) {
-                       spin_unlock_irqrestore(&devdata->priv_lock, flags);
-                       dev_dbg(&devdata->dev->device,
-                               "%s aborting because device removal pending\n",
-                               __func__);
-                       return -ENODEV;
-               }
-               devdata->server_change_state = true;
-               devdata->server_down_complete_func = complete_func;
-               spin_unlock_irqrestore(&devdata->priv_lock, flags);
-               visornic_serverdown_complete(devdata);
-       } else if (devdata->server_change_state) {
+       if (devdata->server_change_state) {
                dev_dbg(&devdata->dev->device, "%s changing state\n",
                        __func__);
-               spin_unlock_irqrestore(&devdata->priv_lock, flags);
-               return -EINVAL;
+               err = -EINVAL;
+               goto err_unlock;
+       }
+       if (devdata->server_down) {
+               dev_dbg(&devdata->dev->device, "%s already down\n",
+                       __func__);
+               err = -EINVAL;
+               goto err_unlock;
        }
+       if (devdata->going_away) {
+               dev_dbg(&devdata->dev->device,
+                       "%s aborting because device removal pending\n",
+                       __func__);
+               err = -ENODEV;
+               goto err_unlock;
+       }
+       devdata->server_change_state = true;
+       devdata->server_down_complete_func = complete_func;
        spin_unlock_irqrestore(&devdata->priv_lock, flags);
+
+       visornic_serverdown_complete(devdata);
        return 0;
+
+err_unlock:
+       spin_unlock_irqrestore(&devdata->priv_lock, flags);
+       return err;
 }
 
 /**