Merge branch 'upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/floppy...
[linux-drm-fsl-dcu.git] / kernel / kmod.c
index 957a7aab8ebc2b4d1527763c2666298be1dc419c..ff2c7cb86d770aaf51712e330dc0f1e8a72a26e6 100644 (file)
@@ -221,13 +221,12 @@ fail:
        return 0;
 }
 
-void call_usermodehelper_freeinfo(struct subprocess_info *info)
+static void call_usermodehelper_freeinfo(struct subprocess_info *info)
 {
        if (info->cleanup)
                (*info->cleanup)(info);
        kfree(info);
 }
-EXPORT_SYMBOL(call_usermodehelper_freeinfo);
 
 static void umh_complete(struct subprocess_info *sub_info)
 {
@@ -322,7 +321,7 @@ static void __call_usermodehelper(struct work_struct *work)
  * land has been frozen during a system-wide hibernation or suspend operation).
  * Should always be manipulated under umhelper_sem acquired for write.
  */
-static int usermodehelper_disabled = 1;
+static enum umh_disable_depth usermodehelper_disabled = UMH_DISABLED;
 
 /* Number of helpers running */
 static atomic_t running_helpers = ATOMIC_INIT(0);
@@ -333,33 +332,111 @@ static atomic_t running_helpers = ATOMIC_INIT(0);
  */
 static DECLARE_WAIT_QUEUE_HEAD(running_helpers_waitq);
 
+/*
+ * Used by usermodehelper_read_lock_wait() to wait for usermodehelper_disabled
+ * to become 'false'.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(usermodehelper_disabled_waitq);
+
 /*
  * Time to wait for running_helpers to become zero before the setting of
  * usermodehelper_disabled in usermodehelper_disable() fails
  */
 #define RUNNING_HELPERS_TIMEOUT        (5 * HZ)
 
