firmware_class: Split _request_firmware() into three functions, v2
Rafael J. Wysocki [Wed, 28 Mar 2012 21:29:55 +0000 (23:29 +0200)]
Split _request_firmware() into three functions,
_request_firmware_prepare() doing preparatory work that need not be
done under umhelper_sem, _request_firmware_cleanup() doing the
post-error cleanup and _request_firmware() carrying out the remaining
operations.

This change is requisite for moving the acquisition of umhelper_sem
from _request_firmware() to the callers, which is going to be done
subsequently.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Cc: stable@vger.kernel.org

drivers/base/firmware_class.c

index deee871..6029067 100644 (file)
@@ -435,7 +435,7 @@ static void firmware_class_timeout(u_long data)
 }
 
 static struct firmware_priv *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
+fw_create_instance(const struct firmware *firmware, const char *fw_name,
                   struct device *device, bool uevent, bool nowait)
 {
        struct firmware_priv *fw_priv;
@@ -449,7 +449,7 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
                goto err_out;
        }
 
-       fw_priv->fw = firmware;
+       fw_priv->fw = (struct firmware *)firmware;
        fw_priv->nowait = nowait;
        strcpy(fw_priv->fw_id, fw_name);
        init_completion(&fw_priv->completion);
@@ -510,13 +510,10 @@ static void fw_destroy_instance(struct firmware_priv *fw_priv)
        device_unregister(f_dev);
 }
 
-static int _request_firmware(const struct firmware **firmware_p,
-                            const char *name, struct device *device,
-                            bool uevent, bool nowait)
+static int _request_firmware_prepare(const struct firmware **firmware_p,
+                                    const char *name, struct device *device)
 {
-       struct firmware_priv *fw_priv;
        struct firmware *firmware;
-       int retval = 0;
 
        if (!firmware_p)
                return -EINVAL;
@@ -533,10 +530,26 @@ static int _request_firmware(const struct firmware **firmware_p,
                return 0;
        }
 
+       return 1;
+}
+
+static void _request_firmware_cleanup(const struct firmware **firmware_p)
+{
+       release_firmware(*firmware_p);
+       *firmware_p = NULL;
+}
+
+static int _request_firmware(const struct firmware *firmware,
+                            const char *name, struct device *device,
+                            bool uevent, bool nowait)
+{
+       struct firmware_priv *fw_priv;
+       int retval;
+
        retval = usermodehelper_read_trylock();
        if (WARN_ON(retval)) {
                dev_err(device, "firmware: %s will not be loaded\n", name);
-               goto out_nolock;
+               return retval;
        }
 
        if (uevent)
@@ -572,13 +585,6 @@ static int _request_firmware(const struct firmware **firmware_p,
 
 out:
        usermodehelper_read_unlock();
-
-out_nolock:
-       if (retval) {
-               release_firmware(firmware);
-               *firmware_p = NULL;
-       }
-
        return retval;
 }
 
@@ -601,7 +607,17 @@ int
 request_firmware(const struct firmware **firmware_p, const char *name,
                  struct device *device)
 {
-        return _request_firmware(firmware_p, name, device, true, false);
+       int ret;
+
+       ret = _request_firmware_prepare(firmware_p, name, device);
+       if (ret <= 0)
+               return ret;
+
+       ret = _request_firmware(*firmware_p, name, device, true, false);
+       if (ret)
+               _request_firmware_cleanup(firmware_p);
+
+       return ret;
 }
 
 /**
@@ -639,8 +655,16 @@ static int request_firmware_work_func(void *arg)
                return 0;
        }
 
-       ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+       ret = _request_firmware_prepare(&fw, fw_work->name, fw_work->device);
+       if (ret <= 0)
+               goto out;
+
+       ret = _request_firmware(fw, fw_work->name, fw_work->device,
                                fw_work->uevent, true);
+       if (ret)
+               _request_firmware_cleanup(&fw);
+
+ out:
        fw_work->cont(fw, fw_work->context);
 
        module_put(fw_work->module);