-void read_lock_usermodehelper(void)
+int usermodehelper_read_trylock(void)
 {
+       DEFINE_WAIT(wait);
+       int ret = 0;
+
        down_read(&umhelper_sem);
+       for (;;) {
+               prepare_to_wait(&usermodehelper_disabled_waitq, &wait,
+                               TASK_INTERRUPTIBLE);
+               if (!usermodehelper_disabled)
+                       break;
+
+               if (usermodehelper_disabled == UMH_DISABLED)
+                       ret = -EAGAIN;
+
+               up_read(&umhelper_sem);
+
+               if (ret)
+                       break;
+
+               schedule();
+               try_to_freeze();
+
+               down_read(&umhelper_sem);
+       }
+       finish_wait(&usermodehelper_disabled_waitq, &wait);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(read_lock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_trylock);
 
-void read_unlock_usermodehelper(void)
+long usermodehelper_read_lock_wait(long timeout)
+{
+       DEFINE_WAIT(wait);
+
+       if (timeout < 0)
+               return -EINVAL;
+
+       down_read(&umhelper_sem);
+       for (;;) {
+               prepare_to_wait(&usermodehelper_disabled_waitq, &wait,
+                               TASK_UNINTERRUPTIBLE);
+               if (!usermodehelper_disabled)
+                       break;
+
+               up_read(&umhelper_sem);
+
+               timeout = schedule_timeout(timeout);
+               if (!timeout)
+                       break;
+
+               down_read(&umhelper_sem);
+       }
+       finish_wait(&usermodehelper_disabled_waitq, &wait);
+       return timeout;
+}
+EXPORT_SYMBOL_GPL(usermodehelper_read_lock_wait);
+
+void usermodehelper_read_unlock(void)
 {
        up_read(&umhelper_sem);
 }
-EXPORT_SYMBOL_GPL(read_unlock_usermodehelper);
+EXPORT_SYMBOL_GPL(usermodehelper_read_unlock);
+
+/**
+ * __usermodehelper_set_disable_depth - Modify usermodehelper_disabled.
+ * @depth: New value to assign to usermodehelper_disabled.
+ *
+ * Change the value of usermodehelper_disabled (under umhelper_sem locked for
+ * writing) and wakeup tasks waiting for it to change.
+ */
+void __usermodehelper_set_disable_depth(enum umh_disable_depth depth)
+{
+       down_write(&umhelper_sem);
+       usermodehelper_disabled = depth;
+       wake_up(&usermodehelper_disabled_waitq);
+       up_write(&umhelper_sem);
+}
 
 /**
- * usermodehelper_disable - prevent new helpers from being started
+ * __usermodehelper_disable - Prevent new helpers from being started.
+ * @depth: New value to assign to usermodehelper_disabled.
+ *
+ * Set usermodehelper_disabled to @depth and wait for running helpers to exit.
  */
-int usermodehelper_disable(void)
+int __usermodehelper_disable(enum umh_disable_depth depth)
 {
        long retval;
 
+       if (!depth)
+               return -EINVAL;
+
        down_write(&umhelper_sem);
-       usermodehelper_disabled = 1;
+       usermodehelper_disabled = depth;
        up_write(&umhelper_sem);
 
        /*
@@ -374,31 +451,10 @@ int usermodehelper_disable(void)
        if (retval)
                return 0;
 
-       down_write(&umhelper_sem);
-       usermodehelper_disabled = 0;
-       up_write(&umhelper_sem);
+       __usermodehelper_set_disable_depth(UMH_ENABLED);
        return -EAGAIN;
 }
 
-/**
- * usermodehelper_enable - allow new helpers to be started again
- */
-void usermodehelper_enable(void)
-{
-       down_write(&umhelper_sem);
-       usermodehelper_disabled = 0;
-       up_write(&umhelper_sem);
-}
-
-/**
- * usermodehelper_is_disabled - check if new helpers are allowed to be started
- */
-bool usermodehelper_is_disabled(void)
-{
-       return usermodehelper_disabled;
-}
-EXPORT_SYMBOL_GPL(usermodehelper_is_disabled);
-
 static void helper_lock(void)
 {
        atomic_inc(&running_helpers);
@@ -422,6 +478,7 @@ static void helper_unlock(void)
  * structure.  This should be passed to call_usermodehelper_exec to
  * exec the process and free the structure.
  */
+static
 struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
                                                  char **envp, gfp_t gfp_mask)
 {
@@ -437,7 +494,6 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
   out:
        return sub_info;
 }
-EXPORT_SYMBOL(call_usermodehelper_setup);
 
 /**
  * call_usermodehelper_setfns - set a cleanup/init function
@@ -455,6 +511,7 @@ EXPORT_SYMBOL(call_usermodehelper_setup);
  * Function must be runnable in either a process context or the
  * context in which call_usermodehelper_exec is called.
  */
+static
 void call_usermodehelper_setfns(struct subprocess_info *info,
                    int (*init)(struct subprocess_info *info, struct cred *new),
                    void (*cleanup)(struct subprocess_info *info),
@@ -464,7 +521,6 @@ void call_usermodehelper_setfns(struct subprocess_info *info,
        info->init = init;
        info->data = data;
 }
-EXPORT_SYMBOL(call_usermodehelper_setfns);
 
 /**
  * call_usermodehelper_exec - start a usermode application
@@ -478,6 +534,7 @@ EXPORT_SYMBOL(call_usermodehelper_setfns);
  * asynchronously if wait is not set, and runs as a child of keventd.
  * (ie. it runs with full root capabilities).
  */
+static
 int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
 {
        DECLARE_COMPLETION_ONSTACK(done);
@@ -519,7 +576,25 @@ unlock:
        helper_unlock();
        return retval;
 }
-EXPORT_SYMBOL(call_usermodehelper_exec);
+
+int call_usermodehelper_fns(
+       char *path, char **argv, char **envp, int wait,
+       int (*init)(struct subprocess_info *info, struct cred *new),
+       void (*cleanup)(struct subprocess_info *), void *data)
+{
+       struct subprocess_info *info;
+       gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
+
+       info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
+
+       if (info == NULL)
+               return -ENOMEM;
+
+       call_usermodehelper_setfns(info, init, cleanup, data);
+
+       return call_usermodehelper_exec(info, wait);
+}
+EXPORT_SYMBOL(call_usermodehelper_fns);
 
 static int proc_cap_handler(struct ctl_table *table, int write,
                         void __user *buffer, size_t *lenp, loff_t *ppos